← All guides

HTTP security headers

Running services
On this page

In plain terms

When a browser loads your site, your server sends back response headers alongside the page. A handful of them are instructions to the browser about how to defend the user: don't let this page be framed by an attacker, only ever connect over HTTPS, don't guess file types, block inline scripts. HTTP security headers are those instructions, and they're some of the cheapest, highest-leverage protections you can add.

Why it matters

Missing headers don't announce themselves. The site works fine without them, but each gap enables a class of attack: no Content-Security-Policy makes cross-site scripting far easier; no X-Frame-Options/frame-ancestors allows clickjacking; no Strict-Transport-Security leaves users exposed to protocol-downgrade attacks. They're a staple of the OWASP Secure Headers Project, OWASP, the Open Worldwide Application Security Project, is a widely-trusted nonprofit that publishes free security guidance.

Where it fits

  • Artifact: a running HTTP endpoint (your web app or API).
  • Stage: against a deployed or preview environment, and you can enforce them declaratively in your CDN/edge config (e.g. a Cloudflare _headers file) so they're reviewed like code.

How it works

A scanner makes a request to your URL and inspects the response headers, checking that the important ones are present and sensibly configured. The relevant set depends on the endpoint: a browser-facing page cares about CSP, X-Frame-Options, and Referrer-Policy; a JSON API cares more about CORS (Cross-Origin Resource Sharing) not being wildcard-open and about not caching sensitive responses, flagging browser-only headers on an API is just noise.

A header that is present and a header that protects somebody Two responses, both carrying a Content-Security-Policy. The first sets default-src to any origin and allows inline script and eval, which permits precisely the things the header was designed to block. The second restricts script to the same origin and forbids the page being framed. A scanner that only asks whether the header is present reports the same result for both, so the check has to read the directives rather than count the header. BOTH OF THESE RESPONSES HAVE A CONTENT-SECURITY-POLICY default-src *; script-src 'unsafe-inline' 'unsafe-eval' PERMITS script from any origin, script written into the page, and string-to-code which is the list of things a Content-Security-Policy exists to stop header present default-src 'self'; script-src 'self'; frame-ancestors 'none' PERMITS script served from this origin, and nothing else and the page cannot be put in a frame by anybody header present A check that counts headers scores these the same, and the site running the permissive one believes it is covered.
Presence is the easy half. The directives are where a Content-Security-Policy either protects a user or quietly declines to.
ToolNotes
Mozilla HTTP ObservatoryFree scanner + letter-grade score
OWASP ZAPOSS DAST; its passive scan flags header gaps
NucleiOSS; templates for header/exposure checks
securityheaders.comPopular free online grader

Common pitfalls

  • A too-strict CSP breaks the site. Content-Security-Policy is the highest-value header and the easiest to get wrong. Start in report-only mode, then enforce.
  • Headers are environment-specific. They're set at the edge/server, so verify them against what's actually served, not just what you think is configured.
  • Browser vs. API. Apply the right checklist to each, don't demand a CSP on a pure API.
How Draugr fits. Draugr's headers control is a native check (no external tool) that fetches each of a component's hosts and evaluates its security headers against OWASP guidance, tuned by host type (browser vs api) so it doesn't flag browser-only headers on an API. It follows redirects, so it grades what a browser actually receives rather than what the first response says. And it grades the Content-Security-Policy rather than counting it: default-src *; script-src 'unsafe-inline' 'unsafe-eval' satisfies a presence check while permitting exactly what a CSP exists to prevent, so Draugr reads the directives. It also checks the policy against the page it protects and names the inline code and third-party resources the policy would refuse, as notes that never fail a security gate. Draugr runs it against this site in CI. See declaring hosts on a component in the Saga reference.

Keep learning

  • DAST, deeper runtime testing of the same endpoint
  • TLS assessment, securing the connection headers ride on