- Home
- HTTP Methods
- HTTP HEAD Method
Method
HTTP HEAD Method
Learn how HTTP HEAD requests retrieve resource metadata (headers) without downloading the body. Useful for checking existence, size, and modification dates.
What is HEAD?
TL;DR: HEAD gets resource metadata (headers) without downloading the actual content. Use it to check file sizes, validate links, or test if resources exist.
HEAD is like GET’s efficient twin—it retrieves all the same headers and metadata about a resource, but without the actual content body. Think of it like checking a book’s title page and table of contents without reading the entire book.
HEAD is perfect when you need to know about a resource (size, type, last modified date) but don’t need the actual data.
Key Characteristics
1. Safe
HEAD requests don’t change anything on the server. They’re read-only operations, just like GET.
2. Idempotent
Making the same HEAD request multiple times produces the same result with no side effects.
3. No Response Body
The server returns the same headers as a GET request would, but with an empty body. This makes HEAD requests much faster and uses less bandwidth.
4. Same Headers as GET
HEAD responses include all the same headers that a GET request to the same URL would return—Content-Type, Content-Length, Last-Modified, etc.
How HEAD Works
1. Client requests resource metadata:
HEAD /posts/42 HTTP/1.1
Host: api.example.com
Accept: application/json
```http
**2. Server responds with headers only:**
```http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1247
Last-Modified: Wed, 18 Jan 2026 10:30:00 GMT
ETag: "abc123def456"
Cache-Control: max-age=3600
[No body content]
Notice: Same headers as GET, but no actual post content in the response body.
Real-World Examples
Example 1: Checking File Size Before Download
HEAD /files/large-video.mp4 HTTP/1.1
Host: cdn.example.com
```http
**Response:**
```http
HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 104857600
Last-Modified: Wed, 18 Jan 2026 09:15:00 GMT
Accept-Ranges: bytes
Now you know the file is 100MB before deciding to download it!
Example 2: Cache Validation
HEAD /api/posts/42 HTTP/1.1
Host: api.example.com
If-None-Match: "abc123"
```text
**Response (not modified):**
```http
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600
Example 3: Checking Resource Existence
HEAD /users/jane-doe HTTP/1.1
Host: api.example.com
```text
**Response (exists):**
```http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 342
Response (doesn’t exist):
HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 45
```text
### Example 4: Getting Image Metadata
```http
HEAD /images/profile-pic.jpg HTTP/1.1
Host: cdn.example.com
Response:
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 245760
Last-Modified: Tue, 17 Jan 2026 14:22:00 GMT
```text
### Example 5: Checking API Endpoint
```http
HEAD /api/v2/health HTTP/1.1
Host: service.example.com
Response:
HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: 2.1.0
X-Rate-Limit-Remaining: 999
```text
## When to Use HEAD
**✅ Use HEAD for:**
- Checking if a resource exists without downloading it
- Getting file size before downloading large files
- Cache validation (checking if content changed)
- Testing API endpoints and connectivity
- Getting metadata about images, videos, or documents
- Checking last modified dates
- Bandwidth-efficient resource monitoring
**❌ Don't use HEAD for:**
- When you actually need the resource content (use GET)
- Creating resources (use POST)
- Updating resources (use PUT or PATCH)
- Deleting resources (use DELETE)
## HEAD vs GET
| Feature | HEAD | GET |
| ------------------- | ------------- | ----------------------- |
| **Response body** | No | Yes |
| **Headers** | Same as GET | Full headers |
| **Bandwidth usage** | Minimal | Full content size |
| **Speed** | Fast | Depends on content size |
| **Use case** | Metadata only | Full resource |
| **Safe** | Yes | Yes |
| **Idempotent** | Yes | Yes |
### Bandwidth Comparison
**GET request for 1MB image:**
```http
Request: ~200 bytes
Response: ~1MB + headers
Total: ~1MB
```text
**HEAD request for same image:**
```http
Request: ~200 bytes
Response: ~500 bytes (headers only)
Total: ~700 bytes
```http
HEAD uses 99.9% less bandwidth!
## Common Use Cases
### 1. Pre-flight File Checks
```javascript
// Check file size before downloading
async function downloadIfSmall(url, maxSize) {
const headResponse = await fetch(url, { method: 'HEAD' })
const fileSize = parseInt(headResponse.headers.get('content-length'))
if (fileSize > maxSize) {
throw new Error(`File too large: ${fileSize} bytes`)
}
// Now download the actual file
return fetch(url)
}
2. Cache Validation
// Check if cached content is still valid
async function isCacheValid(url, cachedETag) {
const headResponse = await fetch(url, { method: 'HEAD' })
const currentETag = headResponse.headers.get('etag')
return currentETag === cachedETag
}
```javascript
### 3. Resource Monitoring
```javascript
// Monitor API health without downloading data
async function checkEndpointHealth(url) {
try {
const response = await fetch(url, { method: 'HEAD' })
return {
status: response.status,
healthy: response.ok,
responseTime: Date.now() - startTime
}
} catch (error) {
return { healthy: false, error: error.message }
}
}
4. Link Validation
// Check if links are valid without downloading pages
async function validateLinks(urls) {
const results = await Promise.all(
urls.map(async (url) => {
const response = await fetch(url, { method: 'HEAD' })
return {
url,
valid: response.ok,
status: response.status
}
})
)
return results
}
```text
## Common Response Codes
| Code | Meaning | When Used |
| ------- | ------------------ | ------------------------------------------ |
| **200** | OK | Resource exists and is accessible |
| **304** | Not Modified | Resource hasn't changed (cache validation) |
| **400** | Bad Request | Invalid request format |
| **401** | Unauthorized | Authentication required |
| **403** | Forbidden | Access denied |
| **404** | Not Found | Resource doesn't exist |
| **405** | Method Not Allowed | HEAD not supported for this resource |
| **500** | Server Error | Server problem |
## Important Headers in HEAD Responses
### Content Headers
```http
Content-Type: application/json
Content-Length: 1247
Content-Encoding: gzip
Caching Headers
Last-Modified: Wed, 18 Jan 2026 10:30:00 GMT
ETag: "abc123def456"
Cache-Control: max-age=3600
Expires: Wed, 18 Jan 2026 11:30:00 GMT
```http
### Custom Headers
```http
X-API-Version: 2.1.0
X-Rate-Limit-Remaining: 999
X-Content-Source: cdn
Best Practices
1. Support HEAD for all GET endpoints
// Server should handle HEAD for any GET route
app.get('/posts/:id', (req, res) => {
const post = findPost(req.params.id)
if (!post) return res.status(404).json({ error: 'Not found' })
res.json(post)
})
// HEAD automatically supported by most frameworks
// But you can handle it explicitly:
app.head('/posts/:id', (req, res) => {
const post = findPost(req.params.id)
if (!post) return res.status(404).end()
res.set('Content-Type', 'application/json')
res.set('Content-Length', JSON.stringify(post).length)
res.end()
})
```javascript
**2. Use HEAD for efficient monitoring**
```javascript
// Monitor multiple services efficiently
const services = [
'https://api.service1.com/health',
'https://api.service2.com/health',
'https://api.service3.com/health'
]
const healthChecks = await Promise.all(services.map((url) => fetch(url, { method: 'HEAD' })))
3. Implement proper caching headers
app.head('/files/:filename', (req, res) => {
const file = findFile(req.params.filename)
if (!file) return res.status(404).end()
res.set({
'Content-Type': file.mimeType,
'Content-Length': file.size,
'Last-Modified': file.modifiedAt.toUTCString(),
ETag: file.etag,
'Cache-Control': 'max-age=86400'
})
res.end()
})
Try It Yourself
Visit our request builder to experiment with HEAD:
- Select HEAD as the method
- Try path /posts/1 to get post metadata
- Compare with a GET request to the same URL
- Notice how HEAD is much faster and uses less data!
Related Methods
- GET - Retrieve full resources
- OPTIONS - Check allowed methods
- POST - Create new resources
- PUT - Replace resources
Related Concepts
- Caching - Using HEAD for cache validation
- Status Codes - Understanding response codes
- Headers - Request and response headers
In Practice
Express.js
// Express handles HEAD automatically for GET routes.
// Define GET — HEAD is served for free.
app.get('/files/:id', async (req, res) => {
const file = await storage.stat(req.params.id)
if (!file) return res.status(404).end()
res.set({
'Content-Length': file.size,
'Content-Type': file.mimeType,
'Last-Modified': file.updatedAt.toUTCString(),
'ETag': file.etag
})
// res.json() sends body for GET; HEAD strips it automatically
res.json(file)
})Next.js App Router
// app/api/files/[id]/route.ts
// Export HEAD handler explicitly
export async function HEAD(
_req: Request,
{ params }: { params: { id: string } }
) {
const file = await storage.stat(params.id)
if (!file) return new Response(null, { status: 404 })
return new Response(null, {
headers: {
'Content-Length': String(file.size),
'Content-Type': file.mimeType,
'ETag': file.etag,
'Last-Modified': file.updatedAt.toUTCString()
}
})
}Go net/http
// HEAD is handled automatically when you register a GET handler.
// To handle HEAD explicitly:
http.HandleFunc("/files/", func(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/files/")
file, err := storage.Stat(id)
if err != nil {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10))
w.Header().Set("Content-Type", file.MimeType)
w.Header().Set("ETag", file.ETag)
// For HEAD, WriteHeader without body
if r.Method == http.MethodHead {
w.WriteHeader(http.StatusOK)
return
}
json.NewEncoder(w).Encode(file)
})Frequently Asked Questions
What is the HTTP HEAD method?
HEAD is identical to GET but returns only headers, no body. Use it to check if a resource exists, get its size, or check modification time without downloading content.
When should I use HEAD instead of GET?
Use HEAD to check resource metadata without downloading: verify links exist, check Content-Length before download, validate cache with ETag/Last-Modified.
Does HEAD return the same headers as GET?
Yes, HEAD must return the same headers GET would return, including Content-Length and Content-Type. Only the body is omitted.
Is HEAD safe and idempotent?
Yes, HEAD is both safe (no side effects) and idempotent (same result on repeat). It should never modify server state.