Course / Level 0 · Reachable / Step 3
Level 0 · Reachable
Baseline security headers
The minimal hardening headers that signal a competently-run origin without breaking machine clients.
In this lesson
Your site is secure and quick. This step adds a few response headers that make it look — and behave — like a competently-run origin, without getting in an agent’s way.
- Understand what security headers are and why they belong at Level 0
- Add the four baseline headers every site should send
- Know why these are safe for agents — unlike a CAPTCHA
- Treat Content-Security-Policy with the caution it deserves
- Verify the headers your server actually sends
What a security header actually is
Every time your server answers a request, it sends back a handful of invisible headers alongside the page — little notes that tell the browser how to handle what follows.
A security header is just one of those notes that closes off a known way to abuse a page. None of them change how your site looks. They’re among the cheapest, lowest-risk improvements you can make — usually a few lines of config — and their absence is a quiet signal that a site isn’t being looked after.
For Level 0 they do double duty: they harden the site, and they’re a trust signal. The verifiers and tools that size up a domain increasingly check for them.
Note
Important distinction: these headers are safe for agents. They change how a response is handled, not who can reach it. That’s the opposite of a CAPTCHA, which blocks clients outright — and which we deal with in the next lesson.
The baseline set
Four headers cover the Level 0 baseline. Here’s each one in plain terms, with the value to use:
- Strict-Transport-Security — “only ever talk to me over HTTPS.” You already added this in the transport-security lesson, so it’s done.
- X-Content-Type-Options:
nosniff— stops the browser from guessing a file’s type. Without it, a file served as the wrong type can sometimes be run as something it shouldn’t be. - X-Frame-Options:
SAMEORIGIN— stops other sites from loading yours inside a hidden frame, which is how clickjacking tricks work. - Referrer-Policy:
strict-origin-when-cross-origin— trims how much of your URL is leaked to other sites when someone clicks away. A sensible default that doesn’t break analytics.
That’s the whole baseline. Three new lines (HSTS is already in place), no visual change, and a meaningfully more buttoned-up origin.
Note
Don’t set Strict-Transport-Security again here if you added it during the transport lesson — one copy is correct. Setting it in two places can produce conflicting values.
Add them
Same pattern as the redirect — it depends on what runs your site.
On nginx, add them to your server block:
# nginx — baseline security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;On Apache, they go in the same .htaccess file you’ve been using:
# .htaccess — baseline security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"And on a managed platform, you usually don’t touch a server at all. Vercel takes a headers block in vercel.json, Netlify uses a _headers file, and Cloudflare can add them for you under Rules → Transform Rules (or its “Managed Transforms”). Same headers, different doorway.
On Vercel, for instance, the four baseline headers go in a headers block that matches every path:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}The mechanics differ per platform, but the headers and their values stay the same.
There’s a fifth header you’ll eventually want — but it needs a gentler touch than the others.
Common mistake
Content-Security-Policy is the most powerful header here and the easiest to break your own site with — one wrong rule and your styles or scripts stop loading. It’s beyond the Level 0 baseline. When you do add it, deploy it as Content-Security-Policy-Report-Only first, watch what it would have blocked, and only enforce once it’s clean.
For now, the four-header baseline is plenty. Let’s confirm the server is actually sending them.
Verify it worked
Headers are easy to think you set and not actually be sending — a typo or the wrong config file and they silently vanish. Ask the server directly:
# See the headers your server actually sends
curl -sI https://yourdomain.com | grep -Ei "x-content-type-options|x-frame-options|referrer-policy|strict-transport-security"You should see all four come back. For a graded second opinion, run the domain through securityheaders.com.
All four present? Then here’s the short version to remember.
Recap
- Security headers harden how responses are handled — cheap, invisible, and expected.
- The baseline: HSTS (done), X-Content-Type-Options, X-Frame-Options, Referrer-Policy.
- They’re safe for agents — they don’t block anyone, unlike a CAPTCHA.
- Content-Security-Policy is powerful but risky — add it later, in Report-Only first.
Worth bookmarking as you go:
Resources
- securityheaders.com — graded report of your response headers
- OWASP Secure Headers Project — what each header does, with recommendations
- MDN — Content-Security-Policy
§Level 0 · Reachable — read the exact requirements
Hardened and trustworthy. The last piece of Level 0 is making sure none of your protections accidentally lock agents out — the agent path.