Vigil
All articles
SecurityAug 12, 2026 · 8 min read

The security headers every site should send — and how to set them

A response header is the cheapest security control you own: one line of config, no code change, and the browser enforces it for every visitor. Vigil flagged one or more of these as missing or weak. Here is what each header does and how to set it correctly.

NB
Nebojsa B.
CTO, Lilly021

Vigil checks the response your site actually returns — following redirects to the final page — and reports the headers that harden a browser against the common web attacks: protocol downgrade, cross-site scripting, MIME confusion, clickjacking and referrer leakage. None of these require touching application code; they are directives you send and the browser obeys. Below, each check Vigil raises, why it matters, and the fix.

Strict-Transport-Security (HSTS)

Without HSTS, the first request a returning visitor makes can still go over plain HTTP — long enough for an attacker on the same network to intercept it or strip the redirect to HTTPS. HSTS tells the browser to refuse HTTP for your domain for a set period, closing that window. Vigil scores a missing HSTS header as a fail because it is the one header that protects the connection itself.

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

Use a max-age of at least 180 days (15552000). Add includeSubDomains only once every subdomain is HTTPS, and preload only when you are ready to be on the browser preload list — that is hard to undo.

Content-Security-Policy (CSP)

A CSP is the single strongest defence against cross-site scripting: it tells the browser which sources of script, style and other content are allowed, so an injected <script> from an attacker simply won't run. Vigil raises two separate findings here — the header being absent entirely, and a policy that is present but still allows 'unsafe-inline' or 'unsafe-eval', which re-open the exact hole a CSP is meant to close.

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

Start in report-only mode (Content-Security-Policy-Report-Only) to find what breaks, then enforce. Replace inline scripts with files or nonces rather than reaching for 'unsafe-inline' — a policy with unsafe sources gives most of the protection away.

X-Content-Type-Options: nosniff

Browsers will sometimes guess a response's real type instead of trusting your Content-Type — and guess a user upload or a JSON endpoint into executable script. nosniff turns that guessing off. It is a single, safe, universally-supported value with essentially no downside.

X-Content-Type-Options: nosniff

Clickjacking protection

If another site can load yours inside a hidden frame, it can trick your users into clicking things they can't see — a classic clickjacking attack against admin actions and payment flows. Vigil is satisfied by either the older X-Frame-Options header or a modern CSP frame-ancestors directive; you only need one.

# modern (preferred) — part of your CSP
Content-Security-Policy: frame-ancestors 'none'

# legacy fallback for very old browsers
X-Frame-Options: DENY

Referrer-Policy and Permissions-Policy

Referrer-Policy controls how much of your URL is sent to other sites when a user clicks a link — full URLs can carry session ids or tokens in the path, so leaking them is a real risk. Permissions-Policy locks down powerful browser features (camera, microphone, geolocation) so embedded or compromised content can't silently reach for them.

Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

Setting them all at once

# nginx — in the server block
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'" 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;
// Node/Express — one line with Helmet
import helmet from "helmet";
app.use(helmet()); // sets HSTS, nosniff, frame protection and a starter CSP

// Apache — in the vhost or .htaccess
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
A header is a control you configure once and the browser enforces a million times. There is no cheaper security you can buy.— the RD-05 design note

Ship the headers, then re-run the check. Vigil re-reads the live response and clears each finding as the header appears — inspecting only the header names and whether a directive is present, never the values themselves.

Can’t fix it yourself? We’ll do it for you.

Our team can apply this fix on your site and verify it with Vigil. Already monitoring this site with Vigil? Open the failing check and hit “Request a fix” — we’ll get the exact problem and URL. Otherwise, get in touch.

Watching client sites the hard way? Try Vigil free →
More articles