Learn HTTP headers, content negotiation, authentication headers, CORS, caching, and best practices for request and response headers in REST APIs.
HTTP headers are metadata sent along with requests and responses. They provide additional information about the message — what format the data is in, who is making the request, how to cache the response, and more.
Every HTTP message has two parts:
Headers are structured as Name: Value pairs. Here is an example request:
GET /users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
User-Agent: Mozilla/5.0And the corresponding response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600
Access-Control-Allow-Origin: *
{"id": 123, "name": "Alice"}Headers are invisible in the browser, but they control everything about how HTTP works. Understanding headers is essential for building robust APIs.
Two of the most important headers control what format the data is in and what format the client wants.
Content-Type — What format is this message?
The Content-Type header tells the recipient what format the body is in. It is sent by both clients (in POST/PUT requests) and servers (in responses).
Common Content-Type values:
application/json — JSON data (most common for REST APIs)application/x-www-form-urlencoded — HTML form data (key1=value1&key2=value2)multipart/form-data — File uploads with formstext/html — HTML pagetext/plain — Plain textapplication/xml — XML dataimage/png, image/jpeg — Image filesExample:
POST /users
Content-Type: application/json
{"name": "Alice", "email": "alice@example.com"}Accept — What format do I want back?
The Accept header is sent by the client to tell the server what format it prefers for the response. This is called content negotiation.
Example:
GET /users/123
Accept: application/jsonThe server should respond with JSON:
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "Alice"}If the server cannot provide the requested format, it returns 406 Not Acceptable. In practice, most modern APIs only support JSON and ignore the Accept header.
The Authorization header is how clients prove their identity. It is used for authentication — proving you are who you say you are.
The most common pattern is Bearer tokens (typically JWTs):
GET /users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyM30.signatureThe format is: Authorization: Bearer <token>
When the server receives this request, it:
Other authorization schemes exist:
Authorization: Basic base64(username:password) — Basic auth (rarely used for APIs)Authorization: Digest ... — Digest auth (obsolete)Authorization: OAuth token — OAuth 2.0But Bearer tokens are by far the most common in modern REST APIs.
Important: Never send sensitive tokens in URL query parameters (/users?token=abc123). URLs are logged everywhere — server logs, proxy logs, browser history. Always use the Authorization header for authentication.
CORS (Cross-Origin Resource Sharing) headers control which websites can call your API from the browser. This is essential for frontend-backend communication.
The Problem: Same-Origin Policy
By default, browsers block JavaScript on https://myapp.com from making requests to https://api.example.com. This is a security feature called the same-origin policy. Without it, malicious websites could steal data from other sites.
But what if you WANT myapp.com to call api.example.com? You use CORS headers.
The Solution: CORS Headers The server sends these headers to tell the browser "it is okay, allow this request":
Access-Control-Allow-Origin — Which origins are allowed?
Access-Control-Allow-Origin: https://myapp.com
Or allow all origins (use cautiously):
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods — Which HTTP methods are allowed?
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers — Which headers can the client send?
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials — Can the client send cookies?
Access-Control-Allow-Credentials: true
Preflight Requests For non-simple requests (POST with JSON, requests with custom headers), the browser sends an OPTIONS request first to check if CORS is allowed. This is called a preflight. Your API must respond to OPTIONS requests with the appropriate CORS headers.
Example preflight:
OPTIONS /users
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Response:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, AuthorizationWithout proper CORS headers, frontend apps cannot call your API.
Headers also control caching (storing responses to avoid redundant requests) and rate limiting (preventing abuse).
Cache-Control — How long can this response be cached?
Cache-Control: max-age=3600
This means "you can reuse this response for up to 3600 seconds (1 hour) without asking the server again".
Other values:
Cache-Control: no-cache — You can cache it, but check with the server before reusing (conditional request)Cache-Control: no-store — Do not cache this at all (sensitive data)Cache-Control: public — Any cache (browser, CDN) can store thisCache-Control: private — Only the browser can cache this, not shared cachesCaching improves performance dramatically. If your API returns the same data frequently (like a user profile or a list of countries), cache it.
ETag — Conditional Requests
The ETag header is a fingerprint of the response content. If the client makes the same request later, it sends the ETag back in an If-None-Match header. If the content hasn't changed, the server returns 304 Not Modified with no body, saving bandwidth.
GET /users/123
Response:
200 OK
ETag: "abc123xyz"
{"id": 123, "name": "Alice"}
Later:
GET /users/123
If-None-Match: "abc123xyz"
Response (if unchanged):
304 Not ModifiedRate Limiting Headers When your API enforces rate limits, tell clients about it:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1640995200When the limit is exceeded:
429 Too Many Requests
Retry-After: 60This helps clients implement exponential backoff and avoid getting blocked.
A client sends a POST request to create a user but forgets to include the Content-Type header. What should the API do?