No Nginx, como posso reescrever todos os pedidos de http para https enquanto mantenho o subdomínio?

496

Eu quero reescrever todos os pedidos de http no meu servidor web para serem requisições https, eu comecei com o seguinte:

server {
    listen      80;

    location / {
      rewrite     ^(.*)   https://mysite.com$1 permanent;
    }
...


Um problema é que isso elimina qualquer informação de subdomínio (por exemplo, node1.mysite.com/folder), como eu poderia reescrever o texto acima para redirecionar tudo para https e manter o subdomínio?

    
por MikeN 21.09.2009 / 16:04

10 respostas

737

Forma correta em novas versões do nginx

A minha primeira resposta a esta pergunta estava correta em determinado momento, mas se transformou em outra armadilha - para se manter atualizado, por favor, verifique Armadilhas para reescrever impostos

Eu fui corrigido por muitos usuários de SE, então o crédito é direcionado a 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; 

       [....]
}
    
por 05.12.2011 / 21:43
270

OBSERVAÇÃO: a melhor maneira de fazer isso foi fornecida pelo link - mas é repetido aqui:

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

No caso mais simples, seu host será corrigido para ser o 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 a regex, um simples 301 é ótimo como mostrado por @kmindi

Eu tenho usado o nginx 0.8.39 e acima, e usei o seguinte:

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

Envia um redirecionamento permanente para o cliente.

    
por 17.08.2010 / 05:07
121

Acho que a melhor e única maneira de usar um HTTP 301 foi movido Permanentemente redirecione assim:

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

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

O novo HTTP 308 movido permanentemente preserva o método de solicitação e é suportado pelos principais navegadores . Por exemplo, usar 308 impede que os navegadores alterem o método de solicitação de POST para GET para a solicitação de redirecionamento.

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

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

Eu uso listen [::]:80 no meu host porque eu tenho bindv6only definido como false , então ele também é vinculado ao soquete ipv4. altere para listen 80 se você não quiser o IPv6 ou quiser ligar em outro lugar.

A solução de Saif Bechan usa o server_name , que 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 os pitfails, existe uma solução melhor com o redirecionamento 301;)

    
por 23.06.2012 / 19:19
17

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

Finalmente, tenho uma configuração funcionando 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
# ...
}
    
por 25.06.2012 / 06:29
16

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;
}
    
por 31.07.2015 / 21:50
6

Eu postei um comentário sobre a resposta correta há muito, muito tempo atrás, com uma correção muito importante, mas sinto que é necessário destacar essa correção em sua própria resposta. Nenhuma das respostas anteriores é segura para usar se em algum momento você configurou HTTP desprotegido e espera conteúdo do usuário, tem formulários, hospeda uma API ou configurou qualquer site, ferramenta, aplicativo ou utilitário para falar com o seu site.

O problema ocorre quando uma solicitação POST é feita ao seu servidor. Se a resposta do servidor com um redirecionamento 30x puro o conteúdo do POST será perdido. O que acontece é que o navegador / cliente atualizará a solicitação para SSL, mas fará downgrade da POST para um pedido GET . Os parâmetros POST serão perdidos e solicitações incorretas serão feitas em seu servidor.

A solução é simples. Você precisa usar um HTTP 1.1 307 redirect. 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 307 no 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"; 

       [....]
}
    
por 19.03.2017 / 21:24
4

Eu consegui fazer assim:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# global HTTP handler
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# global non-WWW HTTPS handler
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

link

    
por 21.04.2016 / 20:27
3

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

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

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 outro local na Internet. Se seus registros estiverem cheios de mensagens como esta, você sabe 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 $host retornará o que o navegador envia no cabeçalho Host ou até mesmo 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 usar $server_name em vez de $host . $server_name sempre avalia o que você coloca na declaração server_name . Mas se você tiver vários subdomínios ou usar um caractere curinga, isso não funcionará, pois $server_name usará somente a entrada primeiro após a declaração server_name e, mais importante, ecoará um caractere curinga (não expandi-lo).

Então, como apoiar vários domínios, mantendo a segurança? Em meus próprios sistemas, lidei com esse dilema primeiro listando um bloco default_server que não usa $host e, 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, domínios não correspondentes serão redirecionados para algum lugar codificado (sempre example.com ) e os domínios que correspondem aos seus próprios irão para o local correto. Seu servidor não será útil como um proxy aberto, por isso você não estará causando problemas.

Se você estiver se sentindo estranho, suponho que você também pode fazer com que o default_server block corresponda a nenhum de seus domínios legítimos e ofereça algo ofensivo. . . .

[1] Tecnicamente, "proxy" é a palavra errada, porque o seu servidor não está saindo e atendendo a pedidos para os clientes, apenas enviando um redirecionamento, mas não tenho certeza qual seria a palavra certa. Eu também não tenho certeza qual é o objetivo, mas ele enche seus logs com ruído e consome seu CPU e largura de banda, então você pode acabar com isso.

    
por 25.03.2018 / 07:58
-1
rewrite ^!https https://$host$request_uri permanent;
    
por 29.06.2014 / 06:33