All guides

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.

  1. In the Cloudflare dashboard, open your zone.
  2. Go to Rules > Settings.
  3. Open the Managed Transforms tab.
  4. 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

  1. Go to Rules > Overview.
  2. Select Create rule > Response Header Transform Rule.
  3. Name it, for example "Allow partner frames".
  4. Under When incoming requests match, pick all requests, or a filter such as URI Path starts with /embed/.
  5. Under Modify response header, pick Set static. Header name: Content-Security-Policy. Value: frame-ancestors 'self' https://partner.example.
  6. Select Set new header, pick Remove, and enter X-Frame-Options.
  7. 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.
  • _headers for 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.

Sources

Check your page now

Know before you embed.

Free, no sign-up, nothing stored. Paste a URL and get a verdict, a live preview, and the fix.