X-frame-options not working using workers


We have an S3 websites to that sits behind cloudflare and we utilize workers in order to inject security response headers for our content security policies, however we have noticed some strange behaviour.

If we take a resource that exists in S3 (highlighted):

We can see it’s served by Cloudflare but it does NOT have the headers we provided.

If you request an object that does not exist with the .html extension, you do get the header:

If the resource does not exist and it doesn’t end with .html then it also won’t show the header:

So all in all it only works if the resource does NOT exist in S3 and ONLY if it has a .html extension. Can anyone please assist as to why this is the case and what we are doing wrong?

Many Thanks,
Andrew Mosiane

Which headers are you trying to inject specifically?
Not all headers are allowed and CF will just strip them away.

I’m guessing it is the content-security-policy, so it might be that CF removes it automatically.

Hi Thomas,

Thanks for the swift response. Here is a sample of our cloudflare worker code below. What we are really interested in is the x-frame options more so than the content security policy. Could cloudflare be stripping away the x-frame options as well?

let securityHeaders = {
"Content-Security-Policy" : "**********",
"Strict-Transport-Security" : "max-age=63072000; includeSubdomains; preload",
"X-Xss-Protection" : "1; mode=block",
"X-Frame-Options" : "same-origin",
"X-Content-Type-Options" : "nosniff",
"Referrer-Policy" : "same-origin"}

addEventListener(‘fetch’, event => {


async function fetchAndLog(req) {
const res = await fetch(req)
console.log(‘req’, res.status, req.url)
let newHdrs = new Headers(res.headers)

Object.keys(securityHeaders).map(function(name, index) {
	newHdrs.set(name, securityHeaders[name]);

if (res.status === 404) {
    console.log('overwrite status', req.url)
    return new Response(res.body, {
        headers: newHdrs,
        status: 200
return res }

Yeah, you’re not doing anything special here so it does seem like it’s being stripped.

@harris tagging you here, maybe you can confirm?

It looks like the case where the resource does exist is expected, from reading the code (specifically, the script only returns the new headers if (res.status === 404)).

As far as the case where the resource does not exist and does not end with .html, I’m not sure. Is that the full script content that you pasted? Also, what are the routes the script is deployed on? Something like example.com/*, I assume? Lastly, your screenshots suggest that you have a browser-side service worker installed. Could you share what that script does?

I’m not aware of Cloudflare stripping any of these headers. Adding many of these headers programmatically is, I think a classic use case of Workers.

I’ve only noticed few that are stripped, like “server” and some headers that CF apply automatically.

I’m thinking, maybe it’s removed because X-Frame-Options only accepts two values SAMEORIGIN and DENY. While he’s written same-origin, which would be wrong.

Hi guys,

I think we figured out what the issue was. We simply added the below block of code and it resolved the issue.
if (res.status === 200 ).

@harris thanks alot it even resolved the issue related to the .html extension.

Gonna mark this resolved

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.