Allow embedding on Cloudflare
Cloudflare can add X-Frame-Options SAMEORIGIN on its own through the Add security headers Managed Transform. Turn it off, then set a Content-Security-Policy header with frame-ancestors. Use a Response Header Transform Rule, a _headers file for Pages and Workers static assets, or Worker code.
4 min read · Updated
Find out which layer adds the header
Compare the response through Cloudflare with the response from your origin server:
curl -sI https://your-site.com/ | grep -iE "x-frame-options|content-security-policy"
curl -sI --resolve your-site.com:443:ORIGIN_IP https://your-site.com/ | grep -iE "x-frame-options|content-security-policy"
Put your origin server IP in place of ORIGIN_IP. If only the first command shows X-Frame-Options, Cloudflare adds it. If both show it, fix the origin first. See Nginx or Apache.
Turn off the security headers Managed Transform
The Add security headers Managed Transform adds x-frame-options: SAMEORIGIN to every response, with some other headers.
- In the Cloudflare dashboard, open your zone.
- Go to Rules > Settings.
- Open the Managed Transforms tab.
- Turn off Add security headers.
If you want its other headers, add them back yourself in the Transform Rule below.
Set frame-ancestors
Pick the method that matches how Cloudflare serves the page.
With a Response Header Transform Rule
- Go to Rules > Overview.
- Select Create rule > Response Header Transform Rule.
- Name it, for example "Allow partner frames".
- Under When incoming requests match, pick all requests, or a filter such as URI Path starts with
/embed/. - Under Modify response header, pick Set static. Header name:
Content-Security-Policy. Value:frame-ancestors 'self' https://partner.example. - Select Set new header, pick Remove, and enter
X-Frame-Options. - Select Deploy.
Set static replaces any CSP your origin sends. If the origin sends a full policy, copy all its directives into the value. Add static would create a second policy, and the browser would enforce both.
With a _headers file (Pages and Workers static assets)
Put a _headers file in the folder that holds your built static files:
/*
! X-Frame-Options
Content-Security-Policy: frame-ancestors 'self' https://partner.example
A line that starts with ! removes that header. These rules apply only to static files. Responses from Pages Functions or from your Worker code do not get them.
With Worker code
For server-rendered pages or a Worker in front of an origin, change the headers in code:
export default {
async fetch(request) {
const response = await fetch(request);
const result = new Response(response.body, response);
result.headers.delete("X-Frame-Options");
result.headers.set("Content-Security-Policy", "frame-ancestors 'self' https://partner.example");
return result;
},
};
new Response(response.body, response) copies the response, so you can change its headers.
Check the result
Run the first curl command again. You want one CSP line with your frame-ancestors and no X-Frame-Options. If an old value stays, purge the Cloudflare cache for that URL.
Common mistakes
- The Managed Transform stays on. Your origin is clean, but the page is still blocked.
_headersfor a Function response. It has no effect there.- Two CSP headers. The origin sends one and a rule adds another. Use Set, not Add.
ALLOW-FROM. Browsers ignore it.
Verify through Cloudflare
Paste the public URL into the iframe tester. It tells you if the page can load in a frame, and what still blocks it if not.
To check many pages at once, use the bulk check. It shows X-Frame-Options and frame-ancestors for up to 100 URLs.