X-Frame-Options WordPress: Configure and Test It Safely

feature image

If you need an x-frame-options wordpress fix, first inspect the response header your site sends. X-Frame-Options tells a browser whether it may display a page inside a frame, iframe, embed, or object. This reduces clickjacking risk, where an attacker hides a real page beneath a decoy and tricks a visitor into activating a sensitive control. It is one focused task within wider WordPress security.

TL;DR: Use SAMEORIGIN when WordPress needs legitimate same-origin previews, editors, or other frames. Use DENY only when the response must never be framed. Set the rule in one controlled place, inspect the final response, and test the WordPress features that rely on frames. For named external origins, use Content Security Policy in WordPress instead of obsolete ALLOW-FROM advice.

Choose the right policy

X-Frame-Options is an HTTP response header, not an HTML setting. A <meta http-equiv="X-Frame-Options"> tag does not enforce it.

The header is a focused clickjacking defense. It does not patch a vulnerable plugin, protect every script on a page, or replace account security, updates, backups, and malware protection.

The practical values are:

  • DENY blocks framing from every origin, including the same site. Use it when the response must never appear in a frame.
  • SAMEORIGIN allows framing only when the framing page and every ancestor have the same origin as the response.

Same origin means the same scheme, host, and port. https://www.example.com and https://app.example.com are different origins. HTTP and HTTPS are different, as are different ports.

For most WordPress sites, SAMEORIGIN is the safer site-wide starting point and a useful part of website hardening. Previews, login or admin screens, editors, and page builders may use legitimate same-origin frames. A site-wide DENY rule can break those workflows, so test before applying it.

🧭 Note: “Same site” does not always mean “same origin.” A sibling subdomain or a different port can still be blocked by SAMEORIGIN.

If a named external site must embed the page, do not use ALLOW-FROM. It is obsolete and modern browsers may ignore it. Use the Content-Security-Policy response header with frame-ancestors instead:

Content-Security-Policy: frame-ancestors 'self' https://app.example.com;

frame-ancestors controls which sites may embed your page. frame-src controls which frames your page may load; they are different directives. Use frame-ancestors ‘none’ for no framing or ‘self’ for same-origin framing. These are directive examples, not complete CSP policies to paste without checking the rest of the site.

When both policies are present, modern browsers use CSP for the framing decision. Keep X-Frame-Options only when its fallback behavior agrees with the CSP policy.

Check the current response

Find the existing header before adding another rule. WordPress, a theme, a plugin, Apache, Nginx, the host, or a CDN may already set it. Duplicate or conflicting values make the result harder to predict. Choose one owner for the final value.

For a quick check, replace the example URL with the exact page you need to protect:

curl -IL https://example.com/

This follows redirects and prints their headers plus the final response headers. If the site treats HEAD and GET differently, use a GET request instead:

curl -sS -D - -o /dev/null -L https://example.com/

You can also open browser developer tools, select Network, reload the page, and inspect the document request’s Response Headers. Inspect the HTML document, not a third-party image, script, or stylesheet.

Check the public page and any route that matters to the site: login, dashboard, previews, editor, page builder, redirects, and 404 pages. A header can exist on one response and be missing on another. A CDN or proxy can also replace or cache the origin response.

WordPress core includes send_frame_options_header(). In protected request contexts, when headers have not already been sent, it sends X-Frame-Options: SAMEORIGIN and a matching Content-Security-Policy: frame-ancestors ‘self’ policy. That does not guarantee site-wide coverage on every front-end response, so inspect the response visitors actually receive.

🔎 Note: A scanner warning that the header is missing is a reason to inspect the response, not proof that every page needs the same policy. Decide whether each response should be frameable first.

Configure X-Frame-Options in WordPress

Use the least invasive layer you control, then remove competing rules. A plugin is convenient, but a server or CDN may still override it.

Use a security-header plugin

A maintained WordPress security plugin can be convenient when you cannot edit server configuration. Plugin screens and supported response types vary, so verify the public response after saving.

  • Open the plugin’s security, headers, or advanced settings and find X-Frame-Options.
  • Choose SAMEORIGIN unless you have confirmed that the response never needs a legitimate frame. Choose DENY only after checking frame-dependent workflows.
  • Save the setting, clear relevant caches, and inspect the final public response.

⚙️ Note: A saved plugin setting is not evidence that the browser received the new header. Check the public response for the route you changed.

Add it to Apache with .htaccess

This method requires Apache and the mod_headers module. Back up the active .htaccess file and keep the existing WordPress rewrite block.

<IfModule mod_headers.c>
    Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>

The always form helps include redirects and error responses processed by Apache. Replace SAMEORIGIN with DENY only when no framing is legitimate. Keep broader .htaccess security rules scoped and tested so they do not interfere with WordPress routing.

If the site returns a server error, restore the backup and ask the host to check the Apache configuration. Do not edit WordPress core files.

Add it to Nginx

Nginx uses its server configuration rather than .htaccess. Add the directive to the active server or location block that serves the site:

add_header X-Frame-Options "SAMEORIGIN" always;

Run the configuration test before reloading:

nginx -t

A more specific location block can change which headers are returned. Verify the public URL after reloading.

Send it through WordPress PHP

When WordPress is the layer you control, use a site-specific plugin or child theme with the send_headers hook:

add_action( 'send_headers', function () {
    header( 'X-Frame-Options: SAMEORIGIN' );
} );

This may not affect redirects, errors, or requests that end before WordPress runs. A server, CDN, or another plugin can also replace or duplicate the header. Inspect the public response after adding it.

Ask the host or CDN to apply it

Use this option when the host manages Apache or Nginx, or when a CDN controls the response visitors receive. Give the provider the exact URLs, desired value, legitimate frame workflows, and requirement to verify the public response after deployment. If the origin is correct but the browser receives a different value, the CDN or proxy is the likely control point.

The public WordPress page renders normally after the site-specific PHP header hook and is ready for response verification.

Verify the change safely

The change is complete only when the public response has one intended value and the workflows that need frames still work.

When available, test the change in a staging environment before production; WordPress staging plugins can help create that safer test path.

  • Recheck the final public response with curl -IL or the browser Network panel after saving and clearing relevant caches. Use a broader WordPress security checklist to track the controls beyond this header.
  • Test the reported page as a logged-out visitor, including important forms and visitor routes.
  • While logged in, test login, the dashboard, media, previews, the editor, the page builder, and other same-origin frames that matter to the site.
  • For every intentional frame, compare the scheme, host, port, and every parent in the frame chain.
  • If external embedding is approved, test the allowed origin and confirm that an unapproved origin remains blocked by CSP frame-ancestors.
  • Compare redirects, 404 responses, cached responses, and the origin response when possible. The browser uses the public result, not the response the origin was expected to send.

🧪 Note: A changed header proves only that the response changed. The workflow checks show whether the policy broke a real WordPress feature.

Troubleshoot common failures

The browser says “Refused to display”

Inspect the response for the page being framed. DENY blocks every frame. SAMEORIGIN blocks the frame when the scheme, host, port, or any ancestor differs. Correct the origin mismatch or use a narrowly scoped CSP frame-ancestors policy for a legitimate external embed.

A browser refuses to display the framed WordPress page because the response uses X-Frame-Options: DENY.

If the frame belongs to another site, inspect that site’s response too. X-Frame-Options controls whether your response can be framed; it does not control every third-party frame your page loads.

An editor, preview, or page builder stopped working

If an error occurred while opening the editor, check the document response for the screen that broke. A site-wide DENY rule can block a legitimate WordPress frame. Use SAMEORIGIN only if the complete parent-frame chain is same-origin; otherwise scope a CSP policy to the legitimate origin.

The header is missing, duplicated, or wrong

Inspect the real response rather than page source. Common causes include a meta tag with no effect, a rule in the wrong Apache virtual host, missing mod_headers, a PHP rule that runs too late, a CDN override, or several layers setting the header.

Clear relevant caches, follow redirects, compare the public response with the origin when possible, and keep one authoritative owner. If the value still differs, trace the request through the server logs, WordPress, firewall, and cache layers.

The site went offline after the change

Restore the backup and undo only the latest configuration change. A tested backup plugin can make that rollback safer. Run the Apache or Nginx syntax check before trying again. If you do not control the server layer, ask the host to apply and test the rule.

Do not blanket-unset the header just to make an iframe load. Removing the framing error can remove a useful defense or expose a separate access problem, such as a WordPress 403 forbidden error. Trace the real cause instead of disabling protection blindly.

An external embed still fails

X-Frame-Options cannot reliably express a modern list of trusted external origins. Replace obsolete ALLOW-FROM advice with CSP frame-ancestors, list only the HTTPS origins that genuinely need access, and test every ancestor in the frame chain.

Keep the protection in perspective

X-Frame-Options reduces clickjacking risk. It does not patch vulnerable WordPress plugins, stop stolen passwords, clean malware, restore lost data, or replace least-privilege accounts, multi-factor authentication, updates, tested backups, and incident response.

After the header is working, review the wider WordPress security headers layer as well. MalCare can complement this narrow control with firewall protection, malware scanning and cleanup, and broader WordPress protection. It does not automatically replace the server or CDN header policy or make a carefully chosen CSP policy unnecessary.

Conclusion

For a safe x-frame-options wordpress configuration, choose the narrowest policy that matches the site’s real framing needs: SAMEORIGIN for legitimate same-origin frames, DENY when framing is never allowed, and CSP frame-ancestors when named external sites need access.

Set the rule in one controlled layer, verify the final public response, and test the WordPress workflows that matter. The header reduces clickjacking risk; updates, account protection, backups, malware defense, and the WordPress security maintenance checklist address the rest of the security picture.

X-Frame-Options is an HTTP response header that tells the browser whether a WordPress page may be displayed inside a frame, iframe, embed, or object. It mainly reduces clickjacking risk.
Use **DENY** when the response must never be framed. Use **SAMEORIGIN** when the site needs legitimate same-origin previews, editors, page builders, or other frames. Test the workflows that matter before choosing **DENY** site-wide.
WordPress core can send **X-Frame-Options: SAMEORIGIN** in protected request contexts, but you should not assume that every front-end response has the header. Inspect the actual public response for each route that matters.
No. It must be sent as an HTTP response header. A `` tag in the page does not enforce the policy.
Inspect the framed page's response for **DENY** or a same-origin mismatch involving the scheme, host, port, or a frame ancestor. Correct the mismatch or use a narrowly scoped CSP **frame-ancestors** policy when an external embed is legitimate.

Akshat is the Founder and CEO of BlogVault, MalCare, and WP Remote. These WordPress plugins, designed for complete website management, allows 100,000+ customers to build and manage high-performance websites with ease.