HTTP Headers for REST APIs
Complete reference for request headers, response headers, caching validators, security headers, and CORS — defined in RFC 9110 (HTTP Semantics).
What Are HTTP Headers?
HTTP headers are key-value pairs sent at the beginning of every HTTP request and response. They carry metadata: what format the body is in, who is making the request, what the client accepts, how long to cache the response, and security directives. They are defined in RFC 9110, Section 5.
POST /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGci...
Idempotency-Key: a4e3f2b1-9c8d-4e6f-b2a1
{ "product_id": 42, "quantity": 2 }
---
HTTP/1.1 201 Created
Content-Type: application/json
Location: /orders/98765
ETag: "v1-a3f9c2b4"
Cache-Control: no-store
{ "id": 98765, "status": "pending" }
Header Categories
- Request headers: Sent by the client —
Authorization,Accept,Content-Type,If-None-Match - Response headers: Sent by the server —
Content-Type,ETag,Location,Retry-After - Representation headers: Describe the body —
Content-Type,Content-Encoding,Content-Language - Caching headers: Control caching behavior —
Cache-Control,ETag,Last-Modified - Security headers: Browser and transport security —
Strict-Transport-Security,Content-Security-Policy - CORS headers: Cross-origin resource sharing —
Access-Control-Allow-Origin
Essential Request Headers
Authorization
Carries credentials for authenticating the request. The most common scheme in REST APIs is Bearer (JWT or OAuth 2.0 token). Defined in RFC 9110 §11.6.2.
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQyIn0...
Authorization: Basic dXNlcjpwYXNzd29yZA== # base64(user:password)
Authorization: ApiKey your-api-key-here
Content-Type
Tells the server what format the request body is in. Required on POST, PUT, and PATCH requests. Defined in RFC 9110 §8.3.
Content-Type: application/json # JSON body (most common in REST)
Content-Type: application/x-www-form-urlencoded # HTML form data
Content-Type: multipart/form-data # File uploads
Content-Type: application/json; charset=utf-8 # JSON with explicit encoding
Accept
Tells the server what media type(s) the client can handle in the response. The server uses this for content negotiation. Defined in RFC 9110 §12.5.1.
Accept: application/json
Accept: application/json, application/xml;q=0.9, */*;q=0.8
Accept: application/vnd.api+json # JSON:API content type
The q parameter is a quality factor (0–1) indicating preference. */* means "anything".
Accept-Encoding
Tells the server which compression algorithms the client supports. The server can compress the response body to save bandwidth.
Accept-Encoding: gzip, deflate, br
# Server responds with:
Content-Encoding: gzip
If-None-Match
Sends a previously received ETag value to check if the resource has changed. If unchanged, the server returns 304 Not Modified with no body — saving bandwidth. Defined in RFC 9110 §13.1.2.
# First request
GET /users/42
→ 200 OK
ETag: "v3-a3f9c2b4"
# Subsequent request
GET /users/42
If-None-Match: "v3-a3f9c2b4"
→ 304 Not Modified (no body, saves bandwidth)
If-Modified-Since
Time-based cache validation. Returns the full response only if the resource has been modified since the given date. Used with the Last-Modified response header.
If-Modified-Since: Wed, 22 Apr 2026 10:00:00 GMT
→ 304 Not Modified (if resource unchanged)
→ 200 OK (if resource has changed, with new body)
If-Match
Conditional write — only apply the update if the ETag matches. Prevents lost update problems (optimistic concurrency). If the ETag does not match, returns 412 Precondition Failed.
PUT /users/42
If-Match: "v3-a3f9c2b4"
{ "name": "Alice Smith" }
→ 200 OK (if ETag matches — update applied)
→ 412 Precondition Failed (if someone else modified the resource first)
Prefer
Client hints to the server about preferred behavior. Not universally supported but increasingly adopted (RFC 7240).
Prefer: return=minimal # Return 204 No Content instead of the full resource
Prefer: return=representation # Return the full resource after POST/PUT
Prefer: respond-async # Process asynchronously, return 202 Accepted
Essential Response Headers
Content-Type
Tells the client what format the response body is in. Always set this on responses with a body.
Content-Type: application/json
Content-Type: application/problem+json # RFC 7807 error responses
Content-Type: text/html; charset=utf-8
Location
On a 201 Created response, points to the URL of the newly created resource. On a 3xx redirect, points to the redirect target. Defined in RFC 9110 §10.2.2.
HTTP/1.1 201 Created
Location: /orders/98765
ETag
A version identifier for the resource representation. Can be a hash of the content, a version number, or a timestamp. Strong ETags ("abc123") require byte-for-byte equality; weak ETags (W/"abc123") indicate semantic equivalence.
ETag: "v5-3a9f2c1b" # Strong ETag
ETag: W/"v5-3a9f2c1b" # Weak ETag
Last-Modified
The date and time the resource was last modified. Used with If-Modified-Since for time-based cache validation.
Last-Modified: Wed, 22 Apr 2026 10:00:00 GMT
Cache-Control
Controls caching behavior at every level (browser, CDN, proxy). One of the most important headers for REST API performance. See our Caching Guide for the full breakdown.
Cache-Control: no-store # Never cache (auth endpoints, mutations)
Cache-Control: no-cache # Revalidate before using cache
Cache-Control: public, max-age=3600 # Cache for 1 hour (CDN + browser)
Cache-Control: private, max-age=300 # Browser only, 5 minutes
Cache-Control: public, max-age=86400, immutable # Static assets (never changes)
Retry-After
On a 429 Too Many Requests or 503 Service Unavailable response, tells the client how long to wait before retrying.
Retry-After: 60 # Wait 60 seconds
Retry-After: Wed, 22 Apr 2026 11:00:00 GMT # Retry after this date
Rate Limit Headers
Non-standard but widely adopted headers for communicating rate limit status. See our Rate Limiting Guide.
X-RateLimit-Limit: 1000 # Requests allowed per window
X-RateLimit-Remaining: 842 # Requests left in current window
X-RateLimit-Reset: 1714003600 # Unix timestamp when window resets
Link
Conveys related resources and pagination links. Widely used for paginated APIs (GitHub, GitLab style). Defined in RFC 8288.
Link: </users?page=3>; rel="next",
</users?page=1>; rel="prev",
</users?page=10>; rel="last"
Caching Validators: ETag vs Last-Modified
RFC 9110 §8.8 defines two types of validators for conditional HTTP requests:
| Aspect | ETag | Last-Modified |
|---|---|---|
| Type | Opaque string (hash/version) | HTTP-date timestamp |
| Precision | Exact — any change = new ETag | 1-second resolution only |
| Request header | If-None-Match / If-Match |
If-Modified-Since / If-Unmodified-Since |
| Best for | Any resource; prefer over Last-Modified | Legacy systems; static files |
| Concurrency control | Yes (optimistic locking with If-Match) |
Limited (timestamp collisions) |
Node.js: Generating ETags
const crypto = require('crypto');
function generateETag(data) {
return '"' + crypto
.createHash('md5')
.update(JSON.stringify(data))
.digest('hex')
.substring(0, 16) + '"';
}
app.get('/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
const etag = generateETag(user);
// Check If-None-Match
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
res.set('ETag', etag);
res.set('Cache-Control', 'private, max-age=60');
res.json(user);
});
Security Headers
Security headers protect against common web vulnerabilities. While some are more relevant to browser-consumed APIs, all public APIs should implement them. See our Security Guide for the full OWASP coverage.
| Header | Purpose | Recommended Value |
|---|---|---|
Strict-Transport-Security |
Force HTTPS (HSTS) | max-age=31536000; includeSubDomains |
X-Content-Type-Options |
Prevent MIME sniffing | nosniff |
X-Frame-Options |
Prevent clickjacking | DENY |
Content-Security-Policy |
Prevent XSS, injection | default-src 'self' |
Referrer-Policy |
Control referrer leakage | strict-origin-when-cross-origin |
Permissions-Policy |
Restrict browser features | camera=(), microphone=() |
CORS Headers
Cross-Origin Resource Sharing headers control which origins can access your API from a browser. CORS is enforced by browsers — it does not protect server-to-server requests.
Response Headers (Server → Browser)
Access-Control-Allow-Origin: https://app.example.com # Specific origin (recommended)
Access-Control-Allow-Origin: * # Any origin (no credentials)
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
Access-Control-Expose-Headers: X-RateLimit-Remaining, ETag
Access-Control-Allow-Credentials: true # Allow cookies/auth headers
Access-Control-Max-Age: 86400 # Cache preflight for 24 hours
Handling Preflight Requests
// Express.js CORS middleware
app.options('*', (req, res) => {
res.set({
'Access-Control-Allow-Origin': req.headers.origin || '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
});
res.status(204).end(); // No content for OPTIONS
});
app.use((req, res, next) => {
res.set('Access-Control-Allow-Origin', req.headers.origin || '*');
next();
});
Security note: Never combine Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true. Browsers will reject this and it is a misconfiguration that bypasses cookie-based authentication protections.
Custom and Vendor Headers
Custom headers used to be prefixed with X- (e.g., X-Request-ID). RFC 6648 (2012) deprecated this convention — the X- prefix no longer carries meaning. Modern custom headers are simply named descriptively:
# Request tracing
Request-ID: 550e8400-e29b-41d4-a716-446655440000
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000 # Still widely used
# API versioning via header
API-Version: 2
Accept: application/vnd.myapi.v2+json
# Idempotency
Idempotency-Key: a4e3f2b1-9c8d-4e6f-b2a1-8d7c9e3f4b2a
# Forwarded IP (set by proxies/load balancers)
X-Forwarded-For: 203.0.113.42
X-Real-IP: 203.0.113.42
# Deprecation warning (RFC 8594)
Deprecation: Wed, 01 Jan 2027 00:00:00 GMT
Sunset: Wed, 01 Jan 2027 00:00:00 GMT
Link: </v2/users>; rel="successor-version"
Deprecation Headers
RFC 8594 defines the Sunset header for communicating API deprecation timelines. The Deprecation header (draft RFC) complements it. Clients can monitor these headers to detect upcoming breaking changes:
# On all responses from a deprecated endpoint:
Deprecation: true
Sunset: Wed, 01 Jan 2027 00:00:00 GMT
Link: <https://developer.example.com/migration>; rel="deprecation"
Quick Reference
| Header | Direction | Purpose |
|---|---|---|
Authorization | Request | Authentication credentials |
Content-Type | Both | Body media type |
Accept | Request | Desired response format |
Accept-Encoding | Request | Supported compression |
Accept-Language | Request | Preferred language |
If-None-Match | Request | Cache validation (ETag) |
If-Match | Request | Conditional write (ETag) |
If-Modified-Since | Request | Cache validation (date) |
Location | Response | Created resource URL |
ETag | Response | Resource version identifier |
Last-Modified | Response | Last modification date |
Cache-Control | Both | Caching directives |
Retry-After | Response | Backoff duration (429/503) |
Link | Response | Pagination / related links |
Idempotency-Key | Request | Safe retry identifier |
Deprecation | Response | Endpoint deprecation signal |
Sunset | Response | Removal date |
Related Topics
Content Negotiation
How Accept, Accept-Language, and Accept-Encoding headers drive response format selection.
Caching
Cache-Control, ETag, and Last-Modified strategies for REST API performance.
Authentication
Authorization header patterns — JWT, OAuth 2.0, API keys.
Security
Security headers, CORS, HTTPS, and OWASP API security top 10.
Rate Limiting
X-RateLimit headers, Retry-After, and backoff strategies.
Idempotency
Idempotency-Key header and safe retry patterns.