X-Frame-Options es una cabecera de respuesta HTTP que indica si una página puede mostrarse dentro de un frame o iframe. Su objetivo principal es reducir el riesgo de clickjacking, un ataque que intenta engañar al usuario para que haga clic sobre una página legítima presentada de manera invisible o manipulada dentro de otro sitio.
Actualmente, la directiva frame-ancestors de Content Security Policy permite un control más flexible. X-Frame-Options continúa siendo útil como compatibilidad y defensa adicional, siempre que ambas políticas sean coherentes.
Cómo funciona un ataque de clickjacking
Un atacante carga una página legítima dentro de un iframe y coloca encima elementos visuales diseñados para ocultarla o confundir al visitante. La persona cree pulsar un botón inocente, pero en realidad interactúa con una función del sitio legítimo donde quizá mantiene una sesión iniciada.
El riesgo es mayor en páginas que cambian contraseñas, autorizan pagos, modifican datos o ejecutan acciones administrativas. Evitar que esas respuestas sean incluidas por sitios no autorizados elimina una parte fundamental del ataque.
Valores válidos de X-Frame-Options
- DENY: impide que la página sea mostrada dentro de cualquier frame, incluso desde el mismo sitio.
- SAMEORIGIN: permite que sea enmarcada únicamente por páginas del mismo origen.
El valor ALLOW-FROM está obsoleto y no ofrece una protección confiable en los navegadores modernos. Si necesitás autorizar orígenes específicos, utilizá CSP con frame-ancestors.
DENY o SAMEORIGIN: cuál elegir
Usá DENY cuando ninguna función del sitio necesite mostrar sus propias páginas dentro de un iframe. Elegí SAMEORIGIN si la aplicación utiliza frames internos legítimos desde el mismo esquema, dominio y puerto.
Antes de decidir, revisá paneles, editores visuales, vistas previas y sistemas que integren páginas propias. No adoptes SAMEORIGIN solamente por precaución: si el framing no es necesario, DENY establece una política más clara.
Configurar X-Frame-Options en Apache
Cuando el servidor tiene habilitado mod_headers, puede agregarse la cabecera desde el archivo .htaccess. Para permitir frames únicamente desde el mismo origen:
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>
Para impedir completamente que las páginas sean enmarcadas:
<IfModule mod_headers.c>
Header always set X-Frame-Options "DENY"
</IfModule>
La opción always ayuda a enviar la cabecera también en respuestas con otros códigos de estado. En un hosting administrado, una configuración del servidor, plugin de seguridad o CDN puede agregarla previamente; verificá el resultado antes de crear otra regla.
Utilizar CSP frame-ancestors
Para impedir todos los frames mediante Content Security Policy:
Content-Security-Policy: frame-ancestors 'none';
Para permitir solamente el mismo origen:
Content-Security-Policy: frame-ancestors 'self';
CSP también permite declarar orígenes concretos cuando una plataforma externa necesita embeber la página. Cada origen debe incluirse explícitamente y con el esquema adecuado. No utilices comodines amplios sólo para resolver una integración puntual.
Agregar frame-ancestors desde .htaccess
Si el sitio todavía no envía una cabecera CSP, un ejemplo básico para permitir el mismo origen es:
<IfModule mod_headers.c>
Header always set Content-Security-Policy "frame-ancestors 'self';"
</IfModule>
Importante: si ya existe una política Content-Security-Policy con directivas para scripts, imágenes u otros recursos, no la reemplaces con este ejemplo. Agregá frame-ancestors dentro de la política completa existente. Varias capas del hosting podrían modificar cabeceras, por lo que siempre hay que comprobar la respuesta final.
No funciona mediante una etiqueta meta
X-Frame-Options debe enviarse como cabecera HTTP. Una etiqueta como <meta http-equiv="X-Frame-Options"> no aplica la protección. La directiva CSP frame-ancestors también debe formar parte de la cabecera HTTP y no de una etiqueta meta.
Esto significa que editar solamente el HTML de una página no es suficiente. La configuración debe realizarse en Apache, NGINX, el framework, el CMS, un plugin apropiado o el servicio que genera la respuesta.
Combinar X-Frame-Options y CSP
Para una política sin framing, una combinación habitual es:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none';
Para permitir únicamente el mismo origen:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self';
Los navegadores modernos que aplican frame-ancestors utilizan esa directiva; X-Frame-Options ayuda con compatibilidad. No configures políticas contradictorias, porque el resultado puede variar entre navegadores antiguos y actuales.
Embeber contenido externo no es lo mismo
Estas cabeceras controlan quién puede mostrar tus páginas dentro de un frame. No impiden que tu sitio incluya un video, mapa o formulario externo mediante iframe. Esa carga se controla con otras directivas CSP, como frame-src, y con las políticas del proveedor externo.
Por ejemplo, que una página muestre un video de YouTube no significa que todo el sitio deba permitir ser enmarcado por terceros. Son dos direcciones y decisiones distintas.
Integraciones que requieren atención
Algunos editores, pasarelas de pago, áreas de cliente, widgets y plataformas de soporte pueden utilizar iframes. Identificá cuál página se enmarca y desde qué origen. Si sólo una ruta necesita esa función, aplicá la excepción con el menor alcance posible en lugar de relajar todo el dominio.
No permitas un origen basándote únicamente en que “la integración dejó de funcionar”. Confirmá en la documentación oficial qué dominio carga el frame y probá que la política no autorice variantes innecesarias.
Cómo verificar las cabeceras
Desde una terminal podés consultar las cabeceras de la respuesta:
curl -I https://www.example.com/
Buscá las líneas X-Frame-Options y Content-Security-Policy. También podés abrir las herramientas de desarrollador del navegador, ingresar en la pestaña de red, recargar la página y revisar las cabeceras de respuesta del documento principal.
Probá una página normal, una ruta administrativa y cualquier integración que utilice iframe. Una redirección puede mostrar cabeceras diferentes de la respuesta final, por lo que hay que revisar ambas.
Problemas frecuentes al implementarlas
- Agregar la cabecera dos veces con valores distintos.
- Reemplazar accidentalmente una CSP completa por una directiva aislada.
- Usar el valor obsoleto ALLOW-FROM.
- Intentar configurarla mediante una etiqueta meta.
- Modificar .htaccess cuando el servidor o CDN sobrescribe la cabecera.
- No probar editores, pagos o paneles que realmente necesitan framing.
Una capa dentro de una estrategia más amplia
Prevenir clickjacking no reemplaza HTTPS, control de acceso, protección CSRF, sesiones seguras ni actualizaciones. El atributo SameSite de las cookies puede reducir determinados escenarios, pero tampoco sustituye las cabeceras que impiden el framing.
Combiná una política de framing coherente con las demás capas explicadas en seguridad en servidores de hosting. Aplicá primero la opción más restrictiva compatible con el sitio, verificá la respuesta HTTP final y documentá cualquier excepción necesaria.