Two ways to add security headers on Cloudflare: a static _headers file (Cloudflare Pages and
Netlify) or a Worker (any site proxied through Cloudflare).
Option A — _headers file (Cloudflare Pages / Netlify)
Put a file named _headers in your build output root (e.g. public/_headers for many frameworks):
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cross-Origin-Opener-Policy: same-origin
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
The /* matches every path; the indented lines are the headers. You can add more specific path
blocks above /* for per-route overrides.
Option B — Cloudflare Worker
Add headers to every proxied response:
export default {
async fetch(request, env, ctx) {
const response = await fetch(request);
const r = new Response(response.body, response);
r.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
r.headers.set("X-Content-Type-Options", "nosniff");
r.headers.set("X-Frame-Options", "SAMEORIGIN");
r.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
r.headers.set("Permissions-Policy", "geolocation=(), camera=(), microphone=()");
r.headers.set("Cross-Origin-Opener-Policy", "same-origin");
r.headers.set("Content-Security-Policy", "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'");
return r;
},
};
Verify
curl -sI https://yourdomain.com | grep -iE 'strict-transport|content-security|x-frame'
Tip: the _headers file is the simplest path for a static site on Cloudflare Pages — no Worker,
no quota. Use a Worker when you need logic (per-path policies, nonces) or you’re proxying an origin
that isn’t on Pages. Cloudflare’s dashboard “Transform Rules → Modify Response Header” is a third,
no-code option.