HTTP Headers for REST APIs

Complete reference for request headers, response headers, caching validators, security headers, and CORS — defined in RFC 9110 (HTTP Semantics).

Last Updated:

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
AuthorizationRequestAuthentication credentials
Content-TypeBothBody media type
AcceptRequestDesired response format
Accept-EncodingRequestSupported compression
Accept-LanguageRequestPreferred language
If-None-MatchRequestCache validation (ETag)
If-MatchRequestConditional write (ETag)
If-Modified-SinceRequestCache validation (date)
LocationResponseCreated resource URL
ETagResponseResource version identifier
Last-ModifiedResponseLast modification date
Cache-ControlBothCaching directives
Retry-AfterResponseBackoff duration (429/503)
LinkResponsePagination / related links
Idempotency-KeyRequestSafe retry identifier
DeprecationResponseEndpoint deprecation signal
SunsetResponseRemoval date