Saltar al contenido

Nginx redirect a HTTPS, HSTS y cabeceras de seguridad (y la trampa de add_header)

Publicado el 07-10-20267 min de lecturaPor la redacción de SmoothSeen

En nginx, la redirección a HTTPS es un bloque server en el puerto 80 con return 301 hacia la URL https canónica. HSTS y las demás cabeceras de seguridad se añaden con add_header y el parámetro always en el server del puerto 443. Cuidado: un add_header dentro de un location anula los heredados. Desde nginx 1.29.3, add_header_inherit merge lo evita.

Lo esencial

  • Un server en el puerto 80 con return 301 a la URL canónica lleva http, https y el dominio sin www al destino final en un solo salto.
  • Sin el parámetro always, add_header solo actúa en algunos códigos de respuesta; con él, HSTS y las demás cabeceras salen también en los errores 404.
  • Un add_header dentro de un location borra todos los heredados del server. Lo comprobamos en nginx 1.30.5 y 1.31.6.
  • Desde nginx 1.29.3, add_header_inherit merge suma las cabeceras del nivel superior; en versiones anteriores, repítelas con un include.
  • El generador de configuración TLS de Mozilla se ha trasladado a TLSRef Configurator, cuya configuración intermedia admite TLS 1.2 y 1.3.

Para comprobarlo en tu web: Auditoría SEO

En esta página

Nginx controla aquí tres cosas: adónde redirige cada petición, qué cabeceras acompañan a cada respuesta y qué versiones de TLS acepta. No controla lo que haga un CDN que tengas delante, ni las cabeceras que tu aplicación ya envíe si nginx hace de proxy. Todos los fragmentos se han probado el 07-10-2026 con nginx 1.30.5 (rama estable) y nginx 1.31.6 (rama principal), las versiones actuales según nginx.org1, en sus imágenes oficiales de Docker (nginx:stable y nginx:mainline), con un certificado autofirmado y curl desde otro contenedor. La compresión, la caché y los bots de IA están en la guía de nginx para gzip, Brotli, caché y bots.

Paso 1: redirigir http a https con return 301

La versión que se ve en casi todos los tutoriales usa la variable $host:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

$host es el nombre del host de la petición y $request_uri la URI original completa, con sus parámetros2. Funciona, pero tiene dos pegas que vimos en la prueba:

  1. Dos saltos. http://example.com/ va a https://example.com/, y de ahí, si tu dominio canónico lleva www, a una segunda redirección.
  2. Repite el host que le manden. Si ese server es el único del puerto 80, también atiende peticiones con cualquier otro nombre. Con la cabecera Host: otro-dominio.test, nginx respondió Location: https://otro-dominio.test/blog/.

La versión canónica escribe el dominio a mano y añade un server en el 443 para el dominio sin www:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;
    ssl_certificate     /etc/nginx/certs/example.crt;
    ssl_certificate_key /etc/nginx/certs/example.key;
    add_header Strict-Transport-Security "max-age=300" always;
    return 301 https://www.example.com$request_uri;
}

Resultado en las dos versiones de nginx: http://example.com/blog/?a=1 llegó a https://www.example.com/blog/?a=1 con un solo 301, conservando la consulta. Si prefieres el dominio sin www, invierte los nombres.

Paso 2: HSTS con always

HSTS (Strict-Transport-Security) es una cabecera que pide al navegador que, durante max-age segundos, entre siempre por HTTPS en ese dominio. Los navegadores la ignoran si llega por una conexión sin cifrar3, así que va en los server del 443, nunca en el del 80.

El parámetro always no es opcional. La documentación de nginx dice que add_header solo añade la cabecera cuando el código de respuesta es 200, 201, 204, 206, 301, 302, 303, 304, 307 o 308, salvo que lleve always4. Sin él, un 404 o un 500 salen sin HSTS ni ninguna otra cabecera de seguridad.

Sube el max-age por etapas, como recomienda el servicio de precarga: 5 minutos (300), una semana (604800) y un mes (2592000), esperando en cada etapa el max-age completo; después, un año (31536000)5. Antes de añadir includeSubDomains, comprueba que todos tus subdominios tienen HTTPS, porque la regla se aplica a todos3. Y preload exige un año de max-age, includeSubDomains y la cabecera también en las redirecciones; salir de esa lista tarda meses5.

Paso 3: el resto de cabeceras y server_tokens

Este es el server del dominio canónico, tal como se probó:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name www.example.com;
    root /var/www/example;
    ssl_certificate     /etc/nginx/certs/example.crt;
    ssl_certificate_key /etc/nginx/certs/example.key;
    server_tokens off;

    add_header Strict-Transport-Security "max-age=300" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
    add_header Content-Security-Policy-Report-Only "default-src 'self'; frame-ancestors 'self'; report-uri /csp-informes" always;
}
Cabecera
X-Content-Type-Options: nosniff
Para qué sirve
Que el navegador no adivine un tipo de fichero distinto del declarado
Cabecera
Referrer-Policy
Para qué sirve
Que a otros dominios solo llegue el origen, no la ruta
Cabecera
X-Frame-Options: SAMEORIGIN
Para qué sirve
Que nadie meta tu web en un iframe ajeno; CSP frame-ancestors la sustituye6
Cabecera
Permissions-Policy
Para qué sirve
Desactivar cámara, micrófono o geolocalización si no los usas
Cabecera
Content-Security-Policy-Report-Only
Para qué sirve
Probar una CSP sin bloquear nada; para recibir informes necesitas report-to o report-uri7

server_tokens off quita la versión de nginx de las páginas de error y de la cabecera Server2. En la prueba, el server con la directiva respondió Server: nginx, y los de redirección, sin ella, Server: nginx/1.30.5. Ponla en el bloque http para que valga en todos los server (también lo probamos). No añadas X-XSS-Protection: 1: MDN advierte de que puede crear vulnerabilidades y recomienda CSP en su lugar8.

La trampa de add_header: un location borra las cabeceras

La documentación de nginx lo dice en una frase: las directivas add_header se heredan del nivel anterior si y solo si no hay ninguna add_header en el nivel actual4. Basta un add_header Cache-Control en un location para que ese location pierda HSTS y todas las demás.

Lo comprobamos con tres location dentro del server anterior:

# La trampa: este add_header borra los seis del server en /blog/
location /blog/ {
    add_header Cache-Control "no-cache";
}

# Arreglo 1 (nginx 1.29.3 o posterior): sumar las del nivel superior
location /assets/ {
    add_header_inherit merge;
    add_header Cache-Control "max-age=604800";
}

# Arreglo 2 (cualquier versión): repetirlas con un include
location /docs/ {
    include /etc/nginx/snippets/cabeceras-seguridad.conf;
    add_header Cache-Control "no-cache";
}
URL pedida
/
Cabeceras de seguridad recibidas
Las 6
Cache-Control
No
URL pedida
/blog/
Cabeceras de seguridad recibidas
Ninguna
Cache-Control
no-cache
URL pedida
/assets/estilos.css
Cabeceras de seguridad recibidas
Las 6
Cache-Control
max-age=604800
URL pedida
/docs/
Cabeceras de seguridad recibidas
Las 6
Cache-Control
no-cache

El resultado fue idéntico en 1.30.5 y en 1.31.6. La directiva add_header_inherit existe desde nginx 1.29.3, admite on (el comportamiento de siempre), off y merge, y se puede poner en http, server o location4. Si tu distribución trae una versión anterior, usa el include con un fichero que contenga las seis líneas add_header del server.

¿Qué versiones de TLS y qué cifrados?

Desde nginx 1.23.4, el valor por defecto de ssl_protocols es TLSv1.2 TLSv1.39. Si tu nginx es reciente y no tocas ssl_protocols, ya estás en esas dos versiones; si heredaste una configuración que menciona TLSv1 o TLSv1.1, bórralas.

Para los cifrados y el resto de parámetros, no copies listas de un blog antiguo. El generador de configuración SSL de Mozilla anuncia en su página que se ha trasladado a TLSRef Configurator10. Las directrices de TLSRef (versión 6.0) recomiendan la configuración intermedia, con TLS 1.2 y 1.3, para un servidor de uso general, y han retirado la antigua configuración «Old»11. Genera el bloque para tu versión de nginx y de OpenSSL, y pruébalo.

Cómo comprobarlo

  1. Redirección. curl -IL http://example.com/ tiene que acabar en tu URL https canónica tras un solo 301.
  2. Cabeceras en todas las rutas. curl -I https://www.example.com/ y, sobre todo, una URL de cada location que tenga su propio add_header. Si en alguna faltan las cabeceras, es la trampa de la herencia.
  3. Errores. Pide una URL que no exista: el 404 debe llevar las cabeceras.
  4. Sintaxis. nginx -t antes de cada recarga.
  5. Nota pública. El HTTP Observatory de Mozilla analiza la redirección, HSTS y el resto de cabeceras12.

Qué hace SmoothSeen con esto

SmoothSeen comprueba, dentro de Posicionamiento web, si la página responde por HTTPS, si la versión http redirige a https y qué cabeceras de seguridad envía, con el HTTP Observatory de Mozilla. Mira la URL que analizas, como un navegador: si una sección de tu web pierde las cabeceras por la herencia de add_header, lo verás al analizar una página de esa sección.

Qué hacer esta semana

Haz una lista de los location de tu configuración que lleven add_header y pide con curl -I una URL de cada uno. Si a alguno le faltan las cabeceras, añade add_header_inherit merge; o un include, y empieza HSTS con max-age=300. Para revisar esto junto al resto del posicionamiento web, haz una auditoría SEO.

Preguntas frecuentes

¿Por qué nginx no envía mis cabeceras en algunas páginas?

Casi siempre por la herencia de add_header: si el location que atiende esa página tiene su propio add_header, por ejemplo para la caché, deja de heredar todos los del server. La otra causa típica es olvidar always, que hace que las cabeceras falten en los errores 404 y 500. Comprueba ambas con curl -I.

¿Uso return 301 o rewrite para la redirección?

return 301 con la URL completa es lo más directo para mandar todo un dominio a otro: no evalúa expresiones regulares y deja claro el destino. rewrite tiene sentido cuando la URL de destino depende de partes de la ruta original. Para pasar de http a https, return basta.

¿Puedo poner add_header_inherit merge en el bloque http?

Sí. La documentación de nginx admite la directiva en http, server y location, así que puedes ponerla una vez arriba si quieres que todos los niveles sumen sus cabeceras a las heredadas. Solo existe desde nginx 1.29.3, así que en versiones anteriores nginx -t la rechazará: comprueba tu versión con nginx -v antes de usarla.

¿Necesito X-Frame-Options si ya tengo CSP con frame-ancestors?

MDN indica que la directiva frame-ancestors de CSP sustituye a X-Frame-Options. Mientras tu CSP esté en modo Report-Only, sin embargo, frame-ancestors solo informa y no protege, así que conviene mantener X-Frame-Options: SAMEORIGIN hasta que la CSP se aplique de verdad.

Fuentes

  1. 1nginx: download, nginx.org, consultada el 07-10-2026.
  2. 2Module ngx_http_core_module, nginx.org, consultada el 07-10-2026.
  3. 3Strict-Transport-Security, MDN Web Docs, actualizada el 11-09-2026.
  4. 4Module ngx_http_headers_module, nginx.org, consultada el 07-10-2026.
  5. 5HSTS Preload List Submission, Chromium, consultada el 07-10-2026.
  6. 6X-Frame-Options, MDN Web Docs, actualizada el 17-09-2026.
  7. 7Content-Security-Policy-Report-Only, MDN Web Docs, actualizada el 22-03-2026.
  8. 8X-XSS-Protection, MDN Web Docs, actualizada el 21-08-2026.
  9. 9Module ngx_http_ssl_module, nginx.org, consultada el 07-10-2026.
  10. 10Mozilla SSL Configuration Generator, Mozilla, consultada el 07-10-2026.
  11. 11Server Side TLS, TLSRef, versión 6.0, consultada el 07-10-2026.
  12. 12HTTP Observatory, Mozilla, consultada el 07-10-2026.

Cómo citar este artículo

SmoothSeen. (2026, 7 de octubre). Nginx redirect a HTTPS, HSTS y cabeceras de seguridad (y la trampa de add_header). https://smoothseen.com/es/blog/nginx-https-hsts-cabeceras-seguridad/

Quién lo escribe

SmoothSeen es una herramienta de auditoría web que mide el posicionamiento en buscadores y en asistentes de IA y entrega informes con la marca de la agencia.

Este blog es de SmoothSeen: cuando un artículo habla del producto, lo hace sabiendo que es el nuestro. Las cifras de terceros enlazan a su fuente original.

Historial de cambios

  • Primera versión.

Sigue leyendo

Nginx: redirect a HTTPS, HSTS y cabeceras de seguridad