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:
- Dos saltos.
http://example.com/va ahttps://example.com/, y de ahí, si tu dominio canónico lleva www, a una segunda redirección. - 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-ancestorsla 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-tooreport-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
- Redirección.
curl -IL http://example.com/tiene que acabar en tu URL https canónica tras un solo301. - Cabeceras en todas las rutas.
curl -I https://www.example.com/y, sobre todo, una URL de cadalocationque tenga su propioadd_header. Si en alguna faltan las cabeceras, es la trampa de la herencia. - Errores. Pide una URL que no exista: el 404 debe llevar las cabeceras.
- Sintaxis.
nginx -tantes de cada recarga. - 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
- 1nginx: download, nginx.org, consultada el 07-10-2026.
- 2Module ngx_http_core_module, nginx.org, consultada el 07-10-2026.
- 3Strict-Transport-Security, MDN Web Docs, actualizada el 11-09-2026.
- 4Module ngx_http_headers_module, nginx.org, consultada el 07-10-2026.
- 5HSTS Preload List Submission, Chromium, consultada el 07-10-2026.
- 6X-Frame-Options, MDN Web Docs, actualizada el 17-09-2026.
- 7Content-Security-Policy-Report-Only, MDN Web Docs, actualizada el 22-03-2026.
- 8X-XSS-Protection, MDN Web Docs, actualizada el 21-08-2026.
- 9Module ngx_http_ssl_module, nginx.org, consultada el 07-10-2026.
- 10Mozilla SSL Configuration Generator, Mozilla, consultada el 07-10-2026.
- 11Server Side TLS, TLSRef, versión 6.0, consultada el 07-10-2026.
- 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/
Sigue leyendo
Posicionamiento web (SEO): qué es, cómo funciona y cómo mejorarlo en 2026
Qué es el posicionamiento web, cómo decide Google qué páginas muestra y una checklist priorizada para mejorarlo con herramientas gratuitas.
.htaccess y caché: activar gzip y Brotli, la caché del navegador y bloquear bots de IA en Apache
Cómo activar gzip y Brotli, configurar la caché del navegador y vetar bots de IA de entrenamiento en el .htaccess de Apache sin salir de ChatGPT. Probado.
.htaccess y HTTPS: redirigir http a https, activar HSTS y cabeceras de seguridad en Apache
Cómo redirigir http a https en .htaccess o en el VirtualHost de Apache, activar HSTS sin riesgos y añadir cabeceras de seguridad. Configuración probada.