Run curl -sI https://your-site.example/ to print the response headers, then look for Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options and a frame control. Test the final URL after redirects, not just the first hop, and check static files, error pages and login responses as well as the home page.
Step 1: fetch headers only
curl -sI https://example.com/
The -I flag sends a HEAD request and prints headers. The -s flag hides the progress meter. Some servers answer HEAD differently from GET, so confirm with a GET that discards the body:
curl -s -D - -o /dev/null https://example.com/
Step 2: follow redirects and check every hop
curl -sIL https://example.com/ | grep -i -E "^(HTTP|location|strict-transport|content-security|x-content-type|x-frame|referrer-policy|permissions-policy|cross-origin)"
The -L flag follows redirects. The output shows each response, so you can see the HTTP to HTTPS redirect and the final page. MDN is specific about one point: the redirect from HTTP must not carry the Strict-Transport-Security header, which belongs on the HTTPS response.
Step 3: know what to expect
| Header | What good looks like |
|---|---|
| Strict-Transport-Security | Present on HTTPS responses, max-age set deliberately |
| Content-Security-Policy | Present, with object-src and base-uri set, no broad unsafe-inline |
| X-Content-Type-Options | nosniff |
| frame-ancestors in CSP | 'none' or a short list, unless embedding is intended |
| Referrer-Policy | Set explicitly |
| Set-Cookie | Secure and HttpOnly, with SameSite chosen on purpose |
Step 4: test where headers differ
Headers set on the application often do not reach static files, error pages or redirects served by a proxy or CDN. Test each:
- The home page and a deep page.
- A static asset such as a script or image.
- A 404 page and a redirect.
- A login response, to inspect Set-Cookie.
- An API route.
curl -sI https://example.com/assets/app.js
curl -sI https://example.com/does-not-exist
curl -si -X POST https://example.com/login -d "u=test&p=test" | grep -i set-cookie
Common traps
- An nginx
add_headerdirective can silently drop parent headers, and error responses may skip headers without the right flag. See nginx add_header inheritance. - A header can appear twice if both the app and the proxy add it. For COEP, MDN says duplicate values are treated as unsafe-none.
- A header that works in curl may be ignored by browsers for context reasons, for example HSTS sent over plain HTTP.
- Browser only behaviour such as CSP enforcement needs a real browser test, since curl never runs the page.
Turn it into a regression check
Put the grep lines into a CI job that fails when an expected header disappears. Keep the expected values in a file and diff against it. This catches the common case of a proxy change quietly removing protections.
Try it yourself
Run the web app scan demo and see how findings are grouped, then compare them with what your own curl checks surface.
Click through it at your own pace, or open it full screen.
Quick check
Why does curl show different headers than my browser?
The browser may follow redirects, send cookies and request different paths. Match the URL, method and cookies you are testing.
Does a present header mean the policy works?
No. A weak value is still present. Read the values, then test behaviour in a browser.
Safeguard's policies and gates can run checks in pull requests, and its dependency scan is free to try at gold.safeguard.sh/scan.
Sources
- Strict-Transport-Security header, MDN
- X-Content-Type-Options header, MDN
- Cross-Origin-Embedder-Policy header, MDN
Last reviewed: 19 October 2026.