Blog · 12 may 2026 · 6 min de lectura

HSTS en Apache: cómo activarlo sin romper subdominios

Strict-Transport-Security cierra la ventana de TLS stripping pero, mal puesto, deja inaccesibles subdominios HTTP legacy. Guía para llegar a max-age=2 años + includeSubDomains + preload sin sustos.

Strict-Transport-Security (HSTS) le dice al browser que tu sitio se accede SIEMPRE por HTTPS. La primera vez que un visitante llega por HTTPS y recibe el header, lo guarda; cualquier intento futuro de cargar el sitio por http:// se reescribe a https:// antes de salir del browser.

Es el control que cierra el último hueco que dejó la migración a HTTPS: el primer http:// que un usuario tipea o que un link viejo apunta. Sin HSTS, ese primer request va en claro y puede ser interceptado para hacer downgrade (TLS stripping).

El problema: HSTS es pegajoso. Una vez que un browser lo memoriza, no hay forma práctica de bajarse rápido si te equivocaste. Por eso vale la pena entender qué activás antes de pegarlo.

Anatomía del header

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Tres piezas, cada una con su trade-off:

La trampa de includeSubDomains

Este es el campo que más se equivoca. Implica que cualquier subdominio bajo tu zona también va a forzarse a HTTPS.

Caso real que vemos a menudo:

La lógica defensiva es la correcta — un atacante que controle DNS del subdominio podría exfiltrar cookies del apex si HSTS no aplicara a subs. Pero en práctica te tomás el tiempo de inventariar todos tus subs antes de prender la regla.

El camino seguro: aumentar gradualmente

La receta que usamos en clientes:

Paso 1 — max-age corto, sin includeSubDomains, sin preload

Strict-Transport-Security: max-age=300

Cinco minutos. Si rompés algo, en 5 minutos los browsers olvidan. Lo dejás 24-48 horas en producción para verificar que el apex realmente sirve TLS bien (no hay request mixto, no hay redirect-loop, todo funciona).

Paso 2 — max-age 6 meses, sin includeSubDomains todavía

Strict-Transport-Security: max-age=15552000

Lo dejás 1-2 semanas. Esto ya te da la protección real para el apex.

Paso 3 — Inventario de subdominios + decisión sobre includeSubDomains

Antes de agregarlo, listá todos tus subs:

dig +short NS cliente.cl
dig +short ANY cliente.cl
# o consulta los CT logs:
curl -s 'https://crt.sh/?q=%25.cliente.cl&output=json' | jq -r '.[].name_value' | sort -u

Para cada uno, verifica:

Si todos sirven HTTPS bien, agregás includeSubDomains. Si hay alguno HTTP-only, primero lo migrás a HTTPS o lo retirás del DNS.

Strict-Transport-Security: max-age=15552000; includeSubDomains

Una semana más en este estado.

Paso 4 — max-age 2 años + preload

Cuando ya estás seguro de que todo el ecosistema soporta TLS y querés cerrar el primer-request gap:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Submitís el dominio en hstspreload.org. Toma 1-3 meses entrar en la próxima release de Chromium, después Firefox/Safari/Edge la heredan.

Importante: una vez en la preload list, salir es lento. Solicitar la remoción puede tomar meses y los browsers viejos pueden quedarte preloaded por años. No actives preload mientras dudes.

Config en Apache

Vía .htaccess o vhost

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>

El flag always es importante: sin él, Apache aplica el header solo en respuestas 200; con always también lo manda en 30x/40x/50x, donde tradicionalmente se “olvida” — y es justamente en un 301 HTTP→HTTPS donde más útil resulta.

Redirect 301 obligatorio antes

HSTS asume que tu sitio responde por HTTPS. El redirect HTTP→HTTPS debe estar antes:

<VirtualHost *:80>
    ServerName cliente.cl
    ServerAlias www.cliente.cl
    Redirect permanent / https://cliente.cl/
</VirtualHost>

O en .htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>

El header HSTS debe servirse desde HTTPS (los browsers ignoran HSTS recibido por HTTP), así que el vhost :443 es el que lleva el Header always set.

Detrás de Cloudflare

Si estás detrás de CF (o cualquier CDN), tenés dos opciones para servir HSTS:

  1. Del origen: Apache pone el header, CF lo deja pasar. Pro: una sola fuente de verdad. Contra: si tu origen alguna vez se sirve directo (CF bypass), el header sigue saliendo.
  2. Del edge (CF dashboard): CF inyecta el header. Pro: aunque tu origen se sirva en HTTP por algún motivo, CF te cubre. Contra: si migrás de CDN o quitás CF, perdés el header sin enterarte.

En la práctica, ambos: el origen lo manda Y CF lo refuerza. Cloudflare detecta duplicados y deja uno solo.

Cómo verificás

Antes de pegarlo en producción

# verifica que el sitio sirve HTTPS bien
curl -sI https://cliente.cl/ | grep -i "strict-transport"

# verifica que ningún subdominio queda HTTP-only:
for sub in www app api blog dashboard mail; do
  echo "--- $sub.cliente.cl ---"
  curl -sI --max-time 5 https://$sub.cliente.cl/ 2>&1 | head -1
done

Después de pegarlo

# debería verse el header completo
curl -sI https://cliente.cl/ | grep -i "strict-transport"
# Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

# verifica que el redirect 301 desde HTTP funciona
curl -sI http://cliente.cl/ | head -5
# debería devolver 301 hacia https://...

Antes de submitir a preload

hstspreload.org hace los checks por vos: header presente, max-age suficiente, includeSubDomains, preload, redirect 301, cert válido, subdominios accesibles por HTTPS. Si fallás algún check, te dice cuál — y mejor arreglarlo antes que después.

Salida de emergencia

Si pegaste HSTS y querés revertir:

  1. Antes de preload: bajá max-age a 0 (no quites el header). Los browsers que reciban max-age=0 borran la entrada. Tardás max-age original en cubrir a todos los visitantes que tuvieron la versión anterior.
  2. Después de preload: hay que solicitar remoción en la HSTS preload list + esperar la siguiente release de Chrome (~6 semanas) + heredan los demás browsers (~3 meses) + los browsers viejos pueden quedarte protegidos por años. No hay rollback rápido.

Cómo lo detectamos en el FreeScan

El Asentic FreeScan reporta tres patrones distintos:

La distinción importa porque “no implementado” y “implementado pobre” se ven igual en una auditoría superficial, pero requieren acciones distintas.

Cierre

HSTS es de los headers con mejor ratio efecto/esfuerzo. La parte difícil no es el header en sí — son tres líneas de Apache — sino el inventario de subdominios que hay que hacer antes para no romper el ecosistema. Dedicarle un día a eso y migrar gradualmente vale el costo de la postura final.

Si tu sitio ya sirve HTTPS hace tiempo, lo más probable es que el camino al preload esté a una semana de distancia.

¿Quieres una segunda opinión sobre tu sitio?

Si lo que leíste te dejó preguntas sobre tu propio dominio, podemos partir conversando — sin compromiso.

Conversemos