What it does
HEAD is GET's quieter sibling: same request, same response headers, same status code — but the server never sends a response body. You get all the metadata about a resource (size, type, last-modified date, whether it exists at all) without downloading the actual content.
The spec requires servers to respond to HEAD exactly as they would to GET, minus the body. If a header would say Content-Length: 50000 on a GET, it says the same thing on a HEAD — even though nothing is actually transferred.
Semantics
| Property | Value | Why it matters |
|---|---|---|
| Safe | Yes | Read-only, same as GET |
| Idempotent | Yes | Repeating it changes nothing |
| Cacheable | Yes | Same caching rules as GET |
| Request body | No | Never sends one |
| Response body | No | This is the entire point of HEAD |
Syntax & example request
HEAD /files/report.pdf HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 4582931
Last-Modified: Tue, 03 Jun 2026 10:15:00 GMT
No body follows — the response ends after the headers.
curl example
curl -I https://example.com/files/report.pdf
-I tells curl to send a HEAD request and print just the response headers.
Common status codes returned
- 200 OK — resource exists, here are its headers → see 200
- 304 Not Modified — conditional HEAD, still valid → see 304
- 404 Not Found — resource doesn't exist → see 404
HEAD vs GET vs OPTIONS
| HEAD | GET | OPTIONS | |
|---|---|---|---|
| Returns a body | No | Yes | Sometimes (small) |
| Purpose | Metadata about a resource | Fetch the resource | Discover allowed methods/capabilities |
| Same headers as GET | Yes, identical | — | No |
HEAD tells you about this specific resource. OPTIONS tells you what you're allowed to do with a resource (or the server in general).
Real-world usage
- Checking if a file exists before downloading it (avoids wasting bandwidth on a 404)
- Getting
Content-Lengthto show a download-size estimate or progress bar before starting the actual GET - Link checkers and uptime monitors verifying a URL resolves without pulling the full page
- Cache validation — checking
Last-Modified/ETagwithout re-fetching the body
Security considerations
HEAD leaks the same information a GET would (headers, existence of a resource, size) just without the body — so anything you'd protect on a GET (auth-gated resources, rate limiting) needs the same protection on HEAD. A common misconfiguration is guarding GET routes with auth middleware but forgetting the framework auto-generates a HEAD route that bypasses it.
FAQ
Do servers have to implement HEAD separately from GET?
Not really — most frameworks generate the HEAD response automatically from the GET handler, just stripping the body before sending. You rarely write HEAD logic by hand.
Why use HEAD instead of GET with a small Range request?
They solve different problems. Range still initiates a real (partial) content transfer. HEAD transfers zero bytes of body — it's the cheapest possible way to ask "does this exist, and what would I get if I asked for it?"
Can HEAD have a different Content-Length than the matching GET would?
It shouldn't. RFC 9110 requires the headers to match what a GET would return, precisely so clients can trust HEAD metadata without a follow-up GET.
Fun fact
HEAD predates most of the web's caching infrastructure — it was one of the three original methods in HTTP/1.0 (1996), alongside GET and POST, specifically so early clients could check "has this changed?" before the concept of conditional GET (If-Modified-Since) was fully worked out.