Skip to content

Four security headers worth twenty minutes

Most sites send none of them. Each one closes a real attack, none of them require a code change, and you can verify all four with a single command.

A laptop screen glowing blue, out of focus

Security headers are the least glamorous work in web development. They are also close to the highest ratio of protection to effort available, because each one is a single line of configuration that closes an attack class outright.

We check for these on every website we build. Most sites send none of them.

Strict-Transport-Security

Tells the browser to only ever reach this site over HTTPS, for a stated period, and to refuse to fall back to plain HTTP even if a link says otherwise.

Without it there is a window on the very first request where a redirect from http to https can be intercepted. HSTS closes that window for every visit after the first.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

The one caution: includeSubDomains means every subdomain must serve valid HTTPS. If something internal is still on http, fix that first.

X-Content-Type-Options

One value, one job.

X-Content-Type-Options: nosniff

Browsers historically guessed at a file's real type when the declared type looked wrong. That guessing is exploitable: an uploaded file declared as text can be sniffed as script and executed. Nosniff turns the guessing off. There is no downside and no reason to omit it.

Clickjacking protection

Stops your pages being loaded inside a frame on somebody else's site, which is how an attacker overlays an invisible copy of your interface on top of theirs and harvests clicks intended for something else.

The modern form is a CSP directive:

Content-Security-Policy: frame-ancestors 'none'

The older header, X-Frame-Options: SAMEORIGIN, does the same job for browsers that predate it. Sending both costs nothing.

A Content-Security-Policy, even a modest one

A full CSP that stops cross site scripting is genuinely hard on a modern framework, because frameworks inline scripts and the strict answer requires per-request nonces, which can force every page to render dynamically.

That difficulty makes people skip CSP entirely, and that is the mistake. A policy that does not stop XSS still closes several real attacks:

Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'

base-uri stops an injected base tag rewriting every relative link on the page. form-action stops a form being repointed at somebody else's server. object-src closes plugin execution. frame-ancestors is the clickjacking protection above.

None of that requires nonces, and none of it breaks a normal site.

Checking what you send

curl -sI https://yoursite.com

Read the response headers. Or run the free site check on this site, which reports the first three and tells you which are missing.

Why this is usually skipped

Not because it is hard. Because nothing visibly improves when it is done. No page loads faster, nothing looks different, and no metric moves.

That is exactly the category of work that decides whether a site is well built, and it is the category that gets cut when someone is estimating by what is visible in a demo.

Tell us what needs building

Describe the problem, the system you have today, and the date it needs to be live. We reply with a written scope and a fixed price.