Saltar al contenido

Cabeceras de seguridad HTTP: qué son y cómo verificarlas

En resumen

Las cabeceras de seguridad HTTP que importan son seis: Strict-Transport-Security (HSTS), Content-Security-Policy (CSP), X-Frame-Options, X-Content-Type-Options (nosniff), Referrer-Policy y Permissions-Policy. Se añaden en Apache, Nginx, WordPress o PrestaShop y se comprueban con curl -I o con el análisis externo gratuito de WebIntacta. CSP conviene empezarla en modo observación (Report-Only) para no romper la web.

Las cabeceras de seguridad son unas pocas líneas que tu servidor envía con cada página y que le dicen al navegador qué puede hacer y qué no: usar solo HTTPS, no dejar que otra web te meta en un marco, no adivinar tipos de archivo. No sustituyen a tener la web actualizada, pero cierran ataques enteros a cambio de muy poco trabajo.

Por Jose SorianoPublicada: Última actualización: 7 min de lectura

Las seis que importan

Strict-Transport-Security (HSTS)

Le dice al navegador que, durante el tiempo indicado, solo entre a tu web por HTTPS, aunque alguien escriba http:// o pulse un enlace antiguo. Evita que un atacante en la misma red (una wifi pública) intercepte la primera visita sin cifrar.

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

max-age va en segundos (31536000 es un año). includeSubDomains lo extiende a todos los subdominios: actívalo solo si todos funcionan con HTTPS, incluidos los internos. La directiva preload sirve para entrar en la lista que los navegadores traen de fábrica; para eso hacen falta al menos un año de max-age, includeSubDomains, redirección de HTTP a HTTPS y certificado válido en todos los subdominios. Salir de esa lista tarda meses, así que no la añadas por probar.

Content-Security-Policy (CSP)

Es la que más protege y la más delicada. Define de qué orígenes puede cargar la página scripts, estilos, imágenes, fuentes o marcos. Si un atacante consigue inyectar un <script> que apunta a su dominio, una buena CSP impide que se ejecute. Es la defensa de fondo contra el XSS y contra muchos skimmers de tarjetas.

Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com; img-src 'self' data: https:; frame-ancestors 'self'; object-src 'none'; base-uri 'self'

El problema: un WordPress o un PrestaShop con diez plugins carga recursos de muchos sitios, y una CSP mal hecha rompe el pago, el chat o las analíticas. Más abajo explico cómo implantarla sin romper nada.

X-Frame-Options y frame-ancestors

Impiden que otra web cargue la tuya dentro de un marco invisible para engañar al usuario y que haga clic donde no quiere (clickjacking). La forma moderna es la directiva frame-ancestors de la CSP; X-Frame-Options es la antigua, pero sigue siendo útil para navegadores viejos y no estorba tenerlas las dos. OWASP recomienda frame-ancestors si puedes, y si no, X-Frame-Options: DENY.

X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

DENY prohíbe cualquier marco; SAMEORIGIN permite solo los de tu propio dominio (algunos editores visuales y constructores de páginas lo necesitan). Ojo: frame-ancestors no funciona si pones la CSP en una etiqueta <meta>; tiene que ir en la cabecera.

X-Content-Type-Options

Con nosniff, el navegador respeta el tipo de archivo que declara el servidor y no intenta adivinarlo. Evita que un archivo subido como imagen se ejecute como script. No tiene contraindicaciones: ponla siempre.

X-Content-Type-Options: nosniff

Referrer-Policy

Controla cuánta información de la página de origen se envía al pulsar un enlace hacia otra web. Con strict-origin-when-cross-origin, a otros dominios solo les llega tu dominio, no la URL completa (que puede llevar parámetros con datos de un pedido o de una búsqueda). Es el valor que recomienda OWASP y el que usan por defecto los navegadores actuales, pero conviene declararlo.

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy

Desactiva funciones del navegador que tu web no usa: cámara, micrófono, geolocalización… Si un script inyectado intenta pedirlas, el navegador se niega. Unos paréntesis vacíos significan «nadie».

Permissions-Policy: camera=(), microphone=(), geolocation=()

Si tu tienda usa un método de pago que funciona con la API de pagos del navegador, o tu web tiene un mapa que pide la ubicación, no desactives esas funciones a ciegas: pruébalo.

Las que sobran

X-XSS-Protection está obsoleta: OWASP recomienda no enviarla o ponerla a 0, porque en navegadores antiguos podía crear fallos nuevos. Y conviene quitar X-Powered-By y las versiones de la cabecera Server, que solo le dicen al atacante qué software usas.

Cómo añadirlas

Apache (.htaccess)

En la mayoría de hostings compartidos con Apache, se añaden al .htaccess de la raíz. Necesita el módulo mod_headers, que casi todos tienen activo:

<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
  Header always set X-Content-Type-Options "nosniff"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header always unset X-Powered-By
</IfModule>

Nginx

En el bloque server del sitio. El always hace que se envíen también en las páginas de error:

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;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

WordPress

Lo mejor es ponerlas en el servidor (.htaccess o Nginx), porque así cubren también imágenes y archivos estáticos. Si no tienes acceso, puedes enviarlas desde PHP con el gancho send_headers, en un plugin propio o en un mu-plugin (no en el functions.php del tema, que se pierde si cambias de tema):

<?php
add_action( 'send_headers', function () {
    header( 'X-Content-Type-Options: nosniff' );
    header( 'Referrer-Policy: strict-origin-when-cross-origin' );
    header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
    header( 'X-Frame-Options: SAMEORIGIN' );
} );

Así solo se aplican a las páginas que genera WordPress, no a los archivos que sirve el servidor directamente. WordPress ya envía X-Frame-Options: SAMEORIGIN por su cuenta en la pantalla de acceso y en el escritorio.

PrestaShop

PrestaShop regenera el .htaccess cuando guardas ciertos ajustes (URL amigables, por ejemplo). Conserva lo que esté fuera del bloque que delimitan sus comentarios # ~~start~~ y # ~~end~~, así que pon tus cabeceras antes de # ~~start~~ o después de # ~~end~~, nunca dentro. Si la tienda va en Nginx, en la configuración del servidor como arriba.

CSP sin romper la web

La forma segura es empezar en modo observación con Content-Security-Policy-Report-Only. El navegador evalúa la política y avisa de lo que habría bloqueado, pero no bloquea nada:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'
  1. Publica la política en modo Report-Only.
  2. Navega por la web con las herramientas de desarrollo abiertas (pestaña Consola): cada recurso que la política habría bloqueado aparece como aviso. Recorre sobre todo el carrito, el pago y los formularios.
  3. Añade a la política los orígenes legítimos (pasarela de pago, analíticas, chat). Si quieres recibir los avisos de los visitantes reales, configura un destino con report-to (y report-uri para navegadores que aún no lo soportan).
  4. Cuando pasen días sin avisos nuevos, cambia el nombre de la cabecera a Content-Security-Policy.

Report-Only no protege por sí sola: solo informa. No te quedes ahí indefinidamente. Y si la web depende de muchos scripts en línea, una CSP con 'unsafe-inline' en script-src protege mucho menos; aun así, frame-ancestors, object-src 'none' y base-uri 'self' aportan desde el primer día.

Cómo verificarlas

En el navegador

Abre tu web, pulsa F12, ve a la pestaña Red (Network), recarga y haz clic en la primera petición (el documento). En Encabezados de respuesta verás todas las cabeceras que envía el servidor.

Con curl

Desde la terminal de Windows, macOS o Linux:

curl -sI https://tu-web.com/

Por ejemplo, esta web responde (entre otras) con:

strict-transport-security: max-age=63072000; includeSubDomains; preload
content-security-policy: default-src 'self'; ... frame-ancestors 'none'; object-src 'none'; upgrade-insecure-requests
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()

Comprueba también una página interior, una imagen y una URL que no exista: es donde suelen faltar.

Con el análisis gratuito

El análisis gratuito revisa las cabeceras de tu portada, la calidad de la CSP (si solo está en Report-Only, si permite scripts de cualquier sitio) y el HTTPS. Si necesitas que alguien las configure y compruebe que no rompen el pago, está incluido en el mantenimiento de WordPress y en la revisión de seguridad de WordPress.

El mismo análisis lee también tu robots.txt y comprueba si publicas un llms.txt, el archivo que resume tu web para los asistentes de IA. Qué es, en qué se diferencia de robots.txt y cómo escribirlo, en la guía de llms.txt.

Fuentes

Preguntas frecuentes

¿Qué son las cabeceras de seguridad HTTP?

Son líneas que el servidor envía con cada respuesta para indicar al navegador cómo comportarse: usar solo HTTPS, qué scripts puede cargar, si la página puede mostrarse dentro de un marco o qué funciones del navegador están permitidas.

¿Cómo veo las cabeceras de seguridad de una web?

En el navegador, con F12 → Red → la primera petición → Encabezados de respuesta. Desde la terminal, con curl -sI https://tu-web.com/. El análisis gratuito de WebIntacta también las revisa.

¿Puede una CSP romper mi web?

Sí, si bloquea un script que necesitas, como el de la pasarela de pago. Por eso se empieza con Content-Security-Policy-Report-Only, que solo avisa, y se pasa a la versión que bloquea cuando ya no aparecen avisos.

¿X-Frame-Options sigue siendo necesaria?

La directiva frame-ancestors de la CSP la sustituye en los navegadores actuales, pero X-Frame-Options sigue sirviendo para los antiguos y no estorba tener las dos.

¿Cómo añado cabeceras de seguridad en WordPress?

Lo mejor es hacerlo en el servidor, en el .htaccess (Apache) o en la configuración de Nginx. Si no tienes acceso, un pequeño plugin con el gancho send_headers puede enviarlas desde PHP.

¿Está intacta tu web? Compruébalo ahora.

Análisis externo gratuito. Sin registro y sin instalar nada.

Recibir el resultado en PDF y avisos 30 días (opcional)

Protección de datos. Responsable: Jose Soriano Castro. Finalidad: enviarte el PDF del diagnóstico, avisarte por email durante 30 días si se publica un fallo de seguridad que afecte a tu web (con un análisis de repaso el día 30) y responder por escrito a tu consulta; las ofertas, solo si marcas esa casilla. Legitimación: tu consentimiento. Destinatarios: nadie, salvo obligación legal. Puedes darte de baja cuando quieras con el enlace de cada correo o escribiéndome. Derechos: acceso, rectificación, supresión, oposición, limitación y portabilidad. Política de privacidad.

  • Sin acceso FTP
  • Solo lectura
  • Sin datos de clientes

Preferencias de cookies