HTTP Headers: The Complete Guide for Developers

HTTP headers are the invisible glue that makes the web work. They control caching, enforce security, enable authentication, and shape performance. Most developers only know a handful — this guide covers all the ones that actually matter.

How HTTP Headers Work

Every HTTP request and response carries headers: key-value pairs that pass metadata between client and server. Headers are divided into four categories:

  • General — Apply to both requests and responses (Date, Connection)
  • Request — Sent by the client (Accept, Authorization, User-Agent)
  • Response — Sent by the server (Content-Type, Cache-Control, Set-Cookie)
  • Entity — Describe the body content (Content-Length, Content-Encoding)

Security Headers (Essential)

These five headers prevent the most common web attacks. Set them on every production site:

Content-Security-Policy (CSP)

Controls which resources the browser can load. Prevents XSS by specifying allowed sources:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'

Start with 'none', then add exceptions as you discover them. Use report-uri to catch violations:

Content-Security-Policy: default-src 'none'; report-uri /csp-violation

X-Content-Type-Options

Prevents MIME type sniffing. Without this header, browsers might execute files they shouldn't:

X-Content-Type-Options: nosniff

X-Frame-Options

Prevents clickjacking by controlling whether your page can be embedded in iframes:

X-Frame-Options: DENY  # No embedding allowed
X-Frame-Options: SAMEORIGIN  # Only same-site embedding

Strict-Transport-Security (HSTS)

Forces HTTPS connections. Browser remembers to only use HTTPS for your domain:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Start with a short max-age (like 60 seconds) while testing, then increase. The preload flag lets you submit to the HSTS preload list for maximum protection.

Referrer-Policy

Controls how much referrer information is sent with requests. Prevents leaking sensitive URLs:

Referrer-Policy: strict-origin-when-cross-origin  # Default for most sites
Referrer-Policy: no-referrer  # Most private option
Referrer-Policy: same-origin  # Only send for same-site requests

Caching Headers

Proper caching makes your site fast and reduces server load. There are two models:

Expiration Model (Cache-Control)

Tell browsers how long to cache a resource:

# Cache for 1 year (for versioned/hash files)
Cache-Control: public, max-age=31536000, immutable

# Cache for 1 hour, but allow stale responses while revalidating
Cache-Control: public, max-age=3600, stale-while-revalidate=86400

# Don't cache at all
Cache-Control: no-cache, no-store, must-revalidate

Validation Model (ETag / Last-Modified)

Check if cached content is still valid without downloading it:

# Server response includes:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

# Next request asks if changed:
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

# Server returns 304 Not Modified if still valid (no body)

Cacheable Responses

Not everything should be cached. Rules:

  • GET / HEAD — Cachable by default
  • POST / PATCH / PUT / DELETE — Never cached automatically
  • Responses with Set-Cookie — Never cache
  • Authenticated requests — Usually private, unless using shared caches

CORS Headers

Cross-Origin Resource Sharing headers control which domains can access your API:

# Allow specific origin (preferred)
Access-Control-Allow-Origin: https://trusted-app.com

# Allow any origin (for public APIs, combined with Vary: Origin)
Access-Control-Allow-Origin: *

# Preflight request for non-simple methods/headers
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400  # Cache preflight for 24 hours

Never use Access-Control-Allow-Origin: * with credentials (cookies, auth headers):

# Wrong — browsers reject this
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

# Correct — use a specific origin
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

Authentication Headers

Basic Authentication

# Client sends:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
# (Base64 encoded "username:password")

# Server responds 401:
WWW-Authenticate: Basic realm="Protected Area"

Bearer Tokens (JWT, API Keys)

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Custom Headers

Many APIs use custom headers for authentication:

X-API-Key: your-api-key-here
X-Auth-Token: session-token
Authorization: ApiKey your-api-key

Content Negotiation

Tell the server what format you want back:

# Accept specific content type
Accept: application/json

# Accept multiple formats (server chooses best match)
Accept: application/json, application/xml, text/html

# Prefer compressed responses
Accept-Encoding: gzip, deflate, br

# Prefer English, but accept Chinese
Accept-Language: en, zh;q=0.9

Performance Headers

Compression

# Server sends compressed response
Content-Encoding: gzip  # or br (Brotli), deflate

# Client advertises supported compression
Accept-Encoding: gzip, deflate, br

Range Requests

For resumable downloads and video streaming:

# Client requests specific bytes
Range: bytes=0-99

# Server responds with portion
Content-Range: bytes 0-99/total_file_size

Early Hints

Send resource hints before the full response is ready:

# Early hint response (HTTP 103)
Link: <https://fonts.googleapis.com>; rel=preload; as=style

# Followed by final response
103 Early Hint
200 OK

Debugging Headers

Tools to inspect and test headers:

  • Chrome DevTools — Network tab shows all headers
  • curl -I — Show response headers only
  • httpbin.org — Test headers interactively
  • securityheaders.com — Grade your security headers
# View headers with curl
curl -I https://example.com

# Send custom headers
curl -H "Authorization: Bearer token" https://api.example.com

# Show request and response headers
curl -v https://example.com

Minimum Viable Header Set

Every production site should have at minimum:

Content-Type: text/html; charset=utf-8
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin

Key Takeaways

  • Security headers (CSP, HSTS, X-Frame-Options) prevent common attacks — set them on every site
  • Cache-Control with max-age is simpler than ETag validation for most use cases
  • Use immutable + long max-age for versioned assets (JS, CSS with hashes in filenames)
  • CORS headers should whitelist specific origins, not use * (except for truly public APIs)
  • Always test headers with curl or DevTools before deploying to production

Frequently Asked Questions

What's the difference between Cache-Control and ETag?

Cache-Control with max-age tells the browser to use cached content for N seconds without asking. ETag is a validator — the browser sends If-None-Match with the ETag hash, and the server returns 304 if unchanged (no body). Use Cache-Control for simple expiration; use ETag when you need to validate freshness without full downloads. They're often used together: Cache-Control says 'use for 1 hour', ETag lets you skip the download if nothing changed before the hour is up.

How do I debug CORS errors?

Check the browser console for CORS errors — they'll name the blocked header. Verify your server sends Access-Control-Allow-Origin for the requesting origin (check request headers Origin). For credentials, the origin must be specific, not *. Make sure you're not sending Access-Control-Allow-Credentials: true with Access-Control-Allow-Origin: *. Use curl to test preflight requests: curl -X OPTIONS -H 'Origin: ...' -H 'Access-Control-Request-Method: PUT' https://api.example.com

What headers should every API response include?

Minimum set: Content-Type (application/json), Cache-Control (no-store for sensitive data), X-Content-Type-Options: nosniff. For public APIs: Access-Control-Allow-Origin, RateLimit-Limit + RateLimit-Remaining (standardized rate limit headers). For APIs with auth: WWW-Authenticate on 401 responses. Security headers (CSP, HSTS) belong on the whole site, not just API responses.

← Back to Blog
Copied!
Copied!