← Blog  ·  October 2026

X-Frame-Options: How to Check and Monitor Clickjacking Protection

What X-Frame-Options does, how to verify it's set correctly, how it compares to CSP frame-ancestors, and how to monitor so a deployment never silently removes it.

Clickjacking is a UI deception attack where an attacker loads your page invisibly inside an iframe and tricks users into clicking buttons they can't see — transferring funds, changing account settings, or authorizing actions without knowing it. X-Frame-Options is one of the oldest and most widely supported defenses against this attack.

It's also one of the most common headers to disappear silently after a deployment. This guide covers how to check whether you have it, what each value means, and how to catch regressions before they matter.


What X-Frame-Options does

The X-Frame-Options HTTP response header controls whether a browser is allowed to embed your page in a <frame>, <iframe>, <embed>, or <object>. When the header is set, any attempt to load your page inside a frame from an unauthorized origin causes the browser to refuse to render it.

There are three valid values:

ValueEffectRecommendation
DENY Page cannot be framed by anyone — not even pages on the same origin Best for most sites
SAMEORIGIN Page can only be framed by pages on the same origin (same protocol + host + port) OK if you use iframes internally
ALLOW-FROM origin Page can be framed only by the specified origin Deprecated — use CSP instead

For most web applications, X-Frame-Options: DENY is the right choice. If your app legitimately embeds its own pages in iframes (e.g. preview panels, widget builders), use SAMEORIGIN.

⚠️ Missing X-Frame-Options = clickjacking risk If your site handles authenticated actions (form submissions, payments, account changes), a missing or misconfigured X-Frame-Options header makes clickjacking technically feasible. Modern browsers also warn about framed pages without this header in developer tools.

How to check your X-Frame-Options header

Method 1: Free security headers checker tool

HTTP Security Headers Checker

Easy Setup: 0 min No install needed

The fastest way is to use the free HTTP Security Headers Checker. Enter your domain and click Check — you'll see all security headers in one view, including whether X-Frame-Options is present, what value it has, and whether it's configured correctly.

The checker also shows:

Method 2: curl — command-line check

curl -I

Easy Works on: macOS, Linux, Windows (WSL)

Check response headers directly:

curl -sI https://yoursite.com | grep -i "x-frame\|x-content\|strict-transport"

Expected output when properly configured:

x-frame-options: DENY
x-content-type-options: nosniff
strict-transport-security: max-age=31536000; includeSubDomains

If X-Frame-Options is absent from the output, it's not set. Note that some frameworks set this header only on HTML responses (not on API endpoints or static assets) — check the page that handles authenticated actions specifically.

To check a specific path:

curl -sI https://yoursite.com/dashboard | grep -i "x-frame"

Method 3: Browser developer tools

Chrome / Firefox DevTools

Easy Best for: checking a specific page you're viewing
  1. Open the page you want to check
  2. Open DevTools (F12 or Cmd+Option+I)
  3. Go to the Network tab
  4. Refresh the page
  5. Click the HTML document request (first item in the list)
  6. Click Response Headers
  7. Look for x-frame-options

Chrome also flags pages without framing protection in the Security panel — go to Security → click the page origin → check the headers section.


X-Frame-Options vs. CSP frame-ancestors

The Content-Security-Policy header with a frame-ancestors directive is the modern replacement for X-Frame-Options. It offers more flexibility (multiple allowed origins) and is supported in all modern browsers.

FeatureX-Frame-OptionsCSP frame-ancestors
Browser support Excellent (IE 8+) Very good (IE not supported)
Multiple allowed origins No Yes
Syntax X-Frame-Options: DENY Content-Security-Policy: frame-ancestors 'none'
Overrides X-Frame-Options? — Yes, when both are present
Recommendation Keep for legacy browsers Primary protection in modern stacks

Best practice: Set both. Use X-Frame-Options: DENY for older browser coverage and Content-Security-Policy: frame-ancestors 'none' for modern browsers. When CSP frame-ancestors is present, it takes precedence in supporting browsers, so there's no conflict.

Nginx configuration

server {
    # ...
    add_header X-Frame-Options "DENY" always;
    add_header Content-Security-Policy "frame-ancestors 'none'" always;
}

Apache configuration

Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none'"

Node.js / Express with Helmet

const helmet = require('helmet');

app.use(helmet({
  frameguard: { action: 'deny' },    // sets X-Frame-Options: DENY
  contentSecurityPolicy: {
    directives: {
      frameAncestors: ["'none'"],    // CSP frame-ancestors: 'none'
    },
  },
}));

Next.js (next.config.js)

const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Frame-Options', value: 'DENY' },
          {
            key: 'Content-Security-Policy',
            value: "frame-ancestors 'none'",
          },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

Common X-Frame-Options issues

Header set on some pages but not others

A common pattern: security headers are set in the CDN or reverse proxy config, but API responses or dynamically routed pages bypass the proxy. Check your most sensitive pages explicitly — the login page, checkout, and any page with form submissions — not just the homepage.

Header silently removed after a deploy

Framework version upgrades, CDN config changes, or proxy rule changes can remove security headers without warning. Helmet configuration in Express, for example, changes defaults across major versions. Without monitoring, this goes undetected until a security audit.

ALLOW-FROM doesn't work in modern browsers

The ALLOW-FROM value was never supported in Chrome or Firefox and has been removed from the spec. If you need to allow specific origins to embed your page, use Content-Security-Policy: frame-ancestors https://trusted-origin.com instead.

Reverse proxy strips the header

Cloudflare, AWS CloudFront, and other CDNs can be configured to strip or override response headers. Check both your origin server response and what the CDN delivers to the browser — they can differ.


Monitoring for header regressions

Setting a header once is not enough. Security headers disappear more often than teams realize — during Nginx config refactors, Helmet upgrades, CDN rule changes, or new service deployments. A regression that removes X-Frame-Options can go unnoticed for months.

The right approach is to monitor continuously and get an alert when any header changes or disappears. HeaderGuard checks your security headers automatically and sends an email alert when any header changes — including X-Frame-Options, Content-Security-Policy, Strict-Transport-Security, and others.

You can also build a basic header regression check into your CI pipeline:

#!/bin/bash
# ci-header-check.sh — fail CI if X-Frame-Options is missing

URL="https://yoursite.com"
EXPECTED="deny"  # lowercase — header values are case-insensitive

actual=$(curl -sI "$URL" | grep -i "x-frame-options" | awk -F': ' '{print $2}' | tr -d '\r\n' | tr '[:upper:]' '[:lower:]')

if [[ "$actual" != "$EXPECTED" && "$actual" != "sameorigin" ]]; then
  echo "FAIL: X-Frame-Options is '$actual' (expected '$EXPECTED' or 'sameorigin')"
  exit 1
else
  echo "OK: X-Frame-Options is '$actual'"
fi

Add this to your CI pipeline to catch header regressions before they reach production. For continuous monitoring outside CI, use an automated service so you're alerted even when no deployment is happening — a CDN change can remove headers at any time without triggering a build.


🛡️ Check your security headers now

See your X-Frame-Options, CSP, HSTS, and all security headers in one free check. Then set up monitoring to get alerted when they change.

Check Security Headers Free → Monitor for Regressions →