X-Frame-Options is an HTTP response header that tells the browser whether your page is allowed to be displayed inside an <iframe>, <frame>, or <object> element. Its primary purpose is to prevent clickjacking attacks, a class of attacks where a malicious website embeds your site invisibly and tricks visitors into performing unintended actions.
Despite being one of the older security headers (introduced around 2009), X-Frame-Options remains widely deployed and still provides important protection. Let's look at why it matters, how it works, and what WordPress site owners need to know.
How Clickjacking Attacks Actually Work
Clickjacking is surprisingly simple in concept. Suppose you run a WordPress site with a membership area, and the profile page has a "Delete My Account" button. An attacker creates a page that looks like a fun quiz or game. They embed your profile page in an invisible iframe positioned directly over their "Click here to see your result" button.
When the visitor clicks what they think is a harmless button on the attacker's page, they actually click the "Delete My Account" button on your hidden page. If the visitor is logged in to your site (and most browsers retain session cookies), the action goes through. The visitor has no idea what just happened.
This technique has been used in real attacks to:
- Change privacy settings on social media accounts
- Authorize OAuth applications without the user's knowledge
- Click ads to generate fraudulent revenue
- Enable webcam or microphone access in older browsers
- Transfer money in online banking interfaces
The attack works because the browser has no built-in way to know that the iframe is being used maliciously. X-Frame-Options gives your server a way to opt out of being framed entirely.
X-Frame-Options Header Values Explained
The header supports two practical values (plus a third deprecated one):
DENY: The page cannot be displayed in any frame, period. No website, including your own, can embed this page in an iframe. This is the strictest setting and works well for pages that should never be framed, like login pages or admin panels.SAMEORIGIN: The page can only be displayed in a frame if the framing site has the same origin (same protocol, domain, and port). This is the most commonly used value because it allows your own site to use iframes while blocking external sites.ALLOW-FROM uri: This was supposed to let you specify a single allowed origin that could frame your page. However, it was never consistently supported across browsers. Chrome and Safari never implemented it. Firefox supported it for a while but dropped support. This value is effectively dead and should not be used.
When to Use DENY vs. SAMEORIGIN on WordPress
For most WordPress sites, SAMEORIGIN is the right choice. Here is why:
WordPress itself uses iframes internally in several places. The theme Customizer loads a preview of your site in an iframe. The media library uploader uses iframes. The classic editor's "Insert Media" dialog does as well. If you set DENY, all of these features will break because even your own domain won't be allowed to frame the page.
DENY makes sense for specific pages where framing should never happen, like a standalone login page or a payment processing page. But as a site-wide default for WordPress, SAMEORIGIN provides the protection you need without breaking admin functionality.
If you're running a headless WordPress setup where the admin interface is on one domain and the front-end is on another, you'll need to be extra careful here, because SAMEORIGIN only allows framing from the exact same origin.
Why ALLOW-FROM Was Deprecated
The deprecation of ALLOW-FROM is worth understanding because it illustrates a broader point about web security headers. The directive was part of the original X-Frame-Options specification, and the idea was straightforward: let a site say "only this specific domain can frame me." In practice, it had several problems:
- It only accepted a single URI, so you couldn't whitelist multiple domains
- Chrome never implemented it (the world's most-used browser simply ignored it)
- Safari never supported it either
- The behavior was inconsistent across the browsers that did support it
The CSP frame-ancestors directive was designed as the proper replacement, supporting multiple origins and having consistent cross-browser behavior. If you need to allow specific external domains to embed your pages, frame-ancestors is the way to do it.
X-Frame-Options vs. CSP frame-ancestors
The Content-Security-Policy header's frame-ancestors directive serves the same purpose as X-Frame-Options but with more flexibility:
Content-Security-Policy: frame-ancestors 'self'This is equivalent to X-Frame-Options: SAMEORIGIN. But frame-ancestors also supports multiple origins:
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com https://another-domain.comThis would be impossible with X-Frame-Options alone.
When both headers are present, browser behavior varies. Modern browsers generally prioritize frame-ancestors over X-Frame-Options. However, the recommended practice is to send both headers for maximum compatibility. Older browsers that don't understand CSP will fall back to X-Frame-Options, while modern browsers will use frame-ancestors.
Here is an example of using both together:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'Setting Up X-Frame-Options for WordPress
WordPress actually sends X-Frame-Options: SAMEORIGIN by default for the admin area (wp-admin). This is done in the wp-includes/functions.php file via the send_frame_options_header() function. However, the front-end of your site does not get this header by default.
To add it site-wide via Apache, put this in your .htaccess file:
Header always set X-Frame-Options "SAMEORIGIN"For Nginx:
add_header X-Frame-Options "SAMEORIGIN" always;You can also set it via PHP in your theme or a plugin:
add_action('send_headers', function() {
header('X-Frame-Options: SAMEORIGIN');
});If you use plugins that need to embed your site in an iframe on another domain (some A/B testing tools, live chat widgets, or preview services), you may need to adjust your approach. In that case, use CSP frame-ancestors to whitelist those specific domains instead of removing X-Frame-Options entirely.
What InspectWP Checks
InspectWP verifies whether your WordPress site sends the X-Frame-Options header on its front-end pages. If the header is missing, the report flags it as a security concern because any website could embed your pages in an iframe, potentially enabling clickjacking attacks against your visitors. The report also notes the header value if present, so you can verify it is set to SAMEORIGIN or DENY as appropriate for your setup.