Skip to main content

What is X-Frame-Options?

The oldest and simplest defense against clickjacking — and how the CSP successor frame-ancestors replaces it.

What is clickjacking?

In a clickjacking attack, an attacker embeds your website inside an invisible iframe on their own page and overlays bait buttons. The user thinks they are clicking "Join for free" — but in reality the click goes through the invisible frame onto, for example, "Delete account" or "Transfer money" on your website where they are currently logged in. Classic victims: banking portals, admin panels, social media sites with like/follow buttons.

How X-Frame-Options works

The HTTP header tells the browser whether your page may be loaded inside an <iframe>, <frame> or <object>. Three values are possible:

  • DENY — the page may not be embedded at all, not even by your own domain. Strictest option.
  • SAMEORIGIN — embedding only by pages from the same domain. Useful for internal admin iframes.
  • ALLOW-FROM uri — allows one specific domain. Deprecated, no longer supported by Chrome and Safari.

Configuration examples

nginx:

add_header X-Frame-Options "DENY" always;

Apache:

Header always set X-Frame-Options "SAMEORIGIN"

Express (Node.js):

app.use(helmet.frameguard({ action: 'deny' }))

Flask (Python):

@app.after_request
def set_frame_options(resp):
    resp.headers['X-Frame-Options'] = 'DENY'
    return resp

frame-ancestors — the modern successor

The Content Security Policy has its own directive for the same problem: frame-ancestors. It is more powerful and supported by all modern browsers. Advantages over X-Frame-Options:

  • Multiple allowed sources possible — not just one.
  • Wildcards and subdomain matching: *.partner.com.
  • Clean integration into your overall CSP strategy.

Example:

Content-Security-Policy: frame-ancestors 'none';
Content-Security-Policy: frame-ancestors 'self' https://partner.com;

Both headers at the same time?

Recommendation: set both. X-Frame-Options for older browsers that don't understand CSP frame-ancestors; CSP frame-ancestors for modern browsers. If both are set, CSP wins. That way you're covered on both sides.

What if you need legitimate embeds?

If third-party sites really should embed your website — for example embedded booking widgets, login buttons or comment widgets — don't enable X-Frame-Options at all and use frame-ancestors with a precise whitelist of allowed domains. Never simply use *.

Check it yourself: HTTP-Header

HSTS, CSP, X-Frame-Options & Co. Enter a domain and see in seconds how your X-FRAME-OPTIONS is doing.

Also in the glossary