Security headers are HTTP response headers that tell browsers and automated clients how to handle your content. They protect users from common attacks, and they're also part of the trust signal stack that search engines and AI crawlers use to evaluate site quality — a site without them looks unfinished.
HSTS: enforce HTTPS at the protocol level
Strict-Transport-Security tells browsers and HTTP clients to connect only over HTTPS for a specified duration, and to refuse HTTP connections without even attempting them.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadmax-age=31536000 is one year. includeSubDomains covers every subdomain under your apex. preload makes the site eligible for browser preload lists, which means HTTPS-only behavior is enforced before the first request is ever made. The SEO implication is direct: Google uses HTTPS as a ranking signal, and a site serving content on both HTTP and HTTPS creates duplicate-content risk that canonical tags alone may not fully resolve. Some AI crawlers skip HTTP origins entirely.
CSP: control what executes on your pages
Content-Security-Policy defines which scripts, styles, images, and frames are allowed to load. It's the primary browser-side defense against cross-site scripting (XSS).
Start with a report-only policy (Content-Security-Policy-Report-Only) to collect violations before enforcing. Enforcing a CSP cold on a production site with third-party scripts breaks things. The minimal enforced baseline for a site without heavy third-party dependencies:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'unsafe-inline in script-src defeats most of CSP's XSS protection — if you need it, you haven't finished the migration. frame-ancestors 'none' replaces X-Frame-Options in browsers that support CSP Level 2, but set both for compatibility with older clients.
X-Content-Type-Options and X-Frame-Options
These two headers have no tuning requirements — set them and move on.
X-Content-Type-Options: nosniff prevents browsers from MIME-sniffing responses. Without it, a JSON API response can be executed as a script if the client ignores the Content-Type header. Every origin needs this.
X-Frame-Options: DENY prevents your pages from being embedded in iframes on other domains, blocking clickjacking attacks where a hidden frame tricks users into taking actions they didn't intend. If your CSP includes frame-ancestors 'none', this header is redundant in modern browsers — set it anyway for HTTP clients that don't parse CSP.
Referrer-Policy: limit what leaks cross-origin
Referrer-Policy: strict-origin-when-cross-origin is the correct default for almost every site. It sends the full URL as the referrer within your own origin (useful for analytics), sends only the origin (no path) on cross-origin requests, and sends nothing when downgrading from HTTPS to HTTP.
The failure mode without this policy is URL paths containing query parameters — session tokens, search terms, user IDs — leaking into third-party referrer headers. That's a security issue and a data leak, not just a privacy preference.
Permissions-Policy: restrict browser feature access
Permissions-Policy limits which browser APIs are accessible on your pages. Camera, microphone, and geolocation access should be off by default on any site that doesn't explicitly need them.
Permissions-Policy: camera=(), microphone=(), geolocation=()Empty parentheses mean "deny for all origins including the page itself." Add specific origins inside the parentheses only if a feature is genuinely required.
Putting it together on Cloudflare Workers
The cleanest approach on Cloudflare Workers is a single constant applied to every response in your fetch handler:
const SECURITY_HEADERS = {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
'Content-Security-Policy': "default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'",
};Spread this into every new Response() you return. Don't rely on Cloudflare Transform Rules for the full set — Transform Rules run after the Worker and can't override headers the Worker already set, but Workers can't always assume Transform Rules are in place either.
Verify before shipping
isitready.dev scans your response headers and validates the full security baseline against your live origin. It checks each header's value, not just its presence — a misconfigured CSP or an HSTS without includeSubDomains will flag separately from a missing header. Run it on your canonical origin before treating the security layer as done.