I wrote the standard for making websites AI-operable. Learn More

OARS Practitioner Course
Level 0 · Step 3 of 5

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.


10 minBeginnerA few lines of config

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:

server blocknginx
# 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:

.htaccessapache
# .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:

vercel.jsonjson
{
  "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:

bash
# 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

§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.

Knowledge check

1. What does a security header change about a request?
2. Which value does the lesson recommend for X-Frame-Options?
3. Why should you avoid setting Strict-Transport-Security again in the headers lesson?
4. How does the lesson recommend first deploying Content-Security-Policy?
5. Why does the lesson call these baseline headers “safe for agents”?