Questions
How do I check the HTTP status code of a page?
`curl -sI https://example.com/page` prints the status line and headers without downloading the body. Add `-L` to follow redirects and see every hop in order. A browser will not tell you directly — it shows the final page after following redirects, so the status of the URL you actually typed is only visible in the Network panel or on the command line.
The command, and what each flag does
`curl -I https://example.com/page` sends a HEAD request and prints the response headers, including the status line.
`-s` silences the progress meter, which otherwise clutters the output when you are reading it rather than piping it.
`-L` follows redirects and prints the headers of every hop, which is the only way to see a chain rather than its destination.
Some servers answer HEAD differently from GET, or refuse it entirely. `curl -sSD - -o /dev/null https://example.com/page` performs a real GET and discards the body, which is the safer form when a HEAD looks wrong.
`-w '%{http_code}\n'` prints just the number, which is what you want inside a script.
`--max-redirs 5` caps how far `-L` will follow, which turns an unbounded loop into a clear error instead of a hang.
Reading a redirect chain
`curl -sIL https://example.com/page | grep -iE '^(HTTP|location)'` prints the status and target of each hop in order.
Each pair of lines is one step. A single 301 followed by a 200 is a normal move; four hops before a 200 is a chain worth shortening.
The same URL appearing twice in the output is a loop, and the client will give up rather than reach anything.
Watch the scheme on each hop. A cycle between `http` and `https` on the same path is almost always a proxy not forwarding the original protocol to the application behind it.
Note where the hop count crosses three. Each one is a full round trip before the page starts loading, and on a mobile connection that is measured in hundreds of milliseconds.
`curl -w '%{num_redirects} %{url_effective}\n' -o /dev/null -sL <url>` prints the hop count and the final address in one line, which is the compact form for checking many URLs.
Why the browser is the wrong tool for this
The address bar shows where you ended up, not what the URL you typed returned. A 301 is invisible once it has been followed.
The Network panel does show it: the status column lists every request, including the redirect hops, in order.
Browsers cache 301 responses, sometimes for a long time, so a redirect you removed on the server can persist in your own browser and look like it is still configured.
Testing in a private window sidesteps that cache, which is why a redirect that appears stuck for you and works for everyone else is usually your own cache.
The codes worth recognising on sight
`200` served normally. `204` succeeded with nothing to return. `206` answered a range request, such as a video seek.
`301` and `308` permanent moves; `302` and `307` temporary ones; `304` told the client its cached copy is still valid.
`401` not authenticated, `403` refused regardless, `404` no such resource, `410` deliberately removed, `429` rate limited.
`500` the application failed, `502` a bad answer from an upstream, `503` temporarily unavailable, `504` an upstream timed out.
The first digit is the class, and it is enough to decide who to ask: 4xx points at the request, 5xx at the server.
`000` from curl is not a status at all — it means no HTTP response arrived, so the fault is DNS, TCP, TLS or a timeout rather than anything the server said.
Why the answer can differ per client
Bot filters and firewalls classify requests by user agent, TLS fingerprint and header shape, and refuse the ones that look automated.
A default `curl/` user agent is the easiest thing in the world to filter, so comparing it against `curl -A 'Mozilla/5.0' ...` identifies a filter in one step.
Geographic routing, rate limits and authentication all change the answer for the same URL.
So a status observed once is evidence about that request, not a property of the URL. Where it matters, check from more than one client.
Checking more than one URL
A crawl answers the question across a whole site rather than one address at a time, which is where the useful patterns appear.
VeriFixScan reports `availability.status_classification` for the entry URL and `availability.broken_links` for every link it followed, with the status each one returned.
`transport.redirect_chain` reports the hops, so a chain is visible as a chain rather than as a working link.
The finding that matters is rarely a single bad status. It is a pattern: one template producing 404s, or a whole section answering 403 to anything automated.
Frequently asked questions
- Why does my browser not show the status code?
- It shows the final page after following redirects. The Network panel lists every request with its status, including the hops.
- Should I use HEAD or GET to check a status?
- HEAD is cheaper, and some servers answer it differently or refuse it. When a HEAD result looks wrong, repeat it as a GET that discards the body.
- Why does curl get a different status than my browser?
- Usually a bot filter reacting to the user agent or the request shape. Repeating the request with a browser user agent identifies it immediately.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website