Skip to main content

Command Palette

Search for a command to run...

You Set Up HTTPS. You Still Forgot These 5 HTTP Security Headers.

Updated
3 min read
A
Solo developer building 7 SaaS products. Founder of ToolKit Online (140+ free tools), CaptureAPI, CompliPilot, AccessiScan, ChurnGuard, and DocuMint. Open source enthusiast based in Spain.

Getting a green padlock feels like the security box is checked. It isn't. HTTPS encrypts the connection, but it says nothing about clickjacking, script injection, or whether browsers will quietly downgrade your visitors to HTTP. That's the job of HTTP response headers, and most sites I audit are missing several of them.

The problem

Here's the concrete situation. You ship a site, it loads over HTTPS, lighthouse looks fine, and you move on. Then someone runs your domain through a header scanner and you get a column of red: no Content-Security-Policy, no Strict-Transport-Security, no X-Frame-Options, no Referrer-Policy, no Permissions-Policy.

Each gap is a real, exploitable surface:

  • No CSP means an injected <script> (from a compromised dependency or a stored XSS) runs with full trust.
  • No HSTS means a visitor's first request can be intercepted and downgraded to plain HTTP before the redirect.
  • No X-Frame-Options or frame-ancestors means your page can be loaded in a hidden iframe and clickjacked.
  • No Referrer-Policy means full URLs (sometimes with tokens in query strings) leak to third parties.
  • A wide-open Permissions-Policy means embedded content can request camera, mic, or geolocation you never intended to grant.

The scanner tells you all this. What it usually does not tell you is the exact config to fix it, and that's where most people stall.

How to fix it

The good news: these are response headers, so you add them once at the server or CDN layer and every page inherits them. A solid baseline for an Nginx site looks like this:

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'" always;

A few notes that save real debugging time:

  1. Start CSP in report-only mode. Ship Content-Security-Policy-Report-Only first, watch what it would have blocked, then promote it to enforcing. Going straight to a strict CSP on a live site usually breaks inline scripts and third-party widgets.
  2. HSTS preload is a commitment. Once you submit to the preload list, removing HTTPS becomes painful. Add preload only when you're confident the whole domain and subdomains are HTTPS-only.
  3. X-Frame-Options is being superseded by CSP frame-ancestors. Set both for now; older browsers still read the former.

After you add the headers, redeploy and check the live response. You can inspect them quickly from the terminal:

curl -sI https://yoursite.example | grep -i -E 'content-security|strict-transport|x-frame|referrer-policy|permissions-policy'

If a header you added isn't showing up, it's almost always a caching layer or a reverse proxy stripping it before the response reaches the browser.

Closing

You can do all of this by hand: read the spec for each header, write the directives, deploy, curl, repeat. It works, it's just slow, and CSP in particular is easy to get subtly wrong.

If you'd rather skip the doc-diving, HeaderShield does this end to end: you paste a URL, it reads your real response headers, explains each one in plain language, scores the result, and generates a ready-to-paste snippet for Nginx, Apache, or Cloudflare. It's free to try in the browser, so it's an easy way to get from "wall of red" to a fixed baseline in a few minutes. Either path gets you to a hardened site; pick whichever fits your day.



Full disclosure: I build HeaderShield, an HTTP security-header scanner that explains each gap and gives you ready-to-paste Nginx/Apache/Cloudflare snippets. It is free to try at https://headershield.dev.