Las cabeceras de seguridad son una de esas partes del desarrollo web que suelen aparecer tarde. Primero aprendes HTML, CSS, JavaScript, rutas, componentes, formularios, SEO, rendimiento. Luego un día pasas una herramienta de análisis o lees sobre seguridad web y descubres que tu servidor puede enviar instrucciones al navegador para reducir riesgos bastante importantes.
No son una solución mágica. Una cabecera no arregla una aplicación vulnerable, no valida formularios por ti y no sustituye revisar dependencias, permisos o lógica de servidor.
Pero sí forman parte de una web profesional.
Lo interesante es entender qué problema intenta reducir cada una. Copiar una configuración de internet sin saber qué hace puede romper imágenes, scripts, analítica, iframes o integraciones externas. En seguridad, una configuración que no entiendes es una deuda.
Qué es una cabecera HTTP
Cuando el navegador pide una página, el servidor no responde solo con HTML. También envía metadatos en forma de cabeceras HTTP.
Algunas indican el tipo de contenido. Otras controlan caché. Otras dan instrucciones de seguridad.
Por ejemplo, una respuesta puede decirle al navegador:
- Esta página solo debe cargarse por HTTPS.
- No permitas que otros sitios la metan dentro de un iframe.
- No ejecutes scripts que no estén permitidos.
- No compartas demasiada información en el
Referer. - No permitas acceso a cámara, micrófono o geolocalización.
La cabecera no cambia el contenido de la página. Cambia las reglas con las que el navegador trata esa página.
Por qué debería importarle al frontend
Durante mucho tiempo pensé en la seguridad como algo más de backend o de sistemas. Base de datos, contraseñas, servidores, permisos.
Pero muchas cabeceras afectan directamente al frontend.
Si cargas scripts externos, usas Google Analytics, sirves imágenes, trabajas con fuentes locales, tienes formularios, publicas una PWA o quieres evitar que tu web se incruste en otra página, las cabeceras te afectan.
Además, un desarrollador frontend suele tomar decisiones que luego condicionan la seguridad:
- Añadir scripts de terceros.
- Usar iframes.
- Cargar fuentes externas.
- Permitir imágenes de otros dominios.
- Insertar HTML dinámico.
- Conectar con APIs.
- Usar analítica o píxeles de seguimiento.
Por eso conviene conocer las cabeceras aunque no seas especialista en ciberseguridad. No para memorizar todas las directivas, sino para saber qué preguntas hacer antes de publicar.
Strict-Transport-Security
Strict-Transport-Security, normalmente abreviada como HSTS, le dice al navegador que ese sitio debe cargarse siempre por HTTPS durante un tiempo determinado.
Ejemplo:
Strict-Transport-Security: max-age=31536000; includeSubDomainsEl objetivo es reducir el riesgo de que alguien intente forzar una conexión insegura por HTTP.
Si una persona visita tu web una vez por HTTPS y recibe esta cabecera, el navegador recordará que debe usar HTTPS en siguientes visitas. Incluso si el usuario escribe http://, el navegador intentará ir por HTTPS.
Es una cabecera muy útil, pero no conviene activarla a ciegas con subdominios si no controlas bien toda la infraestructura. includeSubDomains significa que también afecta a subdominios. Si tienes alguno mal preparado para HTTPS, puedes bloquear accesos.
Para una web profesional publicada en un dominio propio, HTTPS debería ser obligatorio. HSTS ayuda a reforzarlo.
Content-Security-Policy
Content-Security-Policy, o CSP, es una de las cabeceras más potentes y también una de las más delicadas.
Sirve para decirle al navegador qué fuentes están permitidas para scripts, estilos, imágenes, fuentes, conexiones, iframes y otros recursos.
Un ejemplo simplificado:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'La idea es reducir el impacto de ataques como XSS. Si un atacante consigue inyectar un script, una CSP bien configurada puede impedir que ese script se ejecute o que cargue recursos desde un dominio no permitido.
Pero aquí hay que ir con cuidado. Una CSP demasiado estricta puede romper la web. Una demasiado permisiva apenas aporta.
Por ejemplo, si tu web usa Google Analytics, tendrás que permitir ciertos dominios en script-src y connect-src. Si sirves imágenes externas, tendrás que reflejarlo en img-src. Si usas estilos inline, tendrás que decidir si los permites, si usas hashes o si cambias la forma de cargar estilos.
Algunas directivas habituales:
default-src: regla por defecto cuando no hay una directiva más específica.script-src: desde dónde se pueden cargar scripts.style-src: desde dónde se pueden cargar estilos.img-src: desde dónde se pueden cargar imágenes.font-src: desde dónde se pueden cargar fuentes.connect-src: a qué dominios puede conectarse la web confetch, XHR, WebSocket o analítica.object-src: controla recursos tipo<object>o<embed>. En muchas webs modernas puede ser'none'.base-uri: limita el uso de la etiqueta<base>.frame-ancestors: controla quién puede incrustar tu página en un iframe.
Una buena forma de trabajar CSP es empezar en modo informe con Content-Security-Policy-Report-Only, revisar qué rompería y después aplicar la política real.
En una web pequeña y estática puedes llegar a una CSP bastante estricta. En una aplicación con muchas integraciones externas, necesitarás más paciencia.
X-Content-Type-Options
X-Content-Type-Options es una cabecera sencilla:
X-Content-Type-Options: nosniffLe dice al navegador que no intente adivinar el tipo de archivo si el servidor ya ha enviado un Content-Type.
Esto importa porque algunos navegadores históricamente intentaban interpretar recursos de forma flexible. Si un archivo se servía con un tipo incorrecto, podían tratarlo como JavaScript o CSS aunque no correspondiera.
Con nosniff, reduces ese comportamiento.
No es una cabecera complicada y suele tener sentido activarla en prácticamente cualquier web. La condición es servir bien los tipos de contenido. Si envías un CSS como text/plain, el navegador puede bloquearlo, y con razón.
Referrer-Policy
Cuando alguien hace clic desde tu web hacia otra página, el navegador puede enviar información sobre la URL de origen mediante el encabezado Referer.
Sí, se escribe Referer, con esa errata histórica.
Referrer-Policy permite controlar cuánta información compartes.
Una opción bastante razonable para muchas webs es:
Referrer-Policy: strict-origin-when-cross-originCon esta política, al navegar dentro del mismo sitio se puede enviar la URL completa. Al salir hacia otro origen, se envía solo el origen, no la ruta completa. Y si el destino es menos seguro, por ejemplo de HTTPS a HTTP, no se envía.
Esto ayuda a no filtrar URLs internas con parámetros o rutas sensibles a otros dominios.
No todas las webs necesitan la misma política. Si tu analítica o tus integraciones dependen de datos concretos del referer, tendrás que revisarlo. Pero enviar siempre la URL completa a cualquier destino suele ser más exposición de la necesaria.
Permissions-Policy
Permissions-Policy permite limitar qué capacidades del navegador puede usar tu página o sus iframes.
Por ejemplo:
Permissions-Policy: camera=(), microphone=(), geolocation=()Esto indica que la página no puede usar cámara, micrófono ni geolocalización.
Para una web de contenido, portfolio o blog, normalmente no necesitas acceso a cámara, micrófono, geolocalización, pagos, pantalla completa o captura de pantalla. Bloquear lo que no usas reduce superficie y deja más clara la intención de la web.
No es que una página pueda activar la cámara sin permiso del usuario. El navegador ya pide permiso. Pero si tu web no necesita esa capacidad, no hay razón para dejarla disponible.
Es una cabecera especialmente útil para aplicar el principio de mínimo privilegio: permitir solo lo necesario.
X-Frame-Options y frame-ancestors
El clickjacking consiste en engañar al usuario para que interactúe con una página legítima incrustada dentro de otra página.
Una defensa clásica era:
X-Frame-Options: DENYO:
X-Frame-Options: SAMEORIGINHoy, si usas CSP, la directiva más flexible y moderna es frame-ancestors.
Por ejemplo:
Content-Security-Policy: frame-ancestors 'none'Eso indica que nadie puede incrustar tu página en un iframe.
Para un portfolio, blog o web corporativa simple, normalmente tiene sentido bloquear que otros sitios te incrusten. Si tu proyecto necesita estar embebido en otros dominios, entonces tendrás que permitir esos orígenes concretos.
Aquí la pregunta práctica es sencilla: ¿hay algún motivo real para que otra web cargue esta página dentro de un iframe?
Si la respuesta es no, bloquéalo.
Cross-Origin-Opener-Policy
Cross-Origin-Opener-Policy, o COOP, controla cómo se relaciona tu página con otras ventanas o pestañas de distinto origen.
Una configuración común es:
Cross-Origin-Opener-Policy: same-originEsto ayuda a aislar tu documento de otros orígenes. Puede ser útil para reducir ciertos riesgos relacionados con ventanas abiertas, acceso entre contextos y aislamiento del navegador.
Pero también puede romper flujos que dependen de popups externos, como algunos inicios de sesión o integraciones de pago. Por eso a veces se usa una opción más compatible:
Cross-Origin-Opener-Policy: same-origin-allow-popupsEsta cabecera no suele ser la primera que configuraría alguien que empieza. Primero entendería CSP, HSTS, nosniff, Referrer-Policy y Permissions-Policy. Pero merece conocerla porque aparece cada vez más en auditorías y configuraciones modernas.
Cross-Origin-Resource-Policy
Cross-Origin-Resource-Policy, o CORP, permite indicar quién puede cargar un recurso desde otro origen.
Por ejemplo:
Cross-Origin-Resource-Policy: same-siteO:
Cross-Origin-Resource-Policy: cross-originNo se usa igual para todos los recursos. En assets internos puede tener sentido restringir más. En imágenes públicas, favicon u otros recursos que quieres permitir desde fuera, quizá necesitas una política más abierta.
En mi web, por ejemplo, tiene sentido pensar distinto para assets generados, imágenes públicas y páginas HTML. No todo necesita la misma regla.
Esta es una idea importante: las cabeceras no tienen por qué ser idénticas para toda la web. Un sitemap, un archivo de texto, una imagen y una página HTML no tienen el mismo comportamiento.
Cache-Control también afecta a seguridad
Cache-Control no suele meterse en la lista de cabeceras de seguridad, pero puede tener implicaciones.
En una web estática, cachear assets con hash durante mucho tiempo es razonable:
Cache-Control: public, max-age=31536000, immutableSi el archivo cambia de nombre cuando cambia su contenido, el navegador puede guardarlo mucho tiempo sin problema.
Pero no aplicarías la misma lógica a una página con información sensible, una respuesta personalizada o un documento que no quieres que quede guardado en cachés intermedias.
En una web como un portfolio, el riesgo es menor porque el contenido es público. Aun así, conviene entender que caché no es solo rendimiento. También define dónde y durante cuánto tiempo puede quedarse una respuesta.
Cabeceras que no conviene copiar por inercia
Hay cabeceras antiguas o configuraciones que todavía aparecen en artículos, pero no siempre tienen sentido hoy.
Un ejemplo típico es:
X-XSS-Protection: 1; mode=blockDurante años se usó para activar filtros XSS del navegador. Hoy está obsoleta en navegadores modernos y puede incluso crear comportamientos no deseados. Es mejor invertir el esfuerzo en una CSP bien planteada y en evitar la inyección desde la base.
También conviene tener cuidado con copiar políticas CSP enormes sin entenderlas. Si ves una configuración llena de dominios, hashes y excepciones, probablemente responde a una web concreta. No significa que sea buena para la tuya.
La regla práctica es simple: si no sabes qué recurso permite una directiva, no la pegues todavía.
Cómo lo aplicaría en una web pequeña
Para una web profesional sencilla, como un portfolio, blog o landing sin zona privada, empezaría por estas:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'No lo copiaría tal cual sin probar.
La CSP casi siempre necesitará ajustes. Si usas analítica, fuentes externas, imágenes remotas o scripts de terceros, tendrás que permitirlos de forma explícita.
El orden razonable sería:
- Activar HTTPS correctamente.
- Añadir HSTS cuando el dominio esté preparado.
- Añadir
nosniffyReferrer-Policy. - Definir una
Permissions-Policyrestrictiva. - Diseñar una CSP mínima.
- Probar en local, staging o modo report-only.
- Revisar la consola del navegador y las respuestas reales.
- Ajustar solo lo necesario.
No hace falta convertirlo en una obsesión. Hace falta tratarlo como parte normal de publicar una web.
Cómo comprobar tus cabeceras
Hay varias formas sencillas:
- Abrir DevTools, ir a Network, seleccionar una petición y revisar Response Headers.
- Usar
curl -I https://tu-dominio.com. - Pasar herramientas como Security Headers.
- Revisar la configuración del hosting.
- Comprobar que no solo funciona la home, sino también páginas internas, imágenes y archivos técnicos.
La parte importante es mirar la respuesta real en producción. No basta con que el archivo de configuración parezca correcto.
También hay que revisar después de añadir scripts externos. Muchas veces una CSP funciona hasta que añades analítica, un widget, un formulario embebido o un recurso de terceros.
Qué me llevo como desarrollador
Las cabeceras de seguridad me parecen un buen ejemplo de algo que un desarrollador web debería entender aunque no sea especialista en seguridad.
No necesitas memorizar todas las directivas. Necesitas saber qué riesgos existen y qué controles básicos puedes aplicar.
Para mí, la idea principal es esta:
Una web profesional no solo debe verse bien y cargar rápido. También debe decirle al navegador qué está permitido y qué no.
Eso reduce margen de error, obliga a pensar en los recursos que cargas y hace que la web sea más explícita.
Si estás empezando, no intentes configurar todas las cabeceras posibles el mismo día. Empieza por entender HSTS, CSP, nosniff, Referrer-Policy y Permissions-Policy. Con eso ya tienes una base mucho más seria que simplemente publicar la web y esperar que todo vaya bien.
La seguridad no aparece de golpe al final del proyecto. Se construye con pequeñas decisiones correctas desde el principio.