OneWebDesk

Essential Security Headers Guide (CSP, HSTS, X-Frame-Options)

The security headers you must set, what each does, recommended values and common mistakes.

It is tempting to assume that turning on HTTPS makes a site secure, but in practice you need a handful of response headers to stop common attacks like clickjacking, cross-site scripting (XSS), MIME sniffing, and protocol downgrades. Those few lines are your HTTP security headers — instructions the server sends with each page that tell the browser exactly how to treat it.

This guide walks through the six security headers that matter most in practice: what each one blocks, the recommended value, and the mistake people make most often. At the end it shows how to check which headers your site is actually sending. Configure them once and your score on security graders jumps immediately.

Content-Security-Policy (CSP) — your first line against XSS

CSP restricts, via a whitelist, which origins a page may load scripts, styles, images, and fonts from. Even if an attacker injects a malicious <script> into your markup, the browser refuses to run it unless its origin is allowed by the policy. That makes CSP the strongest header you can set: it adds a layer of defense so that an XSS vulnerability does not automatically become a working exploit. One header can block inline scripts, inline styles, and eval(), restrict form submission targets (form-action), and limit who may frame the page (frame-ancestors).

Because it is the most powerful header, it is also the easiest to break. Too strict and legitimate scripts get blocked, taking the site down; too loose and the protection evaporates. The safe path is to start inContent-Security-Policy-Report-Onlymode, watch the violation reports, then tighten and enforce. Generate a starting policy that matches your site’s structure with the CSP Generator to cut down on trial and error.

Strict-Transport-Security (HSTS) — force HTTPS

When a user types just the domain or visits over http://, the very first request goes out in cleartext. A man-in-the-middle can exploit that brief window to intercept traffic and downgrade the connection. HSTS closes the gap by telling a returning browser to “always use HTTPS for this domain from now on.”

  • max-age: how long (in seconds) to remember the rule. The recommended value is two years, 63072000.
  • includeSubDomains: applies the rule to every subdomain. Only enable it when all of them are HTTPS-only.
  • preload: registers the domain in the browser’s built-in HSTS preload list so that even the first visit never touches HTTP. Removal is slow and hard, so add it deliberately.

Recommended value: Strict-Transport-Security: max-age=63072000; includeSubDomains; preload. If even one subdomain has not been moved to HTTPS yet, drop includeSubDomains so you do not accidentally make that subdomain unreachable.

Clickjacking, MIME, referrer, and permissions headers

The remaining headers are one-liners, but their effect is clear.

  • X-Frame-Options / frame-ancestors (clickjacking defense): stops an attacker from layering your page in a transparent <iframe> and tricking users into clicking hidden buttons. UseX-Frame-Options: DENYto forbid all framing, or CSP’sframe-ancestors 'self' to allow only the same origin. The CSP directive is the newer, preferred standard; many sites keep both for older-browser compatibility.
  • X-Content-Type-Options: nosniff (block MIME sniffing): stops the browser from ignoring the declared Content-Type and guessing a type from the bytes. That prevents a text file from being reinterpreted and executed as a script. The only value is nosniff.
  • Referrer-Policy (prevent referrer leakage): controls how much of the originating URL leaks to other sites when a user follows an outbound link. The recommended value isstrict-origin-when-cross-origin: the full path within your own origin, only the domain to external sites.
  • Permissions-Policy (restrict browser features): locks down powerful capabilities like camera, microphone, geolocation, and payment for the page and any embedded iframes. Turn off everything you do not use, e.g. Permissions-Policy: geolocation=(), camera=(), microphone=().

The headers at a glance: purpose and recommended value

HeaderWhat it blocksRecommended value
Content-Security-PolicyXSS, data injection, unwanted framingdefault-src 'self'; object-src 'none'; frame-ancestors 'self' (tune per site)
Strict-Transport-SecurityProtocol downgrade, cleartext first requestmax-age=63072000; includeSubDomains; preload
X-Frame-OptionsClickjacking (iframe embedding)DENY or SAMEORIGIN
X-Content-Type-OptionsMIME sniffingnosniff
Referrer-PolicyReferrer URL leakagestrict-origin-when-cross-origin
Permissions-PolicyAccess to unneeded browser featuresgeolocation=(), camera=(), microphone=()

How to verify, and common mistakes

Setting the headers is not the end — you must confirm they actually ship in the response and that there are no typos or bad values. The fastest route is to enter your domain into Security Headers Check and see at a glance which headers are present and which are missing. You can also read the response headers directly in your browser DevTools Network tab, or from a terminal with curl -I https://example.com.

These are the mistakes that show up most often in the field.

  • An overly broad CSP: adding script-src 'unsafe-inline','unsafe-eval', or a wildcard * means the header exists but the XSS protection is effectively gone. Rather than whitelisting all inline code because CSP keeps breaking, allow only the inline scripts you need via a nonce or hash.
  • Adding includeSubDomains / preload to HSTS blindly: a single subdomain that is not HTTPS becomes unreachable. preload is very slow to undo, so enable it only after confirming every domain is HTTPS.
  • Setting headers on HTTP but missing them on HTTPS: when a reverse proxy or load balancer has split configs, headers often attach to only one side. Check both.
  • Typos and invalid directives: a misspelling like X-Frame-Option (missing the s) is simply ignored by the browser. Always confirm the header is really applied with a checker after you deploy it.

Frequently asked questions

Where do I set security headers?
You can add them as response headers anywhere: in application code (middleware), the web server (nginx/Apache), or a CDN/reverse proxy. Manage them consistently in one place, and if you set them across multiple layers, make sure the values don't conflict. After configuring, always verify they reach the actual response.
I turned on CSP and my site broke.
The policy is likely too narrow, blocking the origins of legitimate scripts, styles, or fonts. Start in Content-Security-Policy-Report-Only mode to collect violations in the console without blocking anything, add the required origins, then switch to enforcing mode. A CSP generator gives you a draft that fits your site structure quickly.
Should I use X-Frame-Options or CSP frame-ancestors?
frame-ancestors is the newer, more expressive standard, and when both headers are present the browser honors frame-ancestors. However, very old browsers don't understand it, so it's common to keep X-Frame-Options: DENY/SAMEORIGIN alongside it for compatibility.
Do I really need to drop unsafe-inline?
Drop it where you can. Allowing unsafe-inline lets all inline scripts run, which removes most of CSP's XSS protection. If you truly need inline code, narrow the allowance to just those scripts with a nonce (a random per-request token) or a content hash.
Is enabling HSTS preload always a good idea?
It's strong security but warrants caution. Once you're on the preload list, HTTPS is forced on every subdomain, and getting removed can take months. Only register when you're certain every (sub)domain serves HTTPS reliably.

Tools to use with this guide