Las cabeceras de seguridad HTTP son unas pocas líneas que se envían con cada página. No reparan un plugin vulnerable, pero piden al navegador de tus visitantes que rechace ciertos comportamientos peligrosos: cargar en HTTP, mostrarse dentro de un marco trampa o ejecutar un script llegado de otro sitio. Este artículo explica qué hace cada cabecera, qué envía ya WordPress, unos valores de partida razonables y el método para probarlas sin romper la administración.
Definición rápida: Cabeceras de seguridad HTTP. Solución lista para usar: Cabeceras de seguridad para WordPress con WP Masquerade.
Qué protegen estas cabeceras y qué no
Una cabecera de seguridad es una instrucción: el servidor la envía y el navegador la aplica. Por tanto, la protección recae sobre el visitante y sobre la forma en que se muestran tus páginas. Un bot que ataca directamente wp-login.php o un plugin ignora por completo estas instrucciones.
- Limitan el impacto de una vulnerabilidad XSS, de un secuestro de clics (clickjacking) o de una interceptación en una wifi pública.
- No bloquean una inyección SQL, la subida de un archivo malicioso ni una escalada de privilegios en un plugin: solo la actualización corrige esos fallos.
- Se suman a las demás capas (actualizaciones, autenticación en dos pasos, copias de seguridad), sin sustituirlas.

Lo que WordPress ya envía
WordPress añade algunas cabeceras por su cuenta, pero solo en determinadas respuestas. Conocerlas evita duplicarlas con valores contradictorios.
| Respuesta | Cabecera enviada por WordPress | Desde |
|---|---|---|
Administración (admin_init) y página de acceso (login_init) | X-Frame-Options: SAMEORIGIN | WordPress 3.1.3 |
| Administración y página de acceso | Content-Security-Policy: frame-ancestors 'self'; | WordPress 6.9 |
| Administración y página de acceso | Referrer-Policy: strict-origin-when-cross-origin | WordPress 4.9 (valor actual desde la 4.9.5) |
API REST y admin-ajax.php | X-Content-Type-Options: nosniff | Presente en el código actual |
| Páginas públicas del sitio | Ninguna de estas cabeceras por defecto |
Dicho de otro modo, son tus páginas públicas las que quedan al descubierto. Ahí es donde hay que actuar.
Las seis cabeceras que debes conocer, con valores de partida
| Cabecera | Función | Valor de partida | Riesgo de romper algo |
|---|---|---|---|
Strict-Transport-Security | Impone HTTPS en las visitas siguientes | max-age=31536000 tras un breve periodo de prueba | Bajo si todo el sitio está en HTTPS, alto si algún subdominio no lo está |
X-Content-Type-Options | Impide que el navegador adivine el tipo de un archivo | nosniff | Muy bajo |
X-Frame-Options | Impide que otro sitio muestre el tuyo dentro de un marco | SAMEORIGIN | Bajo; DENY rompería la vista previa del personalizador |
Referrer-Policy | Limita la URL que se transmite a los sitios enlazados | strict-origin-when-cross-origin | Muy bajo |
Permissions-Policy | Desactiva las API sensibles del navegador | camera=(), microphone=(), geolocation=() | Medio si un plugin usa la geolocalización o la cámara |
Content-Security-Policy | Enumera los orígenes autorizados (scripts, estilos, marcos…) | Primero en modo solo informe | Alto: hay que construirla sitio por sitio |
HSTS: potente, pero difícil de revertir
Strict-Transport-Security indica al navegador que use solo HTTPS durante max-age segundos. El navegador ignora esta cabecera si llega por HTTP, y la primerísima visita sigue expuesta mientras no la haya recibido nunca. Para desactivar HSTS hay que enviar max-age=0 por HTTPS: imposible si tu certificado ha caducado entretanto.

La lista de precarga hstspreload.org elimina ese punto débil, con condiciones estrictas: certificado válido, redirección de HTTP a HTTPS en el mismo host, todos los subdominios en HTTPS y una cabecera con max-age de al menos 31536000 segundos (un año), includeSubDomains y preload. El sitio advierte de que la inscripción no se deshace fácilmente y de que la retirada tarda meses en llegar a los usuarios. Solicita la precarga solo si todos tus subdominios (correo web, preproducción, antigua extranet) van a estar en HTTPS durante mucho tiempo.
X-Frame-Options y frame-ancestors
Ambas impiden que otro sitio muestre el tuyo en un marco invisible para hacerte hacer clic sin que lo sepas. OWASP recomienda la directiva CSP frame-ancestors, que sustituye a X-Frame-Options en los navegadores que la admiten. Enviar las dos con SAMEORIGIN y 'self' reproduce lo que WordPress ya hace en la administración desde la versión 6.9.
Content-Security-Policy: empezar en modo informe
La CSP es la cabecera más eficaz contra los scripts inyectados y la más delicada en WordPress: el núcleo, los temas y muchos plugins escriben scripts en línea. Una política estricta copiada de un tutorial suele bloquear el editor, los formularios o las estadísticas. El método correcto:
- Envía
Content-Security-Policy-Report-Only: el navegador informa de las infracciones sin bloquear nada. - Abre la consola de las herramientas de desarrollo en la página de inicio, una entrada, el carrito y el editor, y anota cada origen bloqueado.
- Para recoger los informes a distancia, añade
report-toy la cabeceraReporting-Endpoints: sin destino, no se envía ningún informe. - Pasa a
Content-Security-Policysolo cuando la consola quede limpia en todos los recorridos importantes.
Desde WordPress 5.7, las funciones wp_get_script_tag(), wp_get_inline_script_tag() y el filtro wp_script_attributes permiten añadir un atributo nonce a los scripts cargados correctamente. Los scripts escritos directamente en el HTML por un tema o un plugin no se benefician de ello: por eso muchas políticas para WordPress conservan 'unsafe-inline' al principio, lo que debilita la protección contra XSS.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'
Este punto de partida es permisivo a propósito: sirve para medir, no para proteger. Después ve ajustándolo origen por origen (dominio de las fuentes tipográficas, herramienta de estadísticas, pasarela de pago).
Las cabeceras que ya no hay que añadir
Muchas guías antiguas y muchos plugins siguen proponiendo cabeceras abandonadas. Añadirlas ya no protege nada, y una de ellas incluso puede ser perjudicial.
| Cabecera | Estado actual | Qué hacer |
|---|---|---|
X-XSS-Protection | Obsoleta y no estándar (MDN). Puede crear vulnerabilidades XSS en sitios sanos. | No enviarla, o enviarla con valor 0. Usar una CSP. |
Expect-CT | Desaconsejada por OWASP, que recomienda quitarla. | Eliminarla. |
Feature-Policy | Sustituida por Permissions-Policy, con una sintaxis distinta. | Convertirla, por ejemplo camera=(). |
X-Powered-By y Server detallada | No son cabeceras de seguridad: anuncian la versión de PHP o del servidor. | Quitarlas (expose_php = Off en PHP) o neutralizarlas. |
Dónde configurarlas: servidor, PHP o plugin
La elección depende de tu alojamiento. La regla general: cuanto antes se aplique la instrucción, más respuestas cubre.
- En el servidor (
.htaccessde Apache o LiteSpeed, configuración de Nginx): cubre también los archivos estáticos, los errores y las páginas que sirve una caché de páginas sin pasar por PHP. - En PHP (acción
send_headers): práctico en un alojamiento sin acceso a la configuración, pero no aparece en las respuestas que no cargan WordPress, como una página servida directamente por una caché. - Con un plugin: la misma limitación que PHP, con una interfaz. Comprueba que no añade las cabeceras obsoletas vistas más arriba.
Apache o LiteSpeed (.htaccess)
Con mod_headers, la condición always añade la cabecera también en las respuestas de error y las redirecciones, cosa que no hace la condición por defecto.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000"
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=()"
</IfModule>
Nginx
Sin el parámetro always (disponible desde Nginx 1.7.5), add_header solo se aplica a los códigos 200, 201, 204, 206, 301, 302, 303, 304, 307 y 308. Otra trampa: un bloque location que define su propio add_header deja de heredar las cabeceras del nivel server. Nginx 1.29.3 añadió add_header_inherit merge; para cambiar este comportamiento.
add_header Strict-Transport-Security "max-age=31536000" 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;
En PHP, en un plugin propio
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=()' );
} );
No añadas HSTS en PHP si tu proveedor de alojamiento o tu CDN ya lo envía: dos valores distintos complican el diagnóstico.
Probar sin romper el sitio
- En la línea de comandos:
curl -I https://tu-sitio.es/muestra las cabeceras que realmente se reciben. Prueba también una página 404, una imagen dewp-content/uploadsywp-login.php. - En el navegador: pestaña Red de las herramientas de desarrollo y, después, la respuesta del documento principal.
- Con el HTTP Observatory de Mozilla, integrado en MDN desde julio de 2024, que puntúa la configuración y explica cada punto.
- Recorre después las funciones sensibles: editor de bloques, personalizador, formularios, pago, vídeos incrustados.
- Vacía todas las cachés (plugin, alojamiento, CDN) antes de cada prueba: una página en caché puede conservar las cabeceras antiguas.
Con WP Masquerade
El plugin gratuito ya añade X-Content-Type-Options: nosniff desde el nivel 1 del módulo de bastionado. WP Masquerade Pro gestiona las cabeceras de seguridad desde la administración (HSTS, X-Frame-Options, Referrer-Policy, CSP…), sin modificar los archivos del servidor. Como con cualquier solución en PHP, una página servida por una caché previa puede no llevar estas cabeceras: comprueba el resultado con curl y, si hace falta, consulta la página de compatibilidad con alojamientos y cachés. La comparativa detalla la frontera entre la versión gratuita y la Pro.
Una plataforma de consentimiento de cookies alojada carga su script desde su propio dominio, que entonces hay que autorizar en la CSP; nuestra comparativa de plugins de cookies indica qué soluciones se quedan en su servidor.
Lo esencial
- Las cabeceras de seguridad protegen a tus visitantes, no tu código: complementan las actualizaciones sin sustituirlas.
- WordPress ya protege la administración y el acceso frente a la visualización en marcos; tus páginas públicas no tienen nada por defecto.
- Despliega primero nosniff, SAMEORIGIN, Referrer-Policy y Permissions-Policy, que rara vez rompen algo.
- Sube HSTS por etapas y solicita la precarga solo si todos los subdominios están en HTTPS.
- Construye la CSP en modo solo informe, página a página, antes de aplicarla.
- Deja de enviar X-XSS-Protection, Expect-CT y Feature-Policy.
Fuentes
- MDN: Strict-Transport-Security (se abre en una pestaña nueva)
- HSTS Preload List Submission (requisitos de inscripción) (se abre en una pestaña nueva)
- MDN: X-XSS-Protection (se abre en una pestaña nueva)
- OWASP HTTP Headers Cheat Sheet (se abre en una pestaña nueva)
Otras fuentes consultadas: documentación oficial.


