Pregunta ¿Cómo forzar o redirigir a SSL en nginx?


Tengo una página de registro en un subdominio como: https://signup.example.com

Solo debería ser accesible a través de HTTPS, pero me preocupa que la gente pueda encontrarlo a través de HTTP y obtener un 404.

Mi bloque html / server en nginx se ve así:

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}

¿Qué puedo agregar para que las personas que van a http://signup.example.com ser redirigido a https://signup.example.com ? (Para tu información, sé que hay complementos de Rails que pueden forzar SSL pero esperaba evitar eso)


210
2018-03-22 18:45


origen


Posible duplicado de En Nginx, ¿cómo puedo reescribir todas las solicitudes http a https mientras mantengo un subdominio? - Nasreddine


Respuestas:


De acuerdo a trampas nginx, es un poco mejor omitir la captura innecesaria, usando $request_uri en lugar. En ese caso, agregue un signo de interrogación para evitar que nginx duplique los argumentos de consulta.

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}

137
2018-03-22 19:22



O, según el sitio que has enlazado, "MEJOR": return 301 http://domain.com$request_uri; - nh2
un comentario. $ server_name $ recoge la primera variable server_name. Tenga en cuenta esto si no tiene nombres FQN en su configuración - engineerDave
@ nh2 Este es otro caso en el que la documentación es incorrecta ya que se usa return 301... provoca un error de "demasiados redireccionamientos" mientras el método de reescritura realmente funciona. - Mike Bethany
Eso ahora está documentado como "también MALO". @MikeBethany return 301 funciona, a menos que (supongo) lo estés activando además para direcciones URL correctas, escuchando en ambos puertos (ejemplo de configuración que desencadena el problema: tome serverfault.com/a/474345/29689's Primera respuesta y omita el if). - Blaisorblade
Me pregunto qué ha cambiado con los años y si esta otra respuesta es mejor: serverfault.com/a/337893/119666 - Ryan


La mejor manera como se describe en el how-to oficial es mediante el uso de la return directiva:

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

237
2017-09-03 23:50



La respuesta más corta y funcionó perfectamente en mi caso. - mateusz.fiolka
Esto generalmente se recomienda porque devuelve un 301 Moved Permanently (sus enlaces se han movido permanentemente) así como reescritura - sgb
Esto no funciona porque provoca un error de "demasiados redireccionamientos" incluso si ha configurado proxy_set_header X-Forwarded-Proto https; - Mike Bethany
@MikeBethany estás definiendo listen 443; en el mismo bloque? - Joe B
Esta debería ser la respuesta aceptada. - sjas


Esta es la forma correcta y más eficiente si desea mantener todo en un bloque de servidor:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

Todo lo demás arriba, usando "rewrite" o "if ssl_protocol" etc. es más lento y peor.

Aquí es lo mismo, pero aún más eficiente, al solo ejecutar la reescritura en el protocolo http, se evita tener que verificar la variable de $ esquema en cada solicitud. Pero en serio, es una cosa tan pequeña que no es necesario separarlos.

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}

108
2018-01-31 20:43



Genial, algunos cobardes rechazaron esta respuesta sin decir por qué, aunque esta respuesta es correcta. Tal vez otro de esos "si es malvado" cultistas. Si se molesta en leer la documentación de Nginx sobre If, ​​sabrá que IfIsNOTEvil, solo CIERTAS las utiliza en un contexto de ubicación {}, ninguna de las cuales hacemos aquí. ¡Mi respuesta es absolutamente la manera correcta de hacer las cosas! - DELETEDACC
No he rechazado este voto, pero me gustaría señalar que el valor predeterminado se ha cambiado a 'servidor_defecto' en las versiones más recientes. - spuder
La primera solución no puede ser la más eficiente, si la segunda es aún más eficiente. E incluso describiste, por qué no deberías usar un "if": "evita tener que verificar la variable de $ esquema en cada solicitud". El punto de no usar ifs no es solo sobre el desempeño, sino también sobre ser declarativo y no imperativo. - pepkin88
+1 para si ($ esquema = http) - Fernando Kosh
Debería usar $ host aquí, como se menciona en las otras respuestas. - Artem Russakovskii


Si está utilizando la nueva definición dual de servidor HTTP y HTTPS, puede usar lo siguiente:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}

Esto parece funcionar para mí y no causa bucles de redireccionamiento.

Editar:

Reemplazado:

rewrite ^/(.*) https://$server_name/$1 permanent;

con la línea de reescritura de Pratik.


56
2017-08-08 11:12



@DavidPashley tu solución funcionó como un encanto para mí. Gracias - Jayesh Gopalan
If you are using the new dual HTTP and HTTPS server definition entonces deberías separarlo. - VBart
¡Elegante y funciona perfecto! - jipipayo
Esta fue la única solución que me funcionó con mi configuración de Laravel / Homestead Nginx. - Jared Eitnier
También la línea de reescritura debería ser return 301 https://$server_name$request_uri; Como este es el método preferido. - Jared Eitnier


Otra variante más, que conserva el encabezado de solicitud Host: y sigue el ejemplo "BUENO" en trampas nginx:

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}

Aquí están los resultados. Tenga en cuenta que utilizando $server_name en lugar de $host siempre redireccionaría a https://site1.

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux

27
2018-04-11 12:20



Note that using $server_name instead of $host would always redirect to https://site1 no es eso lo que $request_uri ¿es para? - Jürgen Paul
$request_uri no contiene un nombre de host o dominio. En otras palabras, siempre comienza con un carácter "/". - Peter
La mejor respuesta por mucho. - Ashesh
No estoy seguro de por qué esta respuesta es tan baja en votos. Es el único que vale la pena usar. - zopieux
No puedo creer que tanta gente use $ server_name. Esta es la forma correcta de hacerlo. - Greg Ennis


Asegúrese de establecer 'seguro' en cualquier cookie, o de lo contrario, se enviarán en la solicitud HTTP y podrían ser capturados por una herramienta como Firesheep.


3
2018-03-23 00:40





server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}

Esto funciona mejor, creo. x.x.x.x se refiere a la IP de su servidor. Si está trabajando con Plesk 12, puede hacerlo cambiando el archivo "nginx.conf" en el directorio "/var/www/vhosts/system/domain.tld/conf" para el dominio que desee. No olvide reiniciar el servicio nginx después de guardar la configuración.


1
2017-08-23 19:40



rewrite ^ https://$host$request_uri? permanent;  Sería una mejor solución, ya que podría tener varios nombres de servidores en un host virtual.


Creo que esta es la solución más simple. Fuerza el tráfico no HTTPS y no WWW a HTTPS y www solamente.

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;
    }
}

EDITAR - Abril 2018: La solución sin IF se puede encontrar en mi publicación aquí: https://stackoverflow.com/a/36777526/6076984


0
2018-04-21 18:30



¿No son las condiciones de IF consideradas malas e ineficientes en el mundo nginx? - PKHunter
Sí lo son, en general. Pero para estas simples comprobaciones supongo que no. Sin embargo, tengo un archivo de configuración adecuado que implica más escritura de código, pero evita los IF completamente. - stamster
Google aconseja utilizar 301 en lugar de 303. Fuente: support.google.com/webmasters/answer/6073543?hl=es - dylanh724
@DylanHunt - Dejé 303 solo para las pruebas, tome nota de que el primer controlador se configuró en 301, solo el segundo Me olvidé de cambiar :) También, la solución sin los IF: stackoverflow.com/a/36777526/6076984 - stamster