James Galley

Web application developer

PHP, Yii, MySQL, Node.js - JavaScript, HTML, CSS & SASS - Linux, AWS & serverless

X-Frame-Options HTTP header

The X-Frame-Options HTTP header is one of the HTTP security headers available to enhance the security of a website or web application. Although it is made obsolete by the frame-ancestors directive available in the Content-Security-Policy header, browsers still support X-Frame-Options and it can be used to manage or prevent the framing of your sites' content in other websites.

X-Frame-Options is a response header generated by a server in response to an HTTP request, and it can be used to prevent rendering of the page in <frame>, <iframe>, <embed>, <object> HTML elements.

Like all the HTTP security headers, this header must be set in an HTTP response by your web server or content delivery mechanism. That means using a server-side scripting language like PHP or Node.js or a web server framework like Apache or Nginx. When working with AWS CloudFront, perhaps for static sites deployed to S3 or apps running through API Gateway, it's possible to use Lambda@Edge or CloudFront functions to add headers into the viewer response.

Sites can use the X-Frame-Options header to ensure that their content is not embedded and rendered on other sites, helping to prevent click-jacking attacks and malicious rendering of your content across the web.

Directives

X-Frame-Options has only two possible values which are simple string values:

X-Frame-Options: DENY

DENY indicates to the browser that the page cannot be displayed in a frame in any location, even framed with the same site and domain.

X-Frame-Options: SAMEORIGIN

SAMEORIGIN indicates to the browser that the page can be framed within the same host origin. An example use-case for this directive is when using Yii 2's debugger for local development, as this module loads a framed window originating from the same host, and will be blocked if the DENY directive is used.

There is a deprecated directive of ALLOW-FROM=url which previously offered the ability to list permitted domains, however this is now unsupported and you should use the frame-ancestors directive available in the Content-Security-Policy header for this purpose.

How to set X-Frame-Options header in Apache

The h5bp/server-configs-apache library is a great reference for this (this version from v6.0.0, 5th December 2022):

<IfModule mod_headers.c>
    Header always set X-Frame-Options "DENY" "expr=%{CONTENT_TYPE} =~ m#text/html#i"
</IfModule>

How to set X-Frame-Options in PHP

<?php
header('X-Frame-Options: DENY');
?>

For Yii2, headers can be added site-wide by modifying the response object in the app config (thanks to this SO post):

'components' => [
    'response' => [
        'on beforeSend' => function($event) {
            $event->sender->headers->add('X-Frame-Options', 'DENY');
        },
    ],

Note that Apache/.htaccess settings of a header will overwrite any value set in PHP.

How to set X-Frame-Options in Node.js / Remix.run

In the Remix JavaScript framework response headers are set in the handleRequest function of the server entry script, typically app/entry.server.jsx.

import { RemixServer } from "@remix-run/react";
import { renderToString } from "react-dom/server";

export default function handleRequest(
  request,
  responseStatusCode,
  responseHeaders,
  remixContext
) {
  let markup = renderToString(
    <RemixServer context={remixContext} url={request.url} />
  );
  responseHeaders.set("Content-Type", "text/html");

  // Set X-Frame-Options Header
  responseHeaders.set("X-Frame-Options", "SAMEORIGIN");

  return new Response("<!DOCTYPE html>" + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

See more information on Remix entry scripts here: https://remix.run/docs/en/v1/guides/migrating-react-router-app#creating-server-and-browser-entrypoints