HTTP Tools for Developers: Status Codes, Headers, and Request Building
· Cosyslabs
HTTP (HyperText Transfer Protocol) defines how clients and servers communicate over the web. Every API call, web page load, and file download is an HTTP exchange. Understanding status codes, headers, authentication schemes, and caching directives is fundamental to building and debugging web applications.
HTTP Methods
| Method | Idempotent | Safe | Common Use |
|---|---|---|---|
| GET | Yes | Yes | Retrieve resource |
| HEAD | Yes | Yes | Get headers without body |
| OPTIONS | Yes | Yes | Check CORS/capabilities |
| POST | No | No | Create resource, submit data |
| PUT | Yes | No | Replace entire resource |
| PATCH | No | No | Partial update |
| DELETE | Yes | No | Remove resource |
Idempotent means repeating the request produces the same result. Safe means no side effects (read-only).
HTTP Status Codes
1xx — Informational
| Code | Name | When Used |
|---|---|---|
| 100 | Continue | Client should send request body |
| 101 | Switching Protocols | WebSocket upgrade |
| 103 | Early Hints | Preload hints before final response |
2xx — Success
| Code | Name | When Used |
|---|---|---|
| 200 | OK | Standard success |
| 201 | Created | Resource successfully created (POST) |
| 202 | Accepted | Request accepted, processing async |
| 204 | No Content | Success, no body (DELETE, PUT) |
| 206 | Partial Content | Range request fulfilled |
3xx — Redirection
| Code | Name | When Used |
|---|---|---|
| 301 | Moved Permanently | SEO-friendly permanent redirect |
| 302 | Found | Temporary redirect (deprecated — use 307/308) |
| 303 | See Other | Redirect to GET after POST (PRG pattern) |
| 304 | Not Modified | Cache is still valid (conditional GET) |
| 307 | Temporary Redirect | Temporary, preserves method |
| 308 | Permanent Redirect | Permanent, preserves method |
4xx — Client Errors
| Code | Name | Common Cause |
|---|---|---|
| 400 | Bad Request | Invalid syntax, missing required field |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but lacks permission |
| 404 | Not Found | Resource does not exist |
| 405 | Method Not Allowed | Wrong HTTP method |
| 408 | Request Timeout | Client took too long |
| 409 | Conflict | State conflict (duplicate, optimistic lock) |
| 410 | Gone | Permanently removed (SEO signal) |
| 413 | Content Too Large | Payload exceeds server limit |
| 415 | Unsupported Media Type | Wrong Content-Type |
| 422 | Unprocessable Entity | Syntactically valid but semantically wrong |
| 429 | Too Many Requests | Rate limit exceeded |
5xx — Server Errors
| Code | Name | Common Cause |
|---|---|---|
| 500 | Internal Server Error | Unhandled exception |
| 501 | Not Implemented | Method not supported |
| 502 | Bad Gateway | Upstream server returned invalid response |
| 503 | Service Unavailable | Server overloaded or down |
| 504 | Gateway Timeout | Upstream server timed out |
Request Headers
GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json
Content-Length: 82
User-Agent: MyApp/1.0 (Linux; x86_64)
X-Request-ID: f47ac10b-58cc-4372-a567-0e02b2c3d479
X-Api-Key: sk_live_abc123
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Response Headers
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 1024
Content-Encoding: gzip
Cache-Control: max-age=3600, must-revalidate
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Mon, 15 Jun 2026 10:00:00 GMT
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1750000000
Vary: Accept-Encoding, Accept-Language
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Authentication Schemes
Bearer Token (JWT)
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
const response = await fetch("/api/data", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
Basic Authentication
Authorization: Basic dXNlcjpwYXNzd29yZA==
The value is Base64(username:password). Always requires HTTPS.
const credentials = btoa(`${username}:${password}`);
fetch("/api", {
headers: { Authorization: `Basic ${credentials}` },
});
API Key
Varies by API — commonly sent as header or query parameter:
X-Api-Key: sk_live_abc123xyz
# or
Authorization: ApiKey sk_live_abc123xyz
OAuth 2.0
OAuth 2.0 uses Bearer tokens obtained through various flows (authorization code, client credentials, device code):
// Client credentials flow (server-to-server)
const tokenResponse = await fetch("https://auth.example.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
scope: "read:users write:posts",
}),
});
const { access_token } = await tokenResponse.json();
Caching Headers
Cache-Control
Cache-Control: no-store # Never cache (sensitive data)
Cache-Control: no-cache # Cache but revalidate every request
Cache-Control: max-age=3600 # Cache for 1 hour
Cache-Control: s-maxage=86400 # CDN cache for 1 day
Cache-Control: max-age=0, must-revalidate # Must revalidate expired cache
Cache-Control: public, max-age=31536000, immutable # CDN-cacheable forever
ETag and Conditional Requests
# First request
GET /api/data HTTP/1.1
# Response includes ETag
HTTP/1.1 200 OK
ETag: "abc123"
# Subsequent request — client sends ETag back
GET /api/data HTTP/1.1
If-None-Match: "abc123"
# If unchanged — server returns 304 with no body
HTTP/1.1 304 Not Modified
CORS
CORS (Cross-Origin Resource Sharing) controls which origins can make requests to your API from browsers.
# Browser sends preflight OPTIONS request for complex requests
OPTIONS /api/data HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
# Server response
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
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
Access-Control-Allow-Credentials: true
// Express CORS middleware
import cors from "cors";
app.use(cors({
origin: ["https://app.example.com", "https://admin.example.com"],
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
maxAge: 86400,
}));
Rate Limiting Headers
Standard rate limit headers (no official RFC, but widely adopted conventions):
X-RateLimit-Limit: 100 # Total requests allowed per window
X-RateLimit-Remaining: 23 # Requests remaining this window
X-RateLimit-Reset: 1750000000 # Unix timestamp when window resets
# GitHub-style
X-RateLimit-Used: 77
# IETF Draft standard headers
RateLimit-Limit: 100
RateLimit-Remaining: 23
RateLimit-Reset: 1750000000
When rate limited, servers should return 429 Too Many Requests with:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content Negotiation
# Client requests preferred formats in priority order
Accept: application/json, text/html;q=0.9, */*;q=0.8
# Client requests preferred languages
Accept-Language: en-US,en;q=0.9,fr;q=0.7
# Client declares what encodings it accepts
Accept-Encoding: gzip, deflate, br, zstd
Security Headers
# Prevent clickjacking
X-Frame-Options: DENY
# Prevent MIME type sniffing
X-Content-Type-Options: nosniff
# Enforce HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Control browser features
Permissions-Policy: camera=(), microphone=(), geolocation=()
# Content Security Policy
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'; style-src 'self'
# Referrer Policy
Referrer-Policy: strict-origin-when-cross-origin
Debugging HTTP Requests
curl
# GET with headers
curl -H "Authorization: Bearer TOKEN" -v https://api.example.com/users
# POST with JSON body
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{"name": "Alice", "email": "alice@example.com"}' \
https://api.example.com/users
# Show only response headers
curl -I https://api.example.com/users
# Follow redirects
curl -L https://example.com/redirect
JavaScript (fetch)
// Full request with error handling
async function apiCall(path, options = {}) {
const response = await fetch(`https://api.example.com${path}`, {
...options,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${getToken()}`,
...options.headers,
},
});
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After");
throw new Error(`Rate limited. Retry after ${retryAfter}s`);
}
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.message ?? `HTTP ${response.status}`);
}
return response.json();
}
Tools
- HTTP Status Code Reference — look up any status code with description and use cases
- curl Command Builder — build curl commands from a form interface
- Request Header Analyzer — inspect your browser's request headers