Cabeceras HTTP de seguridad: CSP, HSTS y las demás
Qué cabeceras HTTP de seguridad configurar, con valores de partida, qué rompe cada una si te equivocas y cómo implantar la CSP sin tumbar el sitio.
En esta página 11 min de lectura
- Resumen: valores de partida y qué puede romperse
- Strict-Transport-Security (HSTS)
- Content-Security-Policy (CSP)
- X-Content-Type-Options
- Referrer-Policy
- Permissions-Policy
- X-Frame-Options o frame-ancestors
- Cross-Origin-Opener-Policy y Cross-Origin-Resource-Policy
- Cabeceras que ya no deben usarse
- Un conjunto inicial completo
- Cómo verificar tu sitio
Las cabeceras HTTP de seguridad que sirven para casi cualquier sitio son: Strict-Transport-Security (obliga a usar HTTPS), Content-Security-Policy (limita los scripts y quién puede mostrar la página en un iframe), X-Content-Type-Options: nosniff, Referrer-Policy y Permissions-Policy. X-Frame-Options todavía sirve de respaldo para navegadores antiguos, y Cross-Origin-Opener-Policy y Cross-Origin-Resource-Policy aíslan la página de otros sitios. X-XSS-Protection, Expect-CT y Feature-Policy ya no deben usarse. La CSP es la que más trabajo da y la que más rompe; conviene empezarla en modo de informe.
Este texto cubre el comportamiento de los navegadores y las recomendaciones de MDN, OWASP y el W3C en octubre de 2026. Forma parte de la guía de seguridad básica de un sitio web, que trata HTTPS, archivos expuestos, versiones y cookies.
Resumen: valores de partida y qué puede romperse
Los valores de abajo son un punto de partida habitual, basado en las referencias citadas en cada sección. No son los únicos correctos: cada sitio tiene que ajustarlos según lo que usa.
| Cabecera | Valor de partida | Qué puede romperse |
|---|---|---|
Strict-Transport-Security | max-age=63072000; includeSubDomains | Los subdominios que todavía no tienen HTTPS quedan inaccesibles |
Content-Security-Policy | Política con nonce, object-src 'none', base-uri 'none' y frame-ancestors | Scripts inline, eventos onclick, eval(), widgets de terceros |
X-Content-Type-Options | nosniff | Scripts y CSS servidos con un Content-Type incorrecto |
Referrer-Policy | strict-origin-when-cross-origin | Herramientas que dependen de la ruta completa de origen en otro dominio |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Funciones que el sitio o un iframe de un socio usan de verdad |
X-Frame-Options | DENY o SAMEORIGIN | La visualización legítima del sitio dentro de iframes |
Cross-Origin-Opener-Policy | same-origin, o same-origin-allow-popups si hay login o pago en ventana emergente | Ventanas emergentes de OAuth y de pago que se comunican con la ventana que las abrió |
Cross-Origin-Resource-Policy | same-site | Archivos de tu dominio que usan otros sitios |
Strict-Transport-Security (HSTS)
HSTS le dice al navegador que a ese host solo se puede acceder por HTTPS durante max-age segundos. Después de recibir la cabecera, el navegador convierte por su cuenta las peticiones http en https y no deja que el usuario ignore errores de certificado (MDN: Strict-Transport-Security). Hay tres detalles que importan:
La cabecera solo vale si llega en una respuesta HTTPS; enviada por http, se ignora.
Solo protege a partir de la segunda visita, porque el navegador tiene que haber recibido la cabecera una vez. La lista de precarga de los navegadores (
preload) cierra ese hueco.includeSubDomainsextiende la regla a todos los subdominios, presentes y futuros.
Strict-Transport-Security: max-age=63072000; includeSubDomainsMozilla recomienda como mínimo seis meses y sugiere dos años (63072000) (MDN: guía práctica de TLS); ASVS 5.0 pide como mínimo un año y, desde el nivel 2, incluir los subdominios (ASVS 5.0, requisito 3.4.1). Recomendación de implantación: empieza con un max-age corto, comprueba que todo funciona y solo entonces súbelo, porque el navegador recordará la regla durante todo el plazo. OWASP advierte que un HSTS mal configurado o un problema de certificado puede dejar a los usuarios sin acceso durante todo el max-age (OWASP HTTP Headers Cheat Sheet).
Para entrar en la lista de precarga, el sitio necesita un certificado válido, redirigir http a https en el mismo host, tener HTTPS en todos los subdominios y enviar un max-age de al menos un año con includeSubDomains y preload. Salir de la lista es lento (puede tardar meses en llegar a los usuarios) y los demás navegadores no tienen obligación de seguir la eliminación (hstspreload.org). Haz la solicitud solo si todos los subdominios, incluidos los internos, van a seguir en HTTPS a largo plazo.
Content-Security-Policy (CSP)
La CSP es una lista de reglas que le dice al navegador de dónde puede la página cargar scripts, estilos, imágenes e iframes y adónde puede enviar datos. Su uso principal es reducir el efecto del XSS (cross-site scripting, la inyección de script en la página): aunque un atacante logre insertar un <script>, el navegador no lo ejecuta si no cumple la política. Es una capa más y no sustituye la corrección de los fallos de XSS (web.dev: strict CSP).
Lista de dominios o política estricta con nonce
Hay dos formas de escribir la regla de scripts. La antigua es enumerar los dominios permitidos (script-src 'self' https://cdn.ejemplo.com). La que recomiendan MDN y web.dev es la llamada CSP estricta, basada en nonce o hash (MDN: guía de CSP; web.dev: strict CSP). ASVS 5.0 acepta las dos formas en el nivel 2, siempre que la política tenga object-src 'none' y base-uri 'none', y exige nonce o hash por respuesta en el nivel 3 (ASVS 5.0, requisito 3.4.3).
Un nonce es un valor aleatorio que el servidor genera en cada respuesta, pone en la cabecera y repite en el atributo nonce de cada <script> legítimo. El navegador solo ejecuta los scripts que llevan el valor correcto. El nonce tiene que ser impredecible y distinto en cada respuesta, y debe insertarlo el sistema de plantillas, nunca en todos los <script> de la página de forma indiscriminada, para no sellar también un script inyectado (MDN: guía de CSP).
Content-Security-Policy:
script-src 'nonce-R4nd0mPorRespuesta' 'strict-dynamic';
object-src 'none';
base-uri 'none';
frame-ancestors 'self'
<script nonce="R4nd0mPorRespuesta" src="/js/app.js"></script>'strict-dynamic' hace que también se ejecuten los scripts creados por un script que ya es de confianza. Así las bibliotecas y los widgets de terceros pueden cargar sus propios archivos sin que tengas que enumerar cada dominio (web.dev: strict CSP). Para páginas estáticas o servidas desde caché, web.dev recomienda hash en lugar de nonce: 'sha256-...' calculado sobre el contenido de cada script inline. Un nonce guardado en una página en caché se repite para todos los visitantes y pierde el sentido.
frame-ancestors
La directiva frame-ancestors define qué páginas pueden mostrar el sitio dentro de un <iframe>, lo que protege contra el clickjacking (cuando otro sitio muestra el tuyo por debajo de botones falsos). MDN recomienda 'none' para sitios que no necesitan incrustarse (MDN: guía de CSP). No hereda el valor de default-src y no funciona en metaetiqueta (MDN: frame-ancestors).
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self' https://socio.ejemplo.comModo de solo informe
La cabecera Content-Security-Policy-Report-Only aplica la misma sintaxis sin bloquear nada: el navegador solo registra y envía las infracciones (MDN: Content-Security-Policy-Report-Only). Las dos cabeceras pueden enviarse juntas; en ese caso se aplica la política normal y la de informe solo genera avisos, lo que permite probar una versión más estricta sin tocar la que está en vigor (MDN: guía de CSP).
Los informes van a la dirección definida en la cabecera Reporting-Endpoints y referenciada por la directiva report-to. La directiva antigua report-uri está obsoleta, pero MDN recomienda declarar las dos mientras report-to no funcione en todos los navegadores (MDN: guía de CSP; W3C: CSP Level 3).
Reporting-Endpoints: csp="https://ejemplo.com/csp-informes"
Content-Security-Policy-Report-Only:
script-src 'nonce-R4nd0mPorRespuesta' 'strict-dynamic';
object-src 'none';
base-uri 'none';
report-uri https://ejemplo.com/csp-informes;
report-to cspCómo implantar la CSP sin tumbar el sitio
Haz una lista de los scripts y recursos de cada tipo de página (inicio, producto, checkout, área privada) con la pestaña Network de DevTools.
Elige nonce si las páginas se generan en el servidor en cada petición, o hash si son estáticas o se sirven desde caché (web.dev).
Ajusta el código que la política va a bloquear: cambia los eventos inline como
onclickporaddEventListener, cambia los enlacesjavascript:por eventos y sustituyeeval()(por ejemplo, porJSON.parse()) (web.dev).Publica la política en
Content-Security-Policy-Report-Onlycon un endpoint de informes.Sigue los informes el tiempo suficiente para cubrir todos los tipos de página y flujos, incluidos el checkout y las campañas de temporada, y corrige o permite cada origen legítimo.
Cambia a
Content-Security-Policyy mantén el envío de informes.Revisa la política en CSP Evaluator o en Lighthouse antes y después del cambio, como sugiere web.dev.
Lo que suele romperse
Scripts inline sin nonce ni hash, incluidos los que imprimen en la página los plugins y temas.
Atributos de evento (
onclick,onload) y enlacesjavascript:, que la CSP estricta bloquea.Bibliotecas que usan
eval()onew Function(). Permitirlas con'unsafe-eval'debilita la política (web.dev).Un nonce en una página guardada en caché, que repite el valor y hace que fallen scripts o deja la protección sin efecto.
Con una política de lista de dominios: cualquier origen nuevo (un píxel, un chat, una fuente) queda bloqueado hasta que alguien actualice la lista.
X-Content-Type-Options
Con nosniff, el navegador deja de adivinar el tipo del archivo por su contenido y usa el Content-Type declarado. Las peticiones de script se bloquean si el tipo no es de JavaScript, y las de hoja de estilo si no es text/css. Esto evita, por ejemplo, que un archivo subido por un usuario como texto se ejecute como HTML o script (MDN: X-Content-Type-Options). ASVS pide la cabecera en todas las respuestas (requisito 3.4.4).
X-Content-Type-Options: nosniffLo que se rompe: archivos servidos con un tipo incorrecto, como un .js entregado como text/plain. Antes de activarla, revisa el Content-Type de tus scripts y CSS, sobre todo los que se generan de forma dinámica.
Referrer-Policy
La cabecera Referer le dice al sitio de destino de dónde viene el visitante. Si la dirección lleva datos en la ruta o en la query string (un token, un correo, un número de pedido), esos datos se filtran a terceros. Referrer-Policy controla cuánto de esa dirección se envía. Cuando el sitio no define nada, el valor por defecto de los navegadores hoy es strict-origin-when-cross-origin: la dirección completa para el propio sitio, solo el origen para otros sitios en HTTPS y nada cuando la conexión baja a HTTP (MDN: Referrer-Policy).
Referrer-Policy: strict-origin-when-cross-originOWASP recomienda declarar la cabecera en vez de depender del valor por defecto del navegador y usa ese valor como ejemplo (OWASP HTTP Headers Cheat Sheet); la lista de valores de OWASP Secure Headers Project va más allá y sugiere no-referrer (headers_add.json). no-referrer es más restrictivo, pero puede estorbar a las herramientas de analítica que dependen del origen del tráfico. Evita unsafe-url, que envía la dirección completa incluso a destinos en HTTP.
Permissions-Policy
Permissions-Policy activa o desactiva funciones del navegador, como la cámara, el micrófono, la geolocalización y el pago, para la página y para los iframes que contiene. () desactiva la función en todas partes; self la permite solo para el mismo origen; un origen entre comillas la permite para ese dominio (MDN: Permissions-Policy).
Permissions-Policy: camera=(), microphone=(), geolocation=()
Permissions-Policy: geolocation=(self "https://mapas.ejemplo.com"), microphone=()La recomendación de OWASP es desactivar todo lo que el sitio no usa (OWASP HTTP Headers Cheat Sheet). MDN marca la cabecera como experimental y dice que no funciona en algunos navegadores muy usados, así que trátala como una protección adicional, no como la única barrera. Lo que se rompe: un iframe de un socio (mapa, vídeo, pago) que necesita una función desactivada. En el iframe, el atributo allow concede la función dentro del límite que permite la cabecera.
X-Frame-Options o frame-ancestors
X-Frame-Options fue la primera cabecera contra el clickjacking. Acepta DENY (nadie muestra la página en un iframe) y SAMEORIGIN (solo páginas del mismo origen). El valor ALLOW-FROM está obsoleto y hace que los navegadores modernos ignoren la cabecera entera, y la cabecera no tiene efecto en metaetiqueta (MDN: X-Frame-Options).
La directiva frame-ancestors de la CSP hace lo mismo con más opciones, como permitir dominios concretos. La especificación de la CSP define que, cuando se aplica una política con frame-ancestors, el navegador ignora X-Frame-Options (W3C: CSP Level 3, sección 6.4.2.2). ASVS 5.0 trata X-Frame-Options como obsoleta y pide frame-ancestors (requisito 3.4.6). Recomendación: usa frame-ancestors como regla principal y mantén un X-Frame-Options coherente para navegadores antiguos. No molesta a los nuevos, que en ese caso ya lo ignoran.
Cross-Origin-Opener-Policy y Cross-Origin-Resource-Policy
Cross-Origin-Opener-Policy (COOP) decide si tu página comparte el contexto de navegación con ventanas de otros sitios que ella abrió o que la abrieron. Con same-origin, una ventana de otro dominio queda aislada y pierde la referencia window.opener. Eso rompe los flujos de login (OAuth) y de pago en ventana emergente que devuelven el resultado a la ventana original; para esos casos existe same-origin-allow-popups (MDN: Cross-Origin-Opener-Policy). ASVS pide uno de los dos valores en las respuestas que abren documentos (requisito 3.4.8).
Cross-Origin-Opener-Policy: same-origin-allow-popupsCross-Origin-Resource-Policy (CORP) va en el recurso, no en la página. Dice qué orígenes pueden cargar ese archivo en peticiones sin CORS, como <img> y <script>: same-origin, same-site o cross-origin (MDN: Cross-Origin-Resource-Policy). Mozilla incluye CORP entre las protecciones contra ataques de canal lateral especulativo (MDN: guías prácticas), y OWASP sugiere same-site. Lo que se rompe: imágenes, fuentes o scripts de tu dominio que usan otros sitios, como un widget que distribuyes. Esos archivos necesitan cross-origin.
La pareja Cross-Origin-Embedder-Policy: require-corp con COOP same-origin activa el aislamiento entre orígenes, necesario para funciones como SharedArrayBuffer (MDN). COEP bloquea los recursos de otros orígenes que no autoricen su uso por CORP o CORS (OWASP). Opinión: en un sitio corporativo o una tienda que no necesita ese aislamiento, COEP suele costar más (anuncios, vídeos y widgets bloqueados) de lo que aporta.
Cabeceras que ya no deben usarse
| Cabecera | Situación | Qué hacer |
|---|---|---|
X-XSS-Protection | Fuera de cualquier especificación y obsoleta. MDN advierte que el filtro puede crear fallos de XSS en sitios que no los tenían (MDN) | No enviarla, o enviar 0 para desactivar el filtro, como sugiere OWASP; proteger con CSP |
Expect-CT | Solo la implementó Chromium, que la considera obsoleta desde la versión 107 porque ya exige Certificate Transparency por defecto (MDN) | Quitarla |
Feature-Policy | Renombrada como Permissions-Policy, con otra sintaxis. En Chrome funciona solo como alias, y Permissions-Policy prevalece cuando llegan las dos (Chrome for Developers) | Migrar a Permissions-Policy |
Public-Key-Pins | OWASP recomienda no usarla y quitarla de producción (OWASP) | Quitarla |
Un conjunto inicial completo
Un ejemplo para un sitio servido de forma dinámica, que otros sitios no incrustan y con pago en ventana emergente. La forma de configurarlo depende del servidor (nginx, Apache), del CDN o del framework; el contenido de las cabeceras es el mismo.
Strict-Transport-Security: max-age=63072000; includeSubDomains
Content-Security-Policy: script-src 'nonce-{generado por respuesta}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; report-to csp
Reporting-Endpoints: csp="https://ejemplo.com/csp-informes"
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
X-Frame-Options: DENY
Cross-Origin-Opener-Policy: same-origin-allow-popupsPara comparar, OWASP Secure Headers Project publica una lista más estricta y actualizada con frecuencia, pensada como referencia máxima y no como configuración para copiar sin probar (headers_add.json).
Cómo verificar tu sitio
En la terminal, curl -sI https://ejemplo.com/ muestra las cabeceras de una respuesta. Repítelo con una página interna, un archivo de imagen y una respuesta de error, porque los servidores y los CDN no siempre aplican las mismas cabeceras a todo. HTTP Observatory de Mozilla da una nota y explica cada punto. La auditoría de WebVyze señala la falta de HSTS, CSP, X-Content-Type-Options y Referrer-Policy, la CSP que existe pero no frena scripts inyectados y la página que otros sitios pueden incrustar, cada una con su guía de corrección. En las tiendas, la CSP de la página de pago también cuenta para PCI DSS; el artículo sobre los requisitos 6.4.3 y 11.6.1 explica cómo, y los planes de pago de WebVyze mantienen un inventario de los scripts de las páginas de checkout. WebVyze solo lee páginas públicas y no modifica nada en el sitio.
Preguntas frecuentes
- ¿Puedo poner la CSP en una metaetiqueta en lugar de en la cabecera?
- Sí, con límites. La especificación ignora las directivas frame-ancestors, report-uri y sandbox cuando la política viene de una metaetiqueta, y el modo de solo informe (Content-Security-Policy-Report-Only) no funciona en metaetiqueta. Para una política completa, usa la cabecera.
- ¿Las cabeceras tienen que estar en todas las respuestas o solo en las páginas HTML?
- Depende de la cabecera. ASVS 5.0 pide HSTS, nosniff y frame-ancestors en todas las respuestas y Cross-Origin-Opener-Policy en las respuestas que abren un documento, como las páginas HTML. Configurarlas en el servidor o en el CDN para todas las respuestas suele ser lo más sencillo.
- ¿Quitar las cabeceras Server y X-Powered-By mejora la seguridad?
- Dificulta identificar la tecnología que usas, y OWASP recomienda quitar esas cabeceras o dejarlas sin información útil. No impide que alguien descubra la tecnología por otras vías y no te ahorra las actualizaciones.
- ¿Cómo recibo los informes de infracción de la CSP?
- El navegador envía un POST en JSON a la dirección declarada en Reporting-Endpoints (directiva report-to) o en report-uri. Necesitas un endpoint que acepte y guarde esos envíos: un servicio propio sencillo o una herramienta de terceros.
Vea lo que su sitio necesita corregir.
Una verificación gratuita por ley, con la causa y la guía de corrección de cada problema.
Siga leyendo
- SEO técnico
Bots de IA en robots.txt: búsqueda, acceso del usuario y entrenamiento
GPTBot, ClaudeBot, OAI-SearchBot, Google-Extended: qué hace cada robot de IA, si respeta el robots.txt y cómo bloquear el entrenamiento sin dejar la búsqueda.
- SEO técnico
Redirecciones: cadenas, bucles y cómo corregirlos
Cómo trata Google los 301, 302, 307 y 308, qué causan las cadenas y los bucles en el rastreo y la velocidad, y cómo mapear, corregir y probar redirecciones.
- SEO técnico
llms.txt: qué es, qué no hace y cómo crearlo
El llms.txt resume el sitio para agentes de IA. Su formato, qué dicen Google y otras empresas, cuándo merece la pena crearlo y cómo escribir un buen archivo.