GET says "give me this." HEAD says "tell me about this."
That's the entire difference. HEAD requests the same response as GET—same status code, same headers—but the server withholds the body. You learn everything about a resource without receiving it.
What HEAD Does
GET Request:
GET /large-video.mp4 HTTP/1.1
Host: example.com
→
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 524288000
Last-Modified: Wed, 15 May 2024 10:00:00 GMT
[524 MB of video data]
HEAD Request:
HEAD /large-video.mp4 HTTP/1.1
Host: example.com
→
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 524288000
Last-Modified: Wed, 15 May 2024 10:00:00 GMT
[No body]
You learn the file is 524 MB, it's an MP4, and when it was last modified—without downloading a single byte of video.
When to Use HEAD
HEAD shines when you need to know about resources without retrieving them:
Checking if Resources Exist:
HEAD /api/users/johndoe HTTP/1.1
→ 200 OK (user exists)
→ 404 Not Found (user doesn't exist)
Testing Links: Web crawlers and link checkers verify links are valid without downloading full pages:
HEAD /downloads/large-file.zip
→ Content-Length: 1073741824
// 1 GB—ask user before downloading
Verifying Resource Freshness:
HEAD /data/report.pdf
→ Last-Modified: Wed, 15 May 2024 10:00:00 GMT
→ ETag: "abc123"
// Compare with cached version
Determining Content Types:
HEAD /file-without-extension
→ Content-Type: application/pdf
// Now we know it's a PDF
Checking Download Availability:
HEAD /exports/data-export-12345.csv
→ 200 OK (ready)
→ 202 Accepted (still processing)
→ 404 Not Found (doesn't exist)
HEAD and Caching
HEAD requests follow the same caching rules as GET. Browsers and proxies cache HEAD responses and can return them without contacting the origin server.
This makes HEAD useful for cache validation:
HEAD /api/data HTTP/1.1
If-Modified-Since: Wed, 15 May 2024 10:00:00 GMT
→
HTTP/1.1 304 Not Modified
// Cache is still fresh, no download needed
Implementation Requirements
Servers should support HEAD for any resource that supports GET. The HTTP specification requires HEAD to return the same headers that GET would return.
This means servers execute the same logic for HEAD as GET:
Retrieve only metadata—don't generate full content just to discard it.
HEAD vs. GET for Existence Checks
HEAD Advantages:
Saves bandwidth (no body transferred)
Faster for large resources
Expresses intent clearly ("just checking")
GET Advantages:
You'll likely need the content anyway
Single request instead of HEAD then GET
Some servers don't support HEAD properly
General guideline: Use HEAD when you're only checking existence or metadata. Use GET when you'll need the content if it exists.
// Check if large file exists before downloadingconst headResponse = awaitfetch(url, { method: 'HEAD' });
if (headResponse.ok) {
const size = headResponse.headers.get('Content-Length');
if (confirm(`Download ${formatBytes(size)}?`)) {
const getResponse = awaitfetch(url);
// Download file
}
}
Security Considerations
HEAD requests can leak information. Apply the same protections as GET:
Directory Listing Prevention: If you block directory listing for GET, block it for HEAD too.
Authentication Required: HEAD should enforce the same authentication and authorization as GET:
HEAD /users/123/private-data
→ 401 Unauthorized (if not authenticated)
→ 403 Forbidden (if authenticated but not authorized)
Information Disclosure: Headers can reveal sensitive information:
HEAD /internal-document.pdf
→ Last-Modified: shows when document was updated
→ Content-Length: shows document size
→ X-Author: might reveal author's name
Ensure HEAD responses don't leak information that GET wouldn't also expose.
Link Checking at Scale
Web crawlers and link checkers use HEAD to validate links efficiently:
This validates thousands of links far faster than GET requests would allow.
Server Support Inconsistencies
While servers should support HEAD for any GET endpoint, reality is messier:
Some servers return 405 Method Not Allowed
Some return different headers than GET
Some frameworks don't automatically convert GET handlers to HEAD
Client workaround:
asyncfunctionheadRequest(url) {
try {
returnawaitfetch(url, { method: 'HEAD' });
} catch (error) {
// Fall back to GET if HEAD failsreturnawaitfetch(url);
}
}
HEAD and Range Requests
HEAD works with Range headers to check if a server supports partial content:
HEAD /large-file.zip HTTP/1.1
Range: bytes=0-0
→
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Length: 1
Content-Range: bytes 0-0/1073741824
// Server supports range requests, total size is 1 GB
Download managers use this to determine if parallel chunk downloads are possible.
HEAD in APIs
RESTful APIs should support HEAD for resource endpoints:
HEAD /api/v1/users/12345
→ 200 OK (user exists)
→ 404 Not Found (user doesn't exist)
HEAD /api/v1/articles/latest
→ Last-Modified: Wed, 15 May 2024 10:00:00 GMT
→ ETag: "abc123"
Some APIs use HEAD for health checks:
HEAD /health
→ 200 OK (service is healthy)
→ 503 Service Unavailable (service is down)
Performance Patterns
Conditional GET: Check freshness before downloading: