En Nginx, ¿cómo puedo reescribir todas las solicitudes http a https mientras mantengo el subdominio?

509

Quiero reescribir todas las solicitudes http en mi servidor web para que sean solicitudes https, comencé con lo siguiente:

servidor {
    escucha 80;

    ubicación / {
      reescribir ^ (. *) https: //mysite.com$1 permanente;
    }
...


Un problema es que esto elimina cualquier información de subdominio (por ejemplo, node1.mysite.com/folder), ¿cómo podría reescribir lo anterior para redirigir todo a https y mantener el subdominio?

MikeN
fuente
2
Considere mover la 'respuesta aceptada' a serverfault.com/a/171238/90758 . Esa es la correcta.
olafure el
Simplemente use $ server_name en lugar de mysite.com codificado
Fedir RYKHTIK

Respuestas:

749

Forma correcta en nuevas versiones de nginx

Resultó que mi primera respuesta a esta pregunta fue correcta en cierto momento, pero se convirtió en otro obstáculo: para mantenerse actualizado, consulte Imposiciones de reescritura de impuestos

Muchos usuarios de SE me han corregido, por lo que el crédito recae en ellos, pero lo más importante, aquí está el código correcto:

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; 

       [....]
}
Saif Bechan
fuente
3
Sin embargo, tendría que hacer esto dominio por dominio, ¿no? ¿Qué sucede si desea aplicarlo a todos los dominios de su servidor?
JM4
30
@ JM4: si usa $ host $ en la reescritura en lugar de server_name y agrega default_server a la directiva de escucha, funcionará para cada dominio en su servidor.
Klaas van Schelven
55
Es importante mencionar que el 301 se almacena en su caché local sin fecha de caducidad. No es muy útil cuando la configuración cambia
Trefex
99
@ todos Use una redirección 307 para preservar el contenido POST.
Mahmoud Al-Qudsi
10
Tenga en cuenta que debe usar en $hostlugar de $server_namesi está usando subdominios.
Bagre
276

NOTA: https://serverfault.com/a/401632/3641 proporcionó la mejor manera de hacerlo , pero se repite aquí:

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

En el caso más simple, su host será reparado para que sea su servicio al que desea enviarlo; esto hará una redirección 301 al navegador y la URL del navegador se actualizará en consecuencia.

A continuación se muestra la respuesta anterior, que es ineficiente debido a expresiones regulares, un 301 simple es genial, como lo muestra @kmindi

He estado usando nginx 0.8.39 y superior, y usé lo siguiente:

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

Envía una redirección permanente al cliente.

Michael Neale
fuente
15
Creo que debería ser 80, ya que esto está escuchando http y luego diciéndole al cliente que regrese como https (443).
Michael Neale
3
¡Esta debería ser la mejor respuesta!
Nathan
3
Esta es la respuesta más exigente.
Caso del
1
esta es la más fácil, pero la menos segura: de esta manera, permite que su servidor redirija a un usuario a cualquier página, sin verificar si incluso se permite su uso en su servidor. Si su servidor sirve mydomain.co, los usuarios malintencionados aún podrían usar su servidor para redirigir a los usuarios a otros dominios como mydomain.co, como google.com.
friedkiwi
10
@ cab0lt no hay problema de seguridad aquí. Servir una redirección no presenta un riesgo de seguridad. Si hay requisitos de control de acceso, deben verificarse en el punto donde el navegador solicita la nueva URL. El navegador no obtendrá acceso simplemente en función de la redirección, ni necesita la redirección para solicitar la nueva URL.
mc0e
125

Creo que la mejor y única forma debería ser usar un redireccionamiento HTTP 301 movido permanentemente como este:

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

La redirección HTTP 301 movida permanentemente también es la más eficiente porque no hay expresiones regulares para evaluar, de acuerdo con fallas ya mencionadas .


El nuevo HTTP 308 Moved Permanentemente conserva el método Request y es compatible con los principales navegadores . Por ejemplo, el uso 308evita que los navegadores cambien el método de solicitud de POSTa GETpara la solicitud de redireccionamiento.


Si desea preservar el nombre de host y el subdominio, este es el camino.

Esto todavía funciona si no tienes DNS , ya que también lo estoy usando localmente. Estoy solicitando, por ejemplo, con http://192.168.0.100/index.phpy será redirigido a exactamente https://192.168.0.100/index.php.

Lo uso listen [::]:80en mi host porque lo he bindv6onlyconfigurado false, por lo que también se une al zócalo ipv4. cámbielo a listen 80si no desea IPv6 o si desea vincularlo a otra parte.

La solución de Saif Bechan utiliza la server_nameque en mi caso es localhost pero que no es accesible a través de una red.

La solución de Michael Neale es buena, pero de acuerdo con las fallas, hay una mejor solución con la redirección 301;)

kmindi
fuente
Es bueno que trates de citarlo, pero 301 no funciona en HTTPS.
Caso del
55
¿qué no funciona? la sección del servidor indicada es para que el tráfico http (sin s) no encriptado sea redirigido permanentemente al servidor encriptado (esa sección que escucha en 443 (https) no está en la lista)
kmindi
Verifiqué que esto funciona muy bien con https y todo, @kmindi actualicé mi respuesta con referencia a la suya, ya que creo que es la forma correcta y esto sigue apareciendo. Buen trabajo.
Michael Neale
Cuando se utiliza una solicitud de dominio (no IP), no funciona a menos que cambie '[::]: 80' a '80'.
Joseph Lust
ese podría ser el comportamiento esperado: trac.nginx.org/nginx/ticket/345 . Actualicé la respuesta para describir la opción de escuchar.
kmindi
20

Dentro del bloque del servidor también puede hacer lo siguiente:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}
Oriol
fuente
2
Esta configuración hizo que mi servidor produjera un bucle de redireccionamiento
Corkscreewe
Tal vez porque hay otra redirección en su lugar o https no está habilitado en su sitio / aplicación
Oriol
1
Ninguno de los otros parecía funcionar, excepto este. Usando la versión nginx: nginx / 1.10.0 (Ubuntu)
ThatGuy343
votó por https: // $ host $ uri
AMB
44
¡Este es el camino a seguir si estás detrás de un equilibrador de carga!
Antwan
17

Lo anterior no funcionó con nuevos subdominios que se crean todo el tiempo. por ejemplo, AAA.example.com BBB.example.com para aproximadamente 30 subdominios.

Finalmente obtuve una configuración que funciona con lo siguiente:

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
# ...
}
Aleck Landgraf
fuente
¡gracias! nginx devolvería 301 https://*/o cancelaría la solicitud prematuramente en las otras respuestas aquí. server_name _;con $hostfue la respuesta que hizo el truco. +1
zamnuts
1
Este es óptimo! Sin embargo, recomiendo que algunos reemplacen _con el dominio real, por ejemplo .domain.com, tenía dos servidores y nginx accidentalmente estaba dirigiendo uno de mis servidores al servidor predeterminado.
zzz
1
Esta es la única respuesta que funcionó para mí, ¡gracias!
Muñeco de nieve
Muchas gracias amigo ... He probado muchas de las soluciones pero no funcionó. Esta solución es increíble y funcionó para mí. nombre del servidor _; para qué sirve esto ... No lo entendí. Por favor explícame esto.
Pavan Kumar el
6

Publiqué un comentario sobre la respuesta correcta hace mucho, mucho tiempo con una corrección muy importante, pero creo que es necesario resaltar esta corrección en su propia respuesta. Ninguna de las respuestas anteriores es segura de usar si en algún momento tuvo una configuración HTTP no segura y esperaba contenido del usuario, tiene formularios, aloja una API o ha configurado un sitio web, herramienta, aplicación o utilidad para hablar con su sitio.

El problema ocurre cuando se realiza una POSTsolicitud a su servidor. Si la respuesta del servidor con una 30xredirección simple, el contenido POST se perderá. Lo que sucede es que el navegador / cliente va a actualizar la solicitud de SSL, pero rebajar el POSTque una GETpetición. Los POSTparámetros se perderán y se realizarán solicitudes incorrectas a su servidor.

La solución es simple. Necesita usar una HTTP 1.1 307redirección. Esto se detalla en 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).

La solución, adaptada de la solución aceptada, es usar 307en su código de redireccionamiento:

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

       [....]
}
Mahmoud Al-Qudsi
fuente
4

Logré hacerlo así:

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

https://stackoverflow.com/a/36777526/6076984

Stamster
fuente
Versión actualizada, sin IF: paste.debian.net/plain/899679
stamster
4

Estoy ejecutando ngnix detrás de un AWS ELB. El ELB está hablando con ngnix a través de http. Como el ELB no tiene forma de enviar redireccionamientos a los clientes, verifico el encabezado X-Fordered-Proto y redirijo:

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

Si usted return 301 https://$host$request_uri;es la respuesta predeterminada en el puerto 80, su servidor tarde o temprano puede obtener una lista de proxies abiertos [1] y comenzar a ser abusado para enviar tráfico a otra parte de Internet. Si sus registros se llenan de mensajes como este, entonces sabe que le ha sucedido:

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)"

El problema es que $hostrepetirá lo que el navegador envíe en el Hostencabezado o incluso el nombre de host de la línea de apertura de HTTP, como esta:

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

Debido a ese problema, algunas otras respuestas aquí recomiendan usar en $server_namelugar de $host. $server_namesiempre evalúa lo que pones en la server_namedeclaración. Pero si tiene varios subdominios allí o usa un comodín, eso no funcionará, ya que $server_namesolo usa la primera entrada después de la server_namedeclaración y, lo que es más importante, solo hará eco de un comodín (no lo expandirá).

Entonces, ¿cómo soportar múltiples dominios manteniendo la seguridad? En mis propios sistemas, he lidiado con este dilema enumerando primero un default_serverbloque que no se usa $hosty luego enumerando un bloque comodín que sí:

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

(También puede enumerar más de un dominio en el segundo bloque).

Con esa combinación, los dominios incomparables serán redirigidos a algún lugar codificado (siempre example.com), y los dominios que coincidan con los suyos irán al lugar correcto. Su servidor no será útil como proxy abierto, por lo que no atraerá problemas.

Si se siente mal, supongo que también podría hacer que el default_serverbloqueo no coincida con ninguno de sus dominios legítimos y sirva para algo ofensivo. . . .

[1] Técnicamente "proxy" es la palabra incorrecta, porque su servidor no está saliendo y cumpliendo con las solicitudes de los clientes, simplemente enviando una redirección, pero no estoy seguro de cuál sería la palabra correcta. Tampoco estoy seguro de cuál es el objetivo, pero llena sus registros con ruido y consume su CPU y ancho de banda, por lo que también podría detenerlo.

Paul A Jungwirth
fuente
0

Parece que nadie realmente lo hizo al 100%. Para que las solicitudes del puerto 80 vayan a sus 443 equivalentes para un servidor web completo, debe usar la directiva listen , no la directiva server_name para especificar el nombre general. Ver también https://nginx.org/en/docs/http/request_processing.html

servidor {
    escuchar 80 por defecto;
    escuchar [::]: 80 por defecto;
      return 307 https: // $ host $ request_uri;
}
  • $ host captura los nombres de subdominio.
  • 307 y 308 incluyen URI de solicitud POST y GET.
  • El 307 es temporal, cambie al 308 permanente después de una prueba exhaustiva:

Y asegúrese de verificar lo que ya está en /etc/nginx/conf.d/ porque la mayoría de las veces tuve problemas en los que default.conf devolvió algunos vhost existentes. Mi orden de trabajo con problemas de nginx siempre comienza moviendo el archivo predeterminado, volviendo a comentar línea por línea para ver dónde sale mal.

Julius
fuente
-1
rewrite ^!https https://$host$request_uri permanent;
nntb2a
fuente