How to Fix a Missing X-Frame-Options Header (Clickjacking Risk)
A missing X-Frame-Options header lets attackers embed your site invisibly inside an iframe and trick your users into clicking things they didn’t mean to. Here’s what that attack looks like and exactly how to stop it.
What is Clickjacking?
Clickjacking (also called a “UI redress attack”) is when an attacker overlays your site — invisible or disguised — inside an <iframe>on their malicious page. The user thinks they’re interacting with something harmless, but their clicks actually land on your real site underneath.
Common clickjacking scenarios include:
- ▶Tricking users into clicking “Confirm” on a bank transfer or account deletion buried in an invisible iframe.
- ▶Manipulating users into granting OAuth permissions or enabling account settings they didn’t intend to change.
- ▶Harvesting credentials when a password manager auto-fills a login form framed invisibly on a malicious page.
The defense is simple: tell the browser that your page is never allowed to be loaded inside an iframe (or restrict it to specific trusted origins).
What is X-Frame-Options?
X-Frame-Options is an HTTP response header that controls whether a browser is allowed to render a page inside an <iframe>, <frame>, or <embed>. It has three values:
DENYThe page cannot be displayed in any iframe, regardless of origin. Recommended for most sites.
SAMEORIGINThe page can only be framed by pages on the same origin (same scheme, host, and port). Use this if your own site legitimately embeds pages within iframes.
ALLOW-FROM uriDeprecated — only allows a specific URI but is not supported in Chrome/Firefox. Use CSP frame-ancestors instead.
The Modern Alternative: CSP frame-ancestors
X-Frame-Options is widely supported but technically superseded by the frame-ancestorsdirective in Content-Security-Policy. They do the same job, but CSP’s version is more flexible and supports multiple trusted origins:
# Equivalent to X-Frame-Options: DENY Content-Security-Policy: frame-ancestors 'none'; # Equivalent to X-Frame-Options: SAMEORIGIN Content-Security-Policy: frame-ancestors 'self'; # Allow framing only from a specific trusted domain Content-Security-Policy: frame-ancestors https://partner.example.com;
Best practice: Set both X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none' for maximum browser compatibility.
How to Fix X-Frame-Options on Nginx
Add both headers in your HTTPS server block:
server {
listen 443 ssl;
server_name example.com;
# Prevent clickjacking
add_header X-Frame-Options "DENY" always;
# Modern equivalent (add to your existing CSP or as standalone)
add_header Content-Security-Policy "frame-ancestors 'none';" always;
}If you have an existing CSP header, append frame-ancestors 'none'; to that policy string rather than adding a second separate CSP header. After editing, test with sudo nginx -t then reload: sudo nginx -s reload.
How to Fix X-Frame-Options on Apache
Ensure mod_headers is enabled (sudo a2enmod headers), then add to your VirtualHost:
<VirtualHost *:443>
ServerName example.com
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none';"
</VirtualHost>After editing, restart or reload Apache: sudo systemctl reload apache2.
How to Fix X-Frame-Options on Cloudflare
Use Cloudflare Transform Rules to inject the header at the edge:
- In the Cloudflare dashboard, go to Rules → Transform Rules → Modify Response Header.
- Click Create Rule. Set the match expression to
trueto apply to all responses. - Under Then, choose Add: Header name
x-frame-options, ValueDENY. - Add a second header:
content-security-policywith valueframe-ancestors 'none';. - Click Deploy. Changes take effect at the edge within seconds.
How to Verify the Header is Set
Use curl to check the headers your server is sending:
curl -sI https://example.com | grep -i -E "x-frame|frame-ancestors"
You should see:
x-frame-options: DENY content-security-policy: frame-ancestors 'none';
You can also verify in Chrome DevTools: Network tab → click your page’s request → Response Headers. Look for x-frame-options. To test that the protection actually works, create a test HTML file with <iframe src="https://example.com"></iframe> and open it locally — the browser should show a blank iframe with a console error like "Refused to display in a frame because it set 'X-Frame-Options' to 'deny'."
Is your site vulnerable to clickjacking?
Run a free Vigilai scan to instantly check X-Frame-Options, CSP, HSTS, TLS, and 4 other security headers — no signup required.