No Nginx, como posso reescrever todas as solicitações HTTP para https enquanto mantém o subdomínio?


509

Quero reescrever todas as solicitações http no meu servidor da Web para serem https, comecei com o seguinte:

servidor {
    ouça 80;

    local / {
      reescrever ^ (. *) https: //mysite.com$1 permanente;
    }
...


Um problema é que isso remove todas as informações de subdomínio (por exemplo, node1.mysite.com/folder), como eu poderia reescrever o acima para redirecionar tudo para https e manter o subdomínio?


2
Considere mover a 'resposta aceita' para serverfault.com/a/171238/90758 . Essa é a correta.
Olafure #

Basta usar $ server_name vez de mysite.com codificado
Fedir RYKHTIK

Respostas:


749

Maneira correta em novas versões do nginx

Minha primeira resposta a esta pergunta estava correta em um determinado momento, mas ela se transformou em outra armadilha - para manter-se atualizado, verifique Taxing reescrever armadilhas

Fui corrigido por muitos usuários do SE, então o crédito é para eles, mas mais importante, aqui está o código correto:

server {
       listen         80;
       server_name    my.domain.com;
       return         301 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

3
No entanto, você teria que fazer isso domínio por domínio - não? E se você quisesse aplicá-lo a todos os domínios do seu servidor?
JM4

30
@ JM4: se você usar $ host $ na reescrita em vez de server_name e adicionar default_server à diretiva listen, ela funcionará para todos os domínios do seu servidor.
Klaas van Schelven

5
É importante mencionar que o 301 é armazenado no cache local sem data de validade. Não é muito útil quando as mudanças de configuração
Trefex

9
@everyone Use um redirecionamento 307 para preservar o conteúdo do POST.
Mahmoud Al-Qudsi

10
Observe que você deve usar em $hostvez de $server_namese estiver usando subdomínios.
Catfish

276

NOTA: A melhor maneira de fazer isso foi fornecida por https://serverfault.com/a/401632/3641 - mas é repetida aqui:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

No caso mais simples, seu host será corrigido como o seu serviço para o qual você deseja enviá-lo - isso fará um redirecionamento 301 para o navegador e o URL do navegador será atualizado de acordo.

Abaixo está a resposta anterior, que é ineficiente devido ao regex, um simples 301 é ótimo, como mostrado por @kmindi

Estou usando o nginx 0.8.39 e superior e usei o seguinte:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

Envia um redirecionamento permanente para o cliente.


15
Eu acho que deveria ser 80 - pois isso está ouvindo http e depois dizendo ao cliente para voltar como https (443).
Michael Neale

3
Essa deve ser a melhor resposta!
31412 Nathan

3
Esta é a resposta mais exigente.
Case

1
esse é o mais fácil, mas o menos seguro - dessa forma, você permite que seu servidor redirecione um usuário para qualquer página, sem verificar se ele ainda pode ser usado em seu servidor. Se o seu servidor atender meu domínio.com, usuários mal-intencionados ainda poderão usá-lo para redirecionar usuários para outros domínios como meu domínio, como google.com.
Friedkiwi

10
@ cab0lt não há nenhum problema de segurança aqui. Servir um redirecionamento não representa um risco de segurança. Se houver requisitos de controle de acesso, eles devem ser verificados no ponto em que o navegador solicita o novo URL. O navegador não terá acesso simplesmente com base no redirecionamento, nem precisará do redirecionamento para solicitar o novo URL.
Mc0e

125

Eu acho que a melhor e única maneira deve estar usando um redirecionamento HTTP 301 movido permanentemente como este:

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

O redirecionamento HTTP 301 movido permanentemente também é o mais eficiente, pois não há regex a ser avaliado, de acordo com os pitfails já mencionados .


O novo HTTP 308 movido permanentemente preserva o método Request e é suportado pelos principais navegadores . Por exemplo, o uso 308impede que os navegadores alterem o método de solicitação de POSTpara GETpara a solicitação de redirecionamento.


Se você deseja preservar o nome do host e o subdomínio, este é o caminho.

Isso ainda funciona se você não tiver DNS , pois também o estou usando localmente. Estou solicitando, por exemplo, com http://192.168.0.100/index.phpe será redirecionado para exatamente https://192.168.0.100/index.php.

Eu uso listen [::]:80no meu host porque bindv6onlydefini como false, portanto, ele também se liga ao soquete ipv4. altere para listen 80se você não deseja IPv6 ou deseja vincular em outro lugar.

A solução da Saif Bechan usa o server_nameque, no meu caso, é localhost, mas que não é acessível através de uma rede.

A solução de Michael Neale é boa, mas de acordo com as pitfails, há uma solução melhor com o redirecionamento 301;)


É bom tentar citá-lo, mas o 301 não funciona em HTTPS.
Case

5
o que não funciona? seção servidor indicado é para não-criptografados http (sem s) tráfego a ser permanentemente redirecionado para servidor criptografada (que seção que escuta em 443 (https) não está listado)
kmindi

Eu verifiquei que isso funciona muito bem com https e tudo - @kmindi Atualizei minha resposta com referência à sua - pois acho que é o caminho certo e isso continua aparecendo! Bom trabalho.
Michael Neale

Ao usar uma solicitação de domínio (não-ip), não funcionará, a menos que eu altere '[::]: 80' para '80'.
31813 Joseph Lust

esse poderia ser o comportamento esperado: trac.nginx.org/nginx/ticket/345 . Atualizei a resposta para descrever a opção de escuta.
kmindi

20

Dentro do bloco do servidor, você também pode fazer o seguinte:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

2
Esta configuração causou o meu servidor para produzir um loop de redirecionamento
Corkscreewe

Talvez porque há um outro redirecionamento no lugar ou https não é permitir em seu site / app
Oriol

1
Nenhum dos outros parecia funcionar, exceto este. Usando a versão nginx: nginx / 1.10.0 (Ubuntu)
ThatGuy343

voto positivo para https: // $ host $ uri
AMB

4
Este é o caminho a percorrer se você estiver atrás de um loadbalancer!
Antwan 28/03

17

O exemplo acima não funcionou com novos subdomínios sendo criados o tempo todo. por exemplo, AAA.example.com BBB.example.com para cerca de 30 subdomínios.

Finalmente consegui uma configuração trabalhando com o seguinte:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}

obrigado! O nginx retornaria 301 https://*/ou cancelaria a solicitação prematuramente nas outras respostas aqui. server_name _;com $hostfoi a resposta que fez o truque. +1
zamnuts 13/03/2019

1
Este é o ideal! No entanto, eu recomendo que alguns sejam substituídos _pelo domínio real, por exemplo, .domain.comeu tinha dois servidores e o nginx estava acidentalmente direcionando um dos meus servidores para o servidor padrão.
Zzz

1
Esta é a única resposta que funcionou para mim, obrigado!
21416 Snowman

Muito obrigado amigo .. Eu tentei muitas das soluções, mas não funcionou. Esta solução é incrível e funcionou para mim. nome do servidor _; o que isso significa? Eu não entendi. Por favor me explique isso.
Pavan Kumar 11/01

6

Publiquei um comentário sobre a resposta correta há muito, muito tempo atrás, com uma correção muito importante, mas considero necessário destacar essa correção em sua própria resposta. Nenhuma das respostas anteriores é segura de usar se, em algum momento, você tiver configurado HTTP inseguro e esperar conteúdo do usuário, tiver formulários, hospedar uma API ou tiver configurado qualquer site, ferramenta, aplicativo ou utilitário para falar com seu site.

O problema ocorre quando uma POSTsolicitação é feita ao seu servidor. Se a resposta do servidor com um 30xredirecionamento simples, o conteúdo do POST será perdido. O que acontece é que o navegador / cliente irá atualizar o pedido para SSL, mas degradar o POSTa um GETpedido. Os POSTparâmetros serão perdidos e uma solicitação incorreta será feita ao seu servidor.

A solução é simples. Você precisa usar um HTTP 1.1 307redirecionamento. Isso está detalhado no RFC 7231 S6.4.7:

  Note: This status code is similar to 302 (Found), except that it
  does not allow changing the request method from POST to GET.  This
  specification defines no equivalent counterpart for 301 (Moved
  Permanently) ([RFC7238], however, defines the status code 308
  (Permanent Redirect) for this purpose).

A solução, adaptada da solução aceita, é usar 307no seu código de redirecionamento:

server {
       listen         80;
       server_name    my.domain.com;
       return         307 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}


4

Estou executando o ngnix atrás de um AWS ELB. O ELB está conversando com o ngnix por http. Como o ELB não tem como enviar redirecionamentos para clientes, verifico o cabeçalho X-Forwarded-Proto e redireciono:

if ($http_x_forwarded_proto != 'https') {
    return 301 "https://www.exampl.com";
}

1

Se você return 301 https://$host$request_uri;for a resposta padrão na porta 80, seu servidor poderá, mais cedo ou mais tarde, entrar em uma lista de proxies abertos [1] e começar a ser abusado para enviar tráfego para outros lugares na Internet. Se seus logs forem preenchidos com mensagens como esta, você saberá que isso aconteceu com você:

42.232.104.114 - - [25/Mar/2018:04:50:49 +0000] "GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1" 301 185 "http://www.ioffer.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Hotbar 4.1.8.0; RogueCleaner; Alexa Toolbar)"

O problema é que $hostele retornará o que o navegador enviar no Hostcabeçalho ou até o nome do host da linha de abertura do HTTP, como esta:

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

Devido a esse problema, algumas outras respostas aqui recomendam o uso em $server_namevez de $host. $server_namesempre avalia o que você coloca na server_namedeclaração. Mas se você tiver vários subdomínios lá ou usar um curinga, isso não funcionará, porque $server_namesó usa a primeira entrada após a server_namedeclaração e, o mais importante, apenas fará eco de um curinga (não o expanda).

Então, como oferecer suporte a vários domínios enquanto mantém a segurança? Em meus próprios sistemas, lidei com esse dilema listando primeiro um default_serverbloco que não usa $hoste, em seguida, listando um bloco curinga que:

server {
  listen 80 default_server;
  server_name example.com;
  return 301 https://example.com$request_uri;
}
server {
  listen 80;
  server_name *.example.com;
  return 301 https://$host$request_uri;
}

(Você também pode listar mais de um domínio no segundo bloco.)

Com essa combinação, os domínios não correspondentes serão redirecionados para algum lugar codificado (sempre example.com) e os domínios que correspondem ao seu irão para o lugar certo. Seu servidor não será útil como um proxy aberto, portanto você não estará atraindo problemas.

Se você estiver com raiva, suponho que você também possa fazer com que o default_serverbloco não corresponda a nenhum dos seus domínios legítimos e sirva algo ofensivo. . . .

[1] Tecnicamente "proxy" é a palavra errada, porque seu servidor não está saindo e atendendo solicitações para os clientes, apenas enviando um redirecionamento, mas não tenho certeza qual seria a palavra certa. Também não tenho certeza de qual é o objetivo, mas ele enche seus logs de ruído e consome sua CPU e largura de banda; portanto, é melhor parar com isso.


0

Parece que ninguém realmente acertou 100%. Para que as solicitações da porta 80 atinjam seus equivalentes 443 para um servidor da Web inteiro, é necessário usar a diretiva listen , não a diretiva server_name para especificar o nome geral . Veja também https://nginx.org/en/docs/http/request_processing.html

servidor {
    listen 80 default;
    listen [::]: 80 padrão;
      retornar 307 https: // $ host $ request_uri;
}
  • $ host captura nomes de subdomínios.
  • 307 e 308 incluem URIs de solicitação POST e GET.
  • 307 é temporário, mude para o permanente 308 após um teste completo:

E certifique-se de verificar o que já está em /etc/nginx/conf.d/, porque na maioria das vezes eu tive problemas em que o default.conf retornou algum vhost existente. Minha ordem de trabalhar com os problemas do nginx está sempre começando movendo o arquivo padrão, colocando-o de volta comentando linha por linha para ver onde ele dá errado.


-1
rewrite ^!https https://$host$request_uri permanent;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.