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-violationX-Content-Type-Options
Prevents MIME type sniffing. Without this header, browsers might execute files they shouldn't:
X-Content-Type-Options: nosniffX-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 embeddingStrict-Transport-Security (HSTS)
Forces HTTPS connections. Browser remembers to only use HTTPS for your domain:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadStart 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 requestsCaching 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-revalidateValidation 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 hoursNever 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: trueAuthentication 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-keyContent 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.9Performance Headers
Compression
# Server sends compressed response
Content-Encoding: gzip # or br (Brotli), deflate
# Client advertises supported compression
Accept-Encoding: gzip, deflate, brRange 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_sizeEarly 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 OKDebugging 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.comMinimum 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-originKey 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.