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: ; 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
← Back to Blog
Top comments (0)