Seguridad esencial en Nginx: TLS, cabeceras y restricciones

Capítulo 8

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

HTTPS en Nginx con TLS: qué resuelve y qué necesitas

TLS (lo que normalmente llamamos “HTTPS”) cifra el tráfico entre el navegador y tu servidor, evitando que terceros lean o modifiquen datos en tránsito. En Nginx, habilitar TLS consiste en: (1) disponer de un certificado y su clave privada, (2) configurar un server que escuche en 443 con ssl, (3) redirigir HTTP (80) a HTTPS (443), y (4) añadir cabeceras y límites defensivos.

Estructura de certificado y clave (cert/key)

  • Certificado (.crt, .pem): contiene la identidad del sitio (dominio) y la firma de una CA. Suele incluir la cadena de certificados (intermedios) según el proveedor.
  • Clave privada (.key): es secreta; Nginx la usa para negociar TLS. Si se filtra, un atacante puede suplantar tu sitio.
  • Permisos recomendados: la clave debe ser legible solo por root (o por el usuario que ejecute Nginx si aplica). Ejemplo: chmod 600 /etc/ssl/private/tu_sitio.key.

Rutas típicas en Linux: /etc/ssl/certs/ para certificados y /etc/ssl/private/ para claves. En entornos con Let’s Encrypt, suelen estar en /etc/letsencrypt/live/tu-dominio/.

Práctica 1: habilitar TLS en un server block

El siguiente ejemplo asume un dominio example.com y archivos: /etc/ssl/certs/example.com.fullchain.pem (certificado + cadena) y /etc/ssl/private/example.com.key (clave). Ajusta rutas y dominio a tu caso.

Paso 1: crear (o editar) el bloque HTTPS

server {    listen 443 ssl http2;    server_name example.com www.example.com;    ssl_certificate     /etc/ssl/certs/example.com.fullchain.pem;    ssl_certificate_key /etc/ssl/private/example.com.key;    # Selección introductoria de protocolos/cifrados (compatibilidad razonable)    ssl_protocols TLSv1.2 TLSv1.3;    ssl_ciphers HIGH:!aNULL:!MD5;    ssl_prefer_server_ciphers off;    # (Opcional) mejora de rendimiento en reanudación de sesión    ssl_session_cache shared:SSL:10m;    ssl_session_timeout 10m;    # Raíz del sitio (ejemplo)    root /var/www/example.com;    index index.html;    location / {        try_files $uri $uri/ =404;    }}

Notas rápidas: TLSv1.2 y TLSv1.3 cubren la mayoría de clientes modernos. La directiva ssl_ciphers aquí es intencionalmente simple (nivel introductorio). En muchos sistemas, los valores por defecto ya son aceptables; lo importante es evitar protocolos antiguos.

Paso 2: redirigir HTTP → HTTPS

Crea un bloque separado en el puerto 80 que responda al mismo server_name y redirija todo a HTTPS.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

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

Esto asegura que cualquier visita por HTTP termine en HTTPS, conservando ruta y parámetros.

Paso 3: validar sintaxis y recargar Nginx

sudo nginx -tsudo systemctl reload nginx

Si nginx -t muestra errores, corrígelos antes de recargar.

Probar HTTPS y la redirección con curl y navegador

Prueba 1: comprobar redirección HTTP → HTTPS

curl -I http://example.com

Deberías ver algo como:

HTTP/1.1 301 Moved PermanentlyLocation: https://example.com/...

Prueba 2: comprobar negociación TLS y respuesta HTTPS

curl -I https://example.com

Si el certificado es válido, verás HTTP/2 200 o HTTP/1.1 200 según el caso. Para inspeccionar detalles del certificado (útil para depurar cadenas incompletas), puedes usar:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates

Prueba 3: validar en navegador

  • Abre https://example.com y verifica el candado.
  • Revisa que http://example.com redirige automáticamente a HTTPS.
  • En herramientas de desarrollador (Network), selecciona la petición y revisa “Response Headers” (lo usaremos en la sección de cabeceras).

Cabeceras de seguridad comunes (y cómo aplicarlas)

Las cabeceras de seguridad ayudan a reducir riesgos del lado del navegador. Se suelen configurar en el bloque HTTPS (y en algunos casos también en HTTP si no rediriges todo). En Nginx se añaden con add_header. Para que se apliquen también en respuestas de error (por ejemplo 404/500), usa el modificador always.

HSTS (Strict-Transport-Security)

Indica al navegador que, durante un tiempo, solo debe acceder al sitio por HTTPS. Esto refuerza la redirección y ayuda contra ataques de “downgrade”.

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Precaución: activa HSTS cuando estés seguro de que HTTPS funciona bien en todo el dominio (y subdominios si usas includeSubDomains). Si lo habilitas y luego rompes HTTPS, los navegadores insistirán en HTTPS hasta que expire.

X-Content-Type-Options

Evita que el navegador “adivine” tipos MIME (mitiga ciertos vectores de ejecución inesperada).

add_header X-Content-Type-Options "nosniff" always;

X-Frame-Options

Reduce el riesgo de clickjacking al controlar si tu sitio puede ser embebido en iframes.

add_header X-Frame-Options "SAMEORIGIN" always;

Si tu sitio no debe ser embebido nunca, puedes usar DENY.

Referrer-Policy

Controla cuánta información del “referer” se envía al navegar a otros sitios.

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Aplicación práctica: añadir cabeceras al server HTTPS

Inserta estas directivas dentro del server que escucha en 443 (idealmente cerca de la configuración TLS):

server {    listen 443 ssl http2;    server_name example.com www.example.com;    ssl_certificate     /etc/ssl/certs/example.com.fullchain.pem;    ssl_certificate_key /etc/ssl/private/example.com.key;    ssl_protocols TLSv1.2 TLSv1.3;    ssl_ciphers HIGH:!aNULL:!MD5;    # Cabeceras de seguridad    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;    add_header X-Content-Type-Options "nosniff" always;    add_header X-Frame-Options "SAMEORIGIN" always;    add_header Referrer-Policy "strict-origin-when-cross-origin" always;    root /var/www/example.com;    index index.html;    location / {        try_files $uri $uri/ =404;    }}

Validar cabeceras con curl

curl -I https://example.com

Busca en la salida:

  • Strict-Transport-Security:
  • X-Content-Type-Options:
  • X-Frame-Options:
  • Referrer-Policy:

Si no aparecen, revisa que estén en el bloque correcto (el de 443) y que recargaste Nginx.

Límites defensivos: limit_req y limit_conn

Además de TLS y cabeceras, es útil aplicar límites para reducir abuso (picos de tráfico, bots agresivos, intentos de fuerza bruta). Nginx ofrece dos herramientas sencillas:

  • limit_req: limita la tasa de solicitudes (requests por segundo) por clave (por ejemplo, por IP).
  • limit_conn: limita conexiones simultáneas por clave (por ejemplo, por IP).

limit_req (rate limiting) con ejemplo simple

Se define una “zona” con limit_req_zone (normalmente en el contexto http) y luego se aplica con limit_req en un server o location.

1) Definir la zona (en nginx.conf, dentro de http { ... }):

limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=5r/s;

Esto crea una zona llamada req_per_ip y permite ~5 solicitudes por segundo por IP. 10m es memoria para almacenar estados (aprox. miles de IPs, depende del tráfico).

2) Aplicar el límite (en tu server 443 o en una ruta sensible):

location /login {    limit_req zone=req_per_ip burst=10 nodelay;    proxy_pass http://backend_login;}

Interpretación: burst=10 permite ráfagas cortas (hasta 10) antes de empezar a rechazar. Con nodelay, en vez de “ralentizar” las solicitudes extra, Nginx las procesa inmediatamente hasta el burst y luego rechaza el exceso (útil para cortar picos agresivos). Para un sitio estático, podrías aplicarlo a todo / si lo necesitas, pero suele ser mejor limitar endpoints sensibles (login, API, etc.).

limit_conn (conexiones simultáneas) con ejemplo simple

Igual que antes, defines una zona y luego aplicas el límite.

1) Definir la zona (en http { ... }):

limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;

2) Aplicar el límite (en el server 443):

server {    listen 443 ssl http2;    server_name example.com;    # ... TLS y resto ...    limit_conn conn_per_ip 20;    location / {        try_files $uri $uri/ =404;    }}

Esto limita a 20 conexiones simultáneas por IP. Es una defensa básica contra clientes que abren demasiadas conexiones.

Prueba rápida de límites

Para observar efectos, puedes lanzar varias solicitudes rápidamente. Por ejemplo, para limit_req en /login:

for i in $(seq 1 30); do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/login; done

Si el límite se activa, es común ver respuestas 429 (Too Many Requests) o 503 dependiendo de la configuración y versión/módulos. Para afinar, ajusta rate y burst según el patrón real de tu aplicación.

Checklist de verificación (TLS, redirección, cabeceras)

ObjetivoComprobaciónResultado esperado
HTTP redirige a HTTPScurl -I http://example.com301 con Location: https://...
HTTPS responde correctamentecurl -I https://example.com200 (o el código esperado)
Certificado válidoNavegador (candado) / openssl s_clientSin advertencias; dominio coincide
HSTS activocurl -I https://example.comCabecera Strict-Transport-Security
Cabeceras básicascurl -I https://example.comX-Content-Type-Options, X-Frame-Options, Referrer-Policy
Límites defensivosRáfaga de requestsRechazos/limitación al exceder umbrales

Ahora responde el ejercicio sobre el contenido:

¿Qué configuración en Nginx asegura que las cabeceras de seguridad también se incluyan en respuestas de error (por ejemplo, 404 o 500)?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

En Nginx, add_header no siempre se aplica en respuestas de error. Al añadir el modificador always, las cabeceras de seguridad se incluyen también en códigos como 404/500, reforzando la protección de forma consistente.

Siguiente capítulo

Observabilidad y solución de problemas en Nginx

Arrow Right Icon
Portada de libro electrónico gratuitaNginx para Principiantes: Domina el Servidor Web Moderno desde Cero
80%

Nginx para Principiantes: Domina el Servidor Web Moderno desde Cero

Nuevo curso

10 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.