Most of what a URL tells you never shows up in the browser window. The status code, the redirect hops, the caching rules, the security policy: all of it lives in the response headers, and the page renders the same whether they are set correctly or not. When something breaks, the headers are usually where the answer is hiding.
So let's look at how to read them. I'll cover the fast command-line way, what each header block actually means, and how to check headers at scale when one curl command is not enough.
The One-Line Check
The quickest header inspection lives in a tool you already have. curl -I sends a HEAD request and prints only the response headers:
curl -I https://example.com
HTTP/2 200
content-type: text/html; charset=UTF-8
cache-control: max-age=604800
content-length: 1256
date: Sat, 19 Jul 2026 10:14:00 GMT
If you want the request headers you sent plus the response headers you got back, add verbose mode and discard the body:
curl -sv https://example.com -o /dev/null
The -o /dev/null throws away the page content so your terminal only shows the exchange. That single trick makes curl a usable header checker without any extra install.
Following the Redirect Chain
A single status code rarely tells the whole story. Type example.com and you might travel through three hops before landing on a real page: HTTP to HTTPS, apex to www, old path to new path. Each hop is its own status code and its own Location header.
To watch every step, tell curl to follow redirects and dump the headers at each stop:
curl -sIL https://example.com | grep -iE "^(HTTP|location)"
HTTP/2 301
location: https://www.example.com/
HTTP/2 200
The -L flag follows the chain, -I keeps it to headers, and the grep pulls out just the status lines and Location targets so you can read the path at a glance. This is the single most useful check I run when a page "works" but ranks badly or loads slowly. A 302 where you expected a 301, or a redirect loop that quietly caps out, shows up here immediately.
A few things to watch for in a chain:
- 301 vs 302. A 301 is permanent and passes ranking signals. A 302 is temporary and often leaks link equity when it should not.
- Mixed schemes. An HTTPS page redirecting to HTTP, even for one hop, is a real security and SEO problem.
- Hop count. Every redirect adds a round trip. Three or more hops to reach the final URL is worth fixing.
If a hop returns something odd like a 429 too many requests or a Cloudflare 520, the header dump tells you exactly which URL in the chain is the culprit rather than leaving you guessing.
Reading the Headers That Matter
Once you have the raw block, most of it falls into a few buckets. Here is a field guide to the ones worth reading.
| Header | What it tells you |
|---|---|
content-type |
The format and charset. A wrong charset is behind most mojibake bugs. |
cache-control |
How long caches may keep the response. no-store vs max-age=604800 is a huge performance difference. |
etag / last-modified |
Validators for conditional requests. Present means the server supports cheap revalidation. |
content-encoding |
gzip or br means the body is compressed in transit. |
set-cookie |
Session and tracking cookies. Check the Secure, HttpOnly, and SameSite flags here. |
server / x-powered-by |
Backend fingerprints. Often worth removing so you leak less about your stack. |
The Security Header Block
Security headers are the ones people forget to set and attackers love to find missing. A quick audit:
curl -sI https://example.com | grep -iE "strict-transport|content-security|x-frame|x-content-type|referrer-policy"
strict-transport-security(HSTS) forces browsers to use HTTPS. Missing it means a user's first request can be downgraded.content-security-policylimits where scripts and assets may load from. The single strongest defense against cross-site scripting, and the most commonly absent.x-frame-optionsor the CSPframe-ancestorsdirective stops clickjacking by controlling who can iframe you.x-content-type-options: nosniffstops browsers from guessing content types, which closes a class of injection tricks.referrer-policycontrols how much of your URL leaks to the sites you link out to.
If a domain returns a 200 with none of these set, that is a finding worth writing down. Remember that a healthy status code says nothing about whether the response is safe or even real: as I covered in what a 200 actually guarantees, the status line and the header block answer very different questions.
When One curl Command Is Not Enough
The command line is perfect for checking one URL by hand. It gets old fast when you need to check headers across hundreds of pages, run the audit from a scheduled job, or pull results into a spreadsheet.
Two problems show up at scale. First, you have to parse text output yourself, which is fiddly and brittle. Second, and more annoying, a lot of sites return different headers to a bare curl user agent than they do to a real browser. Some block the request outright, some serve a challenge page, and your audit silently records the wrong thing.
This is where a fetch API earns its place. link.sc fetches the URL the way a real browser would, follows the redirect chain, and hands back the full response metadata alongside the content. You get the status code, the resolved final URL, and the raw headers as structured data instead of terminal text you have to scrape:
curl -X POST https://api.link.sc/v1/fetch \
-H "x-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "format": "markdown"}'
{
"status": 200,
"finalUrl": "https://www.example.com/",
"headers": {
"content-type": "text/html; charset=UTF-8",
"cache-control": "max-age=604800",
"strict-transport-security": "max-age=31536000"
},
"redirectChain": [
{ "status": 301, "url": "https://example.com/" }
]
}
Because it runs through real browser infrastructure, you also sidestep the "curl gets blocked but the browser does not" trap. The headers you see are the headers a genuine visitor sees, which is the whole point of an audit.
A Practical Header-Audit Routine
Here is the checklist I run on any domain I am debugging or reviewing:
- Status and final URL. Did I land where I expected, in as few hops as possible.
- Redirect scheme integrity. No HTTPS to HTTP downgrades anywhere in the chain.
- Security block present. HSTS, CSP, and
nosniffat minimum. - Caching sane. Static assets cached, HTML with a deliberate policy rather than an accidental one.
- No stack leakage.
serverandx-powered-bytrimmed down.
Run it by hand with curl when you are chasing one bug. Wire it into an API when you want it to run on a schedule across every page you own, and to run from the same vantage point a real user would.
Headers are the layer of the web that machines read and humans ignore. Learn to read them and most "mystery" bugs stop being mysteries.
Want header, status, and redirect data as clean structured JSON instead of terminal guesswork? Start free on link.sc: 500 requests a month, no card required.