Security headers for APIs: what to set and test

Laptop on a desk showing blurred HTTP response output in a dark terminal window

An API should send X-Content-Type-Options: nosniff, a Content-Type that matches the response, Cache-Control: no-store on sensitive data, Content-Security-Policy: frame-ancestors 'none', and Strict-Transport-Security, and it should disable CORS unless it has real cross-origin callers.[1][2] A full CSP, Permissions-Policy and the cross-origin isolation headers mostly matter only if an endpoint can return HTML. A security headers check confirms those headers are present. It cannot tell you whether one user can read another user's data.

What security headers are and what checks do

Security headers are HTTP response headers that tell the browser how to handle a response: whether it may be framed by another site, whether it must only be loaded over HTTPS, and which other origins may read or embed it. A security headers check is an automated scan that fetches a live URL, reads those response headers, and reports which protective ones are present, missing or misconfigured.

The closest thing to a reference list is the OWASP Secure Headers Project. OWASP says its GitHub-hosted documentation is the current version, noting that "the GitHub repository remains the sole source of truth".[3] That list covers headers for every kind of web response, from HSTS and Content-Security-Policy to Permissions-Policy and the cross-origin headers.[4] Most of it was written with browser-rendered pages in mind, and that is where APIs differ.

Why an API needs a different header set

A JSON-only API does not render a page, so there is no layout to clickjack and no inline script for a browser to run. Several headers on a generic website checklist do little for it. OWASP's REST Security Cheat Sheet makes that split: it lists a short core set for every API response and treats the rest as relevant only when an API can return HTML.[1]

The headers that remain are the ones that stop a browser or cache from mishandling the data itself. They are also easy to skip when an app and its API are generated quickly by an AI tool or a no-code platform, because the code works without them and nothing visibly breaks.

The headers to set on every API response

Six settings cover most of what a JSON API needs from its headers. The table below lists the value to send and whether a JSON-only API needs it. For framework-level setup, see our guide on how to set up security headers in a web application.

HeaderValue to sendNeeded for JSON-only API?Source
X-Content-Type-OptionsnosniffYesOWASP REST cheat sheet[1]
Content-TypeThe real type, e.g. application/jsonYesOWASP REST cheat sheet[1]
Cache-Controlno-store on sensitive responsesYesOWASP REST cheat sheet[1]
Content-Security-Policyframe-ancestors 'none'YesOWASP REST cheat sheet[1]
Strict-Transport-SecuritySet once HTTPS works everywhereYesMDN[2]
CORS headersOff, or a narrow list of originsYes, as a deliberate choiceOWASP REST cheat sheet[1]
Full CSP (default-src)A policy for scripts, styles, framesOnly if HTML is returnedOWASP Secure Headers[4]
Permissions-PolicyDisable unused browser featuresOnly if HTML is returnedOWASP Secure Headers[4]
COOP / CORPIsolate from cross-origin pagesOnly if a browser loads itOWASP Secure Headers[4]
Core and conditional security headers for an API

Stop browsers from guessing the content type

X-Content-Type-Options: nosniff "prevents browsers from performing MIME sniffing", so a browser trusts your declared type instead of guessing.[1] That only helps if the declared type is right. OWASP says Content-Type "must be specified as per the type of content returned by an API call", which means application/json for JSON, not a framework default like text/html.[1]

Keep sensitive responses out of caches

Cache-Control: no-store "instructs compliant private and shared HTTP caches not to store the response or reuse it for another request".[1] Send it on login responses, token refreshes, and any endpoint that returns personal or account data. Public, non-sensitive data such as a product catalog can use normal caching.

Block framing and force HTTPS

Content-Security-Policy: frame-ancestors 'none' "prevents any domain from framing the response returned by the API call".[1] It is the one part of CSP worth sending even from a pure JSON API.

Strict-Transport-Security tells browsers the host "should only be accessed using HTTPS" and that later HTTP requests get upgraded automatically.[2] MDN also notes the host must send it "over HTTPS only, not insecure HTTP", so confirm TLS works on every subdomain and endpoint before you add it.[2]

Treat CORS as an allow list, not a default

OWASP's advice is direct: "Disable CORS headers if cross-domain calls are not supported/expected".[1] If your API serves a frontend on another domain, list that exact origin. A wildcard origin lets any website call your API from a visitor's browser, which defeats the point of having a policy.

Headers that only matter if the API returns HTML

A full Content-Security-Policy, Permissions-Policy and Referrer-Policy protect pages a browser renders, so for an API they matter mainly where an endpoint can return HTML: error pages, hosted API docs, or OAuth redirect screens. When they apply, they are worth getting right. OWASP notes that "a proper CSP can prevent a wide range of attacks", and describes Permissions-Policy as a header "for controlling delegation of permissions and powerful features".[4]

The cross-origin headers follow the same logic. Cross-Origin-Opener-Policy lets you "ensure a top-level document does not share a browsing context group with cross-origin documents", and Cross-Origin-Resource-Policy lets a site "opt in to protection against certain requests from other origins".[4] Both protect content that browsers load. A JSON endpoint called server to server gets little from them, though CORP can still help if browsers fetch your API responses directly.

How a security headers check actually works

A header checker requests a URL, reads the response headers, and grades or highlights which protective ones are present. That is the whole mechanism. It does not send attack payloads, log in, or try other users' IDs, so it is not a vulnerability scan.

The best-known free tool, securityheaders.io (now securityheaders.com, run by Snyk), was built to make raw header output easier to read. Its creator wrote that "the results of the scan are colour coded to make it easier to identify security based headers".[5] Browser extensions that grade headers do the same kind of presence check from inside your tab. They save a step, but they do not test anything a web-based checker misses.

Point the check at your API endpoints, not just your homepage. Many apps set headers in the web server for static pages while the API routes, served by a different framework or function, send none. Test at least one authenticated endpoint and one error response, since those often take a different code path.

Start a free scan

What a security headers check completely misses

A header check only reads what is in the response, so it cannot see how your API behaves. It has no idea whether GET /orders/1042 returns another customer's order, whether an admin route works without an admin role, or whether a checkout endpoint accepts a price sent by the client. Those are access control and business logic flaws, and a perfect header grade says nothing about them.

The same goes for weak authentication, poorly validated tokens, and injection. Headers can reduce the damage some attacks cause in a browser, but they do not fix the flaw on the server. We cover this gap in more depth in what a security headers check tells you and what it misses. If your app was built on a hosted AI platform, our breakdown of what the Replit Security Agent covers and what it leaves to you shows a similar split between automated checks and the logic you own.

Set the headers, then test the API behind them

Fix header gaps first. They are a few lines of configuration, low risk to change, and easy to confirm with any header checker. Then test what headers cannot cover: who can reach which data, how tokens are handled, and whether workflows can be abused.

That second step needs a scan of the running app, not its headers. BattleTester crawls your entire application as each user role: anonymous, logged-in and admin. Its tests check whether users can access resources or perform actions beyond their permissions, analyze JSON Web Tokens for weak signing algorithms, missing expiration claims and improper validation, and probe business logic for price manipulation, race conditions, workflow bypasses and parameter tampering.

The roles matter. With no login, a scan tests only publicly accessible endpoints, and access control and privilege checks are then limited or skipped. A header check sits even further out, since it never touches the logic at all. BattleTester is not a comprehensive security audit, so for a high-stakes app, also book an assessment with qualified security professionals.

Start a full scan

FAQ

Does Content-Security-Policy matter for a JSON-only API?

Only the frame-ancestors 'none' directive is worth sending on every JSON response.[1] A full CSP starts to matter once the same host serves anything a browser renders, so check your docs pages and default error pages, which frameworks often serve as HTML even when the API itself only returns JSON.

What does the OWASP Secure Headers Project actually recommend?

It keeps a categorized list of response headers to set and headers to drop, and it treats its GitHub-hosted docs as the authoritative version.[3] Check that version rather than a copied list in an older blog post, because the recommendations change as browsers drop old headers.

Is an A+ grade from securityheaders.com proof that an API is secure?

No. The grade reflects which headers were present on the one URL you tested, so it can be perfect while an authenticated endpoint leaks other users' records. Treat it as one item on a checklist, not as a verdict on the API.

Are browser extensions that scan headers reliable, or just a convenience?

They are mostly a convenience. They see responses to what your browser loads, so they miss endpoints your frontend calls with POST or PUT and anything your backend calls server to server. For those, inspect the headers with curl or your API client.

Sources

  1. REST Security Cheat Sheet, OWASP, accessed October 15, 2026
  2. Strict-Transport-Security, MDN Web Docs (Mozilla), accessed October 15, 2026
  3. OWASP Secure Headers Project, OWASP, accessed October 15, 2026
  4. OWASP Secure Headers Project - Response Headers, OWASP, accessed October 15, 2026
  5. Introducing securityheaders.io, Scott Helme (creator of securityheaders.io), accessed October 15, 2026