X-Frame-Options header and clickjacking

Controls whether other sites may show your page inside a frame, which is how clickjacking attacks work.

Updated 3 November 2026

Check your site's X-Frame-Options header

We send one normal request (following redirects) and grade the X-Frame-Options header on the final response.

Recommended value

X-Frame-Options: DENY
Value / directiveWhat it does
DENY The page cannot be framed by anyone, including your own site.
SAMEORIGIN Only pages on the same origin may frame it.
ALLOW-FROM uri Obsolete. Current browsers ignore it, which leaves the page frameable. Use CSP frame-ancestors instead.

What is clickjacking?

Clickjacking loads your page in an invisible iframe on top of an attacker's page. The victim thinks they are clicking "Play video" but actually clicks "Delete account", "Transfer" or "Grant access" on your site, with their own logged-in session. Because the click is real, CSRF tokens do not help.

X-Frame-Options: DENY tells the browser to refuse to render your page inside a frame at all. SAMEORIGIN allows your own site to frame it, which you need if you embed your own pages (for example an admin preview).

X-Frame-Options vs CSP frame-ancestors

CSP's frame-ancestors directive is the modern replacement. It can list several allowed origins, which X-Frame-Options cannot:

Content-Security-Policy: frame-ancestors 'self' https://partner.example

When a response has both, modern browsers obey frame-ancestors and ignore X-Frame-Options. Sending both is still sensible: old browsers only understand X-Frame-Options. Remember that frame-ancestors does not work in a <meta> tag, and neither does X-Frame-Options.

When you do need framing

If you build an embeddable widget, do not drop protection everywhere. Serve the embeddable route without the header (or with a frame-ancestors allowlist of customer origins) and keep DENY on login, settings and payment pages.

How to set X-Frame-Options

nginx

# inside your server { } block
add_header X-Frame-Options "DENY" always;

Apache

# .htaccess or virtual host (needs mod_headers)
Header always set X-Frame-Options "DENY"

Laravel middleware

// app/Http/Middleware/SecurityHeaders.php
public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);
    $response->headers->set('X-Frame-Options', 'DENY');

    return $response;
}

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})

Express (Node.js)

app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  next();
});

// or: app.use(require('helmet')()) for a sensible default set

Cloudflare

Rules → Transform Rules → Modify Response Header → Create rule.
Match: all incoming requests (or your hostname).
Then: Set static → Header name: X-Frame-Options → Value: DENY

In nginx, an add_header inside a location block discards every add_header inherited from the server block. Repeat your security headers there, or use an include file. The always flag also sends the header on error responses.

Common mistakes

  • Using ALLOW-FROM. Chrome and Safari never supported it and Firefox removed it, so the page is frameable everywhere.
  • Setting the header in an HTML <meta> tag. Browsers ignore X-Frame-Options in meta tags.
  • Protecting the home page but not the pages with dangerous buttons (account deletion, OAuth consent, payments).
  • Sending DENY and SAMEORIGIN from two different layers. Browsers may treat the conflicting header as invalid.

FAQ

Should I use DENY or SAMEORIGIN?

Use DENY unless your own site frames its pages. SAMEORIGIN is fine when you need same-site previews or embeds.

Is X-Frame-Options deprecated?

It is superseded by CSP frame-ancestors, but still worth sending for older browsers. Browsers that support frame-ancestors use it first.

Can I allow one other site to frame my page?

Not with X-Frame-Options (ALLOW-FROM no longer works). Use Content-Security-Policy: frame-ancestors with the allowed origin.

Headers are one of 30+ things attackers check

The free StackShield scan also checks for exposed .env files, debug mode, leaked tooling, weak TLS and more, from the outside, in about a minute.

Run a free scan