Skip to content
Control Plane Labs

HTTP Header Inspector

Paste a URL, see the response headers with a plain-English explanation of each.

Privacy: This tool is server-backed. The URL you submit is sent to our /api/header-inspect edge function, which performs the GET on your behalf and returns the status chain and response headers. It sees only that URL — no cookies, no credentials, and nothing else from your browser. We keep anonymous request counts for rate limiting (30 per hour) and do not store the URLs. Grading happens in your browser on the returned headers.

This is the one tool here that uses a server. Our edge function makes the GET request, because a browser cannot read cross-origin response headers. It sees the URL you submit and nothing else. Private, loopback, and link-local addresses are refused, and the limit is 30 lookups per hour.

What it does

Submit a URL and get back the full redirect chain, the final status, every response header, and a graded read on the ones that matter for security and caching. Each header links to the specification or MDN page that defines it, and each verdict explains what the value actually does rather than just whether the header is present.

The grading covers the six headers worth arguing about — HSTS, CSP, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and X-Frame-Options — plus the caching set: Cache-Control, ETag, Vary, Age, Expires, and Last-Modified. Present-but-useless counts as weak, not as a pass. A CSP containing 'unsafe-inline' is scored down. An HSTS header with no max-age is scored as broken, because that is what browsers do with it.

How it works

This is the one tool on the site that needs a server, and it is worth being explicit about why. The Fetch standard only exposes a short safelist of response headers to cross-origin script: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma. Everything interesting is hidden unless the origin opts in with Access-Control-Expose-Headers, which almost nobody does. A browser-only header inspector can therefore only inspect sites that were already configured to be inspected, which is not a useful tool.

So the request runs in a Vercel Edge Function at /api/header-inspect. It performs a plain GET with no cookies and no credentials, follows up to ten redirects manually so it can record each hop’s status and Location, and gives up after ten seconds. It sees the URL you submit and nothing else. The header grading itself runs in your browser on the JSON that comes back.

Because that endpoint fetches a URL an anonymous user supplied, it is a textbook server-side request forgery target, and it is written accordingly. Only http and https are accepted. Credentials embedded in the URL are rejected. Loopback, RFC 1918 private space, carrier-grade NAT, link-local 169.254.0.0/16 — which is where cloud instance metadata lives — unique-local and link-local IPv6, and IPv4-mapped forms of all of the above are refused. So are single-label hostnames and .internal, .local, and .cluster.local suffixes. Critically, every redirect hop is re-validated, because a public URL is free to redirect somewhere private, and validating only the first URL is the most common way this class of endpoint gets broken.

The honest limitation

Address checks happen on the hostname, and the edge runtime cannot resolve DNS before fetch does. A hostname that is public in the registry but whose A record points at 127.0.0.1 will pass the string check. That is a real gap, mitigated by the fact that the endpoint returns only status lines and response headers, never a response body, and by the 30-requests-per-hour limit. If you are building something similar in an environment where you can resolve first, resolve the name, validate the resulting addresses, and connect to the validated address rather than the name. Otherwise the name is free to resolve differently between your check and your connection.

When to reach for it

Reach for it when you have just deployed a header change and want to confirm what the edge is actually serving rather than what your config says. Reach for it when a redirect chain has grown a hop nobody remembers adding, since each extra hop is a full round trip and httphttpswww → path is three of them. Reach for it when a cached asset will not update and you need to see whether the response even carries a validator, or whether an Age header proves you are talking to a cache instead of the origin. The relevant rules live in RFC 9111 for caching and RFC 6797 for HSTS.

Do not use it as a substitute for an audit. It reads what one anonymous request from one region received at one moment. Bot protection, geographic routing, and Vary-keyed caches all mean your own browser may legitimately see something else.

Frequently asked questions

Why does this need a server when the rest of your tools do not?+
Because the Fetch standard will not let script read cross-origin response headers. Without CORS, only a short safelist (Cache-Control, Content-Type, Expires, Last-Modified, Pragma) is exposed, and everything you actually want — Strict-Transport-Security, Content-Security-Policy, Set-Cookie — is invisible. A browser-only header inspector can only inspect sites that already opt in via Access-Control-Expose-Headers, which is almost none of them.
Why was my URL refused?+
The endpoint refuses anything that is not a globally routable http or https address: loopback (127.0.0.0/8, ::1), RFC 1918 private ranges, link-local 169.254.0.0/16 including the cloud metadata address, unique-local IPv6, single-label hostnames, and suffixes like .internal or .local. That is server-side request forgery protection: without it, anyone could use our edge function to probe our own network. Every redirect hop is re-validated for the same reason.
The results differ from what my browser shows. Why?+
Our edge function is a different client from a different network, so anything that varies by request will vary here. Geographic routing, CDN point of presence, bot protection, User-Agent sniffing, and Vary-keyed caching all produce legitimately different responses. Watch for an Age header, which proves you were served from a shared cache rather than the origin. Cookie-gated pages will show you the logged-out response, because no cookies are sent.
How is the security grade calculated?+
Six headers are scored two points for a solid configuration, one for present-but-weak, zero for missing: HSTS, CSP, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and X-Frame-Options. Caching headers are graded too but the checks are advisory. The letter is a rough conversation-starter, not an audit — a CSP with 'unsafe-inline' is downgraded but a nonce-based policy with a sloppy allowlist can still score well.
Why is my HSTS header being ignored?+
Two common reasons. First, browsers only honour Strict-Transport-Security when it arrives over a valid TLS connection, so sending it over plain http does nothing. Second, max-age is mandatory — a header without a parseable max-age is discarded entirely, per RFC 6797 §6.1. For preload eligibility you also need at least max-age=31536000 plus includeSubDomains and preload.