HTTP Protocol Complete Guide

Introduction
Every time you browse a website, call an API, or interact with a web application, you're using HTTP — the HyperText Transfer Protocol. It's the foundation of data communication on the web and understanding it deeply will make you a better developer.
This guide takes you from HTTP basics to advanced concepts like HTTP/2 and HTTP/3, with practical examples you can apply immediately.
What You'll Learn
✅ Understand what HTTP is and how it works
✅ Master HTTP methods (GET, POST, PUT, DELETE, PATCH, and more)
✅ Learn all HTTP status codes and when to use them
✅ Work with headers, cookies, and authentication
✅ Understand HTTPS and TLS/SSL security
✅ Explore HTTP/2 and HTTP/3 improvements
✅ Implement caching and CORS correctly
✅ Debug HTTP with curl, Postman, and browser DevTools
What is HTTP?
HTTP (HyperText Transfer Protocol) is an application-layer protocol for transmitting hypermedia documents (like HTML). It follows a client-server model where:
- Client (browser, app, or API consumer) sends a request
- Server processes the request and returns a response
Client Server
| |
| -------- Request ---------> |
| |
| <------- Response --------- |
| |Key Characteristics
- Stateless: Each request is independent. Server doesn't remember previous requests.
- Text-based: HTTP messages are human-readable (until HTTP/2 introduced binary framing).
- Extensible: Headers allow adding custom metadata.
- Request-Response: Every interaction is a request followed by a response.
HTTP in the Protocol Stack
┌─────────────────────────────┐
│ Application Layer (HTTP) │ ← You work here
├─────────────────────────────┤
│ Transport Layer (TCP/UDP) │
├─────────────────────────────┤
│ Network Layer (IP) │
├─────────────────────────────┤
│ Link Layer (Ethernet) │
└─────────────────────────────┘HTTP sits on top of TCP (or QUIC for HTTP/3), which handles reliable data transmission.
HTTP Version History
HTTP/0.9 (1991) — The Beginning
- Only GET method
- No headers
- Only HTML responses
- Connection closed after each response
GET /index.htmlHTTP/1.0 (1996) — Headers Introduced
- Added POST, HEAD methods
- Status codes introduced
- Headers added (Content-Type, etc.)
- One request per TCP connection
GET /index.html HTTP/1.0
User-Agent: Mozilla/1.0
HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 1234
<html>...</html>HTTP/1.1 (1997) — Persistent Connections
- Persistent connections (keep-alive by default)
- Pipelining (send multiple requests without waiting for responses)
- Chunked transfer encoding (streaming)
- New methods: PUT, PATCH, DELETE, OPTIONS
- Host header required (virtual hosting)
GET /index.html HTTP/1.1
Host: example.com
Connection: keep-aliveStill widely used today! Most APIs run on HTTP/1.1.
HTTP/2 (2015) — Binary and Multiplexed
- Binary framing (not text-based)
- Multiplexing (multiple requests on single connection)
- Header compression (HPACK)
- Server push (send resources before requested)
- Stream prioritization
Single TCP Connection
├── Stream 1: GET /index.html
├── Stream 2: GET /style.css
├── Stream 3: GET /script.js
└── Stream 4: GET /image.pngHTTP/3 (2022) — QUIC Protocol
- Built on QUIC (UDP-based) instead of TCP
- 0-RTT connection (faster initial connection)
- Independent streams (no head-of-line blocking)
- Built-in encryption (TLS 1.3 required)
- Better performance on unreliable networks (mobile)
HTTP/3 over QUIC (UDP)
├── No head-of-line blocking
├── Connection migration (IP change during request)
└── Faster handshake (0-RTT or 1-RTT)HTTP Request Structure
Every HTTP request has this structure:
METHOD /path HTTP/version
Header1: Value1
Header2: Value2
Request Body (optional)Example Request
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Length: 58
{"name": "John Doe", "email": "john@example.com"}Request Components
| Component | Description | Example |
|---|---|---|
| Method | Action to perform | POST |
| Path | Resource location | /api/users |
| Version | HTTP version | HTTP/1.1 |
| Headers | Metadata key-value pairs | Content-Type: application/json |
| Body | Data payload (optional) | {"name": "John"} |
HTTP Response Structure
Every HTTP response has this structure:
HTTP/version StatusCode StatusMessage
Header1: Value1
Header2: Value2
Response BodyExample Response
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/123
X-Request-Id: abc-123-def
Date: Thu, 05 Feb 2026 10:30:00 GMT
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2026-02-05T10:30:00Z"
}Response Components
| Component | Description | Example |
|---|---|---|
| Version | HTTP version | HTTP/1.1 |
| Status Code | Numeric result code | 201 |
| Status Message | Human-readable status | Created |
| Headers | Metadata | Content-Type: application/json |
| Body | Response data | {"id": 123, ...} |
HTTP Methods (Verbs)
HTTP methods define the action to perform on a resource.
GET — Retrieve Data
Safe and idempotent. Should not modify server state.
GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/jsonUse for:
- Fetching resources
- Search queries
- Reading data
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "John Doe"}POST — Create Data
Not idempotent. Creates a new resource.
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name": "Jane Doe", "email": "jane@example.com"}Use for:
- Creating new resources
- Submitting forms
- Complex queries (when URL would be too long)
Response:
HTTP/1.1 201 Created
Location: /api/users/124
{"id": 124, "name": "Jane Doe"}PUT — Replace Data
Idempotent. Replaces entire resource.
PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"id": 123, "name": "John Smith", "email": "john.smith@example.com"}Use for:
- Replacing entire resource
- Create if not exists (upsert)
Key point: Send the complete resource. Missing fields may be set to null/default.
PATCH — Partial Update
Not always idempotent. Updates specific fields.
PATCH /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name": "John Smith"}Use for:
- Updating specific fields
- Partial modifications
Key point: Only send fields you want to update.
DELETE — Remove Data
Idempotent. Removes a resource.
DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOi...Response:
HTTP/1.1 204 No ContentOr with body:
HTTP/1.1 200 OK
{"message": "User deleted successfully"}HEAD — Get Headers Only
Same as GET but returns only headers (no body). Used for checking if resource exists or getting metadata.
HEAD /api/users/123 HTTP/1.1
Host: api.example.comResponse:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 245
Last-Modified: Thu, 05 Feb 2026 10:00:00 GMTUse for:
- Checking if resource exists
- Getting file size before download
- Checking if cached version is still valid
OPTIONS — Get Allowed Methods
Returns allowed methods for a resource. Used by browsers for CORS preflight.
OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: https://frontend.example.comResponse:
HTTP/1.1 204 No Content
Allow: GET, POST, OPTIONS
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, AuthorizationMethod Comparison
| Method | Request Body | Response Body | Idempotent | Safe | Cacheable |
|---|---|---|---|---|---|
| GET | No | Yes | Yes | Yes | Yes |
| POST | Yes | Yes | No | No | Rarely |
| PUT | Yes | Optional | Yes | No | No |
| PATCH | Yes | Yes | No | No | No |
| DELETE | Optional | Optional | Yes | No | No |
| HEAD | No | No | Yes | Yes | Yes |
| OPTIONS | Optional | Optional | Yes | Yes | No |
Idempotent: Same request multiple times = same result. Safe: Doesn't modify server state.
HTTP Status Codes
Status codes indicate the result of an HTTP request.
1xx — Informational
Rarely used in APIs. Indicates the request was received and is being processed.
| Code | Name | Description |
|---|---|---|
| 100 | Continue | Server received headers, client should send body |
| 101 | Switching Protocols | Server switching to different protocol (WebSocket) |
| 102 | Processing | Server is processing (WebDAV) |
Example: WebSocket upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade2xx — Success
Request was successful.
| Code | Name | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH, DELETE with body |
| 201 | Created | Resource created (POST). Include Location header |
| 202 | Accepted | Request accepted for async processing |
| 204 | No Content | Success, no response body (DELETE, PUT) |
| 206 | Partial Content | Range request successful (file downloads) |
200 OK — Standard success
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 1, "name": "John"}201 Created — Resource created
HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
{"id": 123, "name": "John", "createdAt": "2026-02-05T10:00:00Z"}202 Accepted — Async processing
HTTP/1.1 202 Accepted
Content-Type: application/json
{"message": "Report generation started", "statusUrl": "/api/jobs/456"}204 No Content — Success, no body
HTTP/1.1 204 No Content3xx — Redirection
Client must take additional action.
| Code | Name | When to Use |
|---|---|---|
| 301 | Moved Permanently | Resource permanently moved. Update bookmarks |
| 302 | Found | Temporary redirect (original method may change to GET) |
| 303 | See Other | Redirect to different resource (after POST) |
| 304 | Not Modified | Cached version is still valid |
| 307 | Temporary Redirect | Temporary redirect (preserves method) |
| 308 | Permanent Redirect | Permanent redirect (preserves method) |
301 — Permanent redirect
HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/resource304 — Use cached version
HTTP/1.1 304 Not Modified
ETag: "abc123"307 vs 302:
302may change POST to GET (historical behavior)307guarantees method is preserved
4xx — Client Errors
Something wrong with the request.
| Code | Name | When to Use |
|---|---|---|
| 400 | Bad Request | Invalid syntax, validation failed |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported |
| 408 | Request Timeout | Client took too long |
| 409 | Conflict | Request conflicts with current state |
| 410 | Gone | Resource permanently deleted |
| 413 | Payload Too Large | Request body too big |
| 415 | Unsupported Media Type | Content-Type not supported |
| 422 | Unprocessable Entity | Validation errors (semantic) |
| 429 | Too Many Requests | Rate limit exceeded |
400 Bad Request — Invalid input
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Bad Request",
"message": "Invalid JSON syntax",
"details": "Unexpected token at position 45"
}401 Unauthorized — Not authenticated
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json
{"error": "Unauthorized", "message": "Invalid or missing authentication token"}403 Forbidden — Not authorized
HTTP/1.1 403 Forbidden
Content-Type: application/json
{"error": "Forbidden", "message": "You don't have permission to access this resource"}404 Not Found — Resource doesn't exist
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "Not Found", "message": "User with ID 999 not found"}409 Conflict — State conflict
HTTP/1.1 409 Conflict
Content-Type: application/json
{"error": "Conflict", "message": "Email already registered"}422 Unprocessable Entity — Validation errors
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": "Validation Failed",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "age", "message": "Must be at least 13"}
]
}429 Too Many Requests — Rate limited
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{"error": "Rate limit exceeded", "message": "Try again in 60 seconds"}5xx — Server Errors
Something wrong on the server.
| Code | Name | When to Use |
|---|---|---|
| 500 | Internal Server Error | Generic server error |
| 501 | Not Implemented | Feature not implemented |
| 502 | Bad Gateway | Upstream server returned invalid response |
| 503 | Service Unavailable | Server overloaded or in maintenance |
| 504 | Gateway Timeout | Upstream server didn't respond in time |
500 Internal Server Error — Something broke
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error": "Internal Server Error", "message": "An unexpected error occurred"}⚠️ Never expose stack traces or sensitive error details in production!
503 Service Unavailable — Maintenance or overload
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json
{"error": "Service Unavailable", "message": "Server is under maintenance"}Status Code Decision Tree
Request received?
├── No → 5xx (Server Error)
│ ├── Can't reach upstream? → 502/504
│ ├── Overloaded? → 503
│ └── Bug? → 500
│
└── Yes → Process request
├── Valid request?
│ ├── No → 4xx (Client Error)
│ │ ├── Bad syntax? → 400
│ │ ├── Not authenticated? → 401
│ │ ├── Not authorized? → 403
│ │ ├── Not found? → 404
│ │ ├── Validation failed? → 422
│ │ └── Rate limited? → 429
│ │
│ └── Yes → 2xx or 3xx
│ ├── Redirect? → 3xx
│ ├── Created? → 201
│ ├── No content? → 204
│ ├── Accepted (async)? → 202
│ └── Success → 200HTTP Headers
Headers provide metadata about the request or response.
Common Request Headers
GET /api/users HTTP/1.1
Host: api.example.com # Required in HTTP/1.1
Accept: application/json # Preferred response format
Accept-Language: en-US,en;q=0.9 # Preferred language
Accept-Encoding: gzip, deflate, br # Supported compression
Content-Type: application/json # Request body format
Content-Length: 123 # Request body size
Authorization: Bearer eyJhbGciOi... # Authentication token
User-Agent: Mozilla/5.0 (Windows NT 10.0) # Client identification
Cache-Control: no-cache # Caching directives
Cookie: session=abc123; theme=dark # Cookies
X-Request-Id: req-12345 # Custom header for tracingCommon Response Headers
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8 # Response body format
Content-Length: 456 # Response body size
Content-Encoding: gzip # Compression used
Cache-Control: max-age=3600 # Caching directives
ETag: "abc123" # Resource version
Last-Modified: Thu, 05 Feb 2026 10:00:00 GMT # Last update time
Set-Cookie: session=xyz; HttpOnly; Secure # Set cookies
Location: /api/users/123 # Redirect or created resource URL
X-RateLimit-Limit: 100 # Rate limit info
X-RateLimit-Remaining: 95 # Remaining requests
X-RateLimit-Reset: 1707134400 # Reset timestampContent Negotiation Headers
Client tells server preferred formats:
Accept: application/json, application/xml;q=0.9, */*;q=0.8
Accept-Language: vi,en;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Charset: utf-8The q= parameter sets priority (0.0 to 1.0, default 1.0).
Server responds with what it used:
Content-Type: application/json; charset=utf-8
Content-Language: vi
Content-Encoding: gzipSecurity Headers
# CORS headers
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
# Security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=blockCustom Headers
Custom headers traditionally start with X- (though this convention is deprecated):
X-Request-Id: abc-123-def-456 # Request tracing
X-Correlation-Id: corr-789 # Distributed tracing
X-Rate-Limit-Limit: 1000 # Rate limit info
X-Feature-Flag: new-checkout-enabled # Feature flagsCookies and Sessions
Cookies allow servers to store data on the client, enabling stateful behavior over the stateless HTTP protocol.
How Cookies Work
1. Client sends request
2. Server responds with Set-Cookie header
3. Browser stores cookie
4. Browser sends cookie with subsequent requests to same domainSetting Cookies
Server sets cookie:
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=86400
Set-Cookie: theme=dark; Path=/; Max-Age=31536000Browser sends cookie in subsequent requests:
GET /api/dashboard HTTP/1.1
Cookie: session_id=abc123; theme=darkCookie Attributes
| Attribute | Description | Example |
|---|---|---|
Path | URL path where cookie is sent | Path=/api |
Domain | Domain(s) where cookie is sent | Domain=.example.com |
Expires | Expiration date | Expires=Thu, 05 Feb 2027 10:00:00 GMT |
Max-Age | Seconds until expiration | Max-Age=86400 (1 day) |
Secure | Only send over HTTPS | Secure |
HttpOnly | Not accessible via JavaScript | HttpOnly |
SameSite | CSRF protection | SameSite=Strict or Lax or None |
Secure Cookie Best Practices
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600- ✅ HttpOnly: Prevents JavaScript access (XSS protection)
- ✅ Secure: Only sent over HTTPS
- ✅ SameSite=Strict: Only sent for same-site requests (CSRF protection)
- ✅ Max-Age: Expires after specific time
SameSite Attribute Values
| Value | Behavior |
|---|---|
Strict | Only sent for same-site requests. Safest. |
Lax | Sent for top-level navigation (link clicks). Default in modern browsers. |
None | Sent for all requests. Requires Secure. Used for cross-site APIs. |
Session vs Token Authentication
Session-based (cookies):
1. User logs in
2. Server creates session, stores in database/Redis
3. Server sends session_id cookie
4. Browser sends cookie with requests
5. Server looks up session in databaseToken-based (JWT):
1. User logs in
2. Server creates JWT token (self-contained)
3. Server sends token in response body
4. Client stores token (localStorage or cookie)
5. Client sends token in Authorization header
6. Server validates token signature (no database lookup)HTTPS and TLS/SSL
HTTPS = HTTP + TLS (Transport Layer Security)
Why HTTPS?
- ✅ Encryption: Data can't be read by attackers
- ✅ Authentication: Server identity verified via certificate
- ✅ Integrity: Data can't be modified in transit
- ✅ SEO: Google ranks HTTPS sites higher
- ✅ Trust: Browser shows padlock icon
- ✅ Required for modern features: HTTP/2, Service Workers, Geolocation
TLS Handshake (Simplified)
Client Server
| |
| ------ ClientHello ----------------> |
| (supported ciphers) |
| |
| <----- ServerHello ------------------ |
| (chosen cipher) |
| <----- Certificate ------------------ |
| (server's public key) |
| |
| ------ Key Exchange ---------------> |
| (encrypted with public key) |
| |
| <===== Encrypted Connection =======> |
| (symmetric key encryption) |
| |HTTP vs HTTPS
| Feature | HTTP | HTTPS |
|---|---|---|
| Port | 80 | 443 |
| URL | http:// | https:// |
| Encryption | None | TLS |
| Certificate | Not required | Required |
| Performance | Slightly faster | Small overhead |
| Security | Vulnerable to MITM | Secure |
Forcing HTTPS
Server redirect:
// Express.js middleware
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect(`https://${req.headers.host}${req.url}`);
}
next();
});HSTS header (HTTP Strict Transport Security):
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadThis tells browsers to always use HTTPS for this domain.
Connection Management
Persistent Connections (Keep-Alive)
HTTP/1.0 closed connection after each request. HTTP/1.1 keeps connection open by default.
# HTTP/1.1 (default: keep-alive)
Connection: keep-alive
Keep-Alive: timeout=5, max=100
# Close connection after response
Connection: closeHTTP/1.1 Pipelining (Rarely Used)
Send multiple requests without waiting for responses:
Client Server
| |
| -------- Request 1 --------> |
| -------- Request 2 --------> |
| -------- Request 3 --------> |
| |
| <------- Response 1 -------- |
| <------- Response 2 -------- |
| <------- Response 3 -------- |Problem: Head-of-line blocking. Slow response 1 blocks responses 2 and 3.
HTTP/2 Multiplexing
Multiple streams on single connection, no head-of-line blocking at HTTP layer:
Single TCP Connection
│
├── Stream 1: GET /index.html → Response (HTML)
├── Stream 2: GET /style.css → Response (CSS)
├── Stream 3: GET /script.js → Response (JS)
└── Stream 4: GET /image.png → Response (Image)
All streams interleaved, no blocking!HTTP/3 with QUIC
QUIC runs over UDP and eliminates TCP head-of-line blocking:
HTTP/3 over QUIC (UDP)
│
├── Stream 1 (independent)
├── Stream 2 (independent) ← Packet loss here doesn't block others
├── Stream 3 (independent)
└── Stream 4 (independent)HTTP Caching
Caching reduces server load and improves response times.
Cache-Control Header
# Server response
Cache-Control: public, max-age=3600, must-revalidate| Directive | Meaning |
|---|---|
public | Can be cached by CDN, proxy, browser |
private | Only browser can cache (not CDN) |
no-cache | Must revalidate with server before using |
no-store | Don't cache at all |
max-age=N | Cache for N seconds |
s-maxage=N | Cache duration for shared caches (CDN) |
must-revalidate | Must check with server when stale |
immutable | Content will never change |
Caching Examples
Static assets (long cache):
Cache-Control: public, max-age=31536000, immutableAPI data (short cache):
Cache-Control: private, max-age=60No caching:
Cache-Control: no-storeCache but validate:
Cache-Control: no-cacheETag and Conditional Requests
Server sends ETag:
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: no-cache
{"id": 1, "name": "John"}Client validates with If-None-Match:
GET /api/users/1 HTTP/1.1
If-None-Match: "abc123"Server response if unchanged:
HTTP/1.1 304 Not Modified
ETag: "abc123"Last-Modified and If-Modified-Since
# Server response
Last-Modified: Thu, 05 Feb 2026 10:00:00 GMT
# Client validation
If-Modified-Since: Thu, 05 Feb 2026 10:00:00 GMT
# Server response if unchanged
HTTP/1.1 304 Not ModifiedCaching Strategy Summary
| Content Type | Strategy |
|---|---|
| Static files (JS, CSS, images) | Long max-age, versioned URLs |
| HTML pages | Short max-age or no-cache with ETag |
| API responses | private with short max-age or no-cache |
| User-specific data | private, no-store |
| Public data | public with ETag validation |
CORS (Cross-Origin Resource Sharing)
CORS controls which domains can access your API from browsers.
Same-Origin Policy
Browsers block requests to different origins by default:
https://frontend.com → https://api.com ❌ Blocked (different origin)
https://api.com → https://api.com ✅ Allowed (same origin)Origin = protocol + domain + port
https://example.com:443andhttps://example.com:3000are different origins!
Simple Requests (No Preflight)
Simple requests don't trigger preflight:
- Methods: GET, HEAD, POST
- Headers: Only basic headers (Accept, Content-Type with certain values)
- Content-Type:
text/plain,multipart/form-data,application/x-www-form-urlencoded
Request:
GET /api/public HTTP/1.1
Host: api.example.com
Origin: https://frontend.example.comResponse:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.example.comPreflight Requests
Complex requests trigger OPTIONS preflight:
Browser API Server
| |
| ---- OPTIONS (preflight) -----------> |
| Origin: frontend.example.com |
| Access-Control-Request-Method: PUT
| Access-Control-Request-Headers: Content-Type, Authorization
| |
| <--- 204 (preflight response) ------- |
| Access-Control-Allow-Origin: frontend.example.com
| Access-Control-Allow-Methods: GET, PUT, DELETE
| Access-Control-Allow-Headers: Content-Type, Authorization
| Access-Control-Max-Age: 86400
| |
| ---- PUT (actual request) ----------> |
| |
| <--- 200 (actual response) ---------- |CORS Headers
Response headers:
| Header | Description |
|---|---|
Access-Control-Allow-Origin | Allowed origin(s). Use specific origin or * |
Access-Control-Allow-Methods | Allowed HTTP methods |
Access-Control-Allow-Headers | Allowed request headers |
Access-Control-Allow-Credentials | Allow cookies/auth headers (true) |
Access-Control-Expose-Headers | Headers client can access |
Access-Control-Max-Age | Preflight cache duration (seconds) |
CORS Configuration Examples
Express.js:
import cors from 'cors';
// Allow specific origin
app.use(cors({
origin: 'https://frontend.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// Allow multiple origins
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
credentials: true
}));
// Dynamic origin validation
app.use(cors({
origin: (origin, callback) => {
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));CORS with Credentials
When sending cookies or authentication:
# Request (browser automatically adds)
GET /api/user HTTP/1.1
Origin: https://frontend.example.com
Cookie: session=abc123
# Response
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Credentials: true⚠️ Cannot use Access-Control-Allow-Origin: * with credentials!
Content Encoding and Compression
Compression
Reduce response size with compression:
Client request:
Accept-Encoding: gzip, deflate, brServer response:
Content-Encoding: gzip| Algorithm | Compression Ratio | Speed | Browser Support |
|---|---|---|---|
| gzip | Good | Fast | Universal |
| deflate | Good | Fast | Universal |
| br (Brotli) | Best | Slower | Modern browsers |
Chunked Transfer Encoding
Stream response without knowing total size:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\nUseful for:
- Streaming large files
- Server-sent events
- Real-time data
HTTP/2 Deep Dive
Key Features
1. Binary Framing HTTP/2 uses binary format instead of text:
HTTP/1.1: "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
HTTP/2: Binary frames with stream ID, type, flags, length2. Multiplexing Multiple requests/responses over single connection:
Stream 1: Request → ← Response
Stream 3: Request → ← Response
Stream 5: Request → ← Response
(All on same TCP connection, interleaved)3. Header Compression (HPACK) Headers are compressed and cached:
First request: Full headers sent
Second request: Only changes sent (index references)4. Server Push Server sends resources before client requests them:
Client: GET /index.html
Server: Response (index.html)
PUSH_PROMISE (style.css)
Response (style.css)5. Stream Prioritization Client can indicate priority of resources:
Stream 1 (HTML): Highest priority
Stream 3 (CSS): High priority
Stream 5 (image): Low priorityEnabling HTTP/2
Most servers enable HTTP/2 automatically over HTTPS:
Nginx:
server {
listen 443 ssl http2;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}Node.js:
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
});
server.on('stream', (stream, headers) => {
stream.respond({ ':status': 200 });
stream.end('Hello HTTP/2!');
});
server.listen(443);HTTP/3 and QUIC
Why HTTP/3?
HTTP/2 still uses TCP, which has problems:
- Head-of-line blocking: Lost packet blocks all streams
- Connection setup: TCP + TLS handshake is slow
- Connection migration: IP change breaks connection
QUIC Features
1. UDP-based No TCP head-of-line blocking at transport layer.
2. 0-RTT Connection Can send data with first packet (if previously connected):
Traditional: TCP handshake (1 RTT) + TLS handshake (1-2 RTT) = 2-3 RTT
QUIC: 0-RTT (resumed) or 1-RTT (new connection)3. Independent Streams Packet loss in one stream doesn't affect others.
4. Connection Migration Connection survives IP address changes (mobile network switching).
5. Built-in Encryption TLS 1.3 is mandatory, always encrypted.
HTTP/3 Adoption
Check if a site supports HTTP/3:
curl -I --http3 https://cloudflare.comMajor CDNs support HTTP/3:
- Cloudflare
- Fastly
- Akamai
Debugging HTTP
Using curl
Basic GET:
curl https://api.example.com/usersWith headers:
curl -H "Authorization: Bearer token123" https://api.example.com/usersPOST with JSON:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "John", "email": "john@example.com"}'Show request and response headers:
curl -v https://api.example.com/usersOnly response headers:
curl -I https://api.example.com/usersFollow redirects:
curl -L https://example.comSave response to file:
curl -o response.json https://api.example.com/usersTiming information:
curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/usersWhere curl-format.txt contains:
time_namelookup: %{time_namelookup}s
time_connect: %{time_connect}s
time_appconnect: %{time_appconnect}s
time_pretransfer: %{time_pretransfer}s
time_redirect: %{time_redirect}s
time_starttransfer: %{time_starttransfer}s
----------
time_total: %{time_total}sUsing Postman
- Create new request
- Set method and URL
- Add headers in Headers tab
- Add body in Body tab (for POST/PUT)
- View response, headers, timing
Browser DevTools
- Open DevTools (F12)
- Go to Network tab
- Make request
- Click on request to see:
- Headers (request and response)
- Preview (formatted response)
- Response (raw)
- Timing (connection, waiting, download)
- Cookies
HTTPie (Alternative to curl)
More user-friendly CLI tool:
# GET
http https://api.example.com/users
# POST with JSON
http POST https://api.example.com/users name=John email=john@example.com
# With headers
http https://api.example.com/users Authorization:"Bearer token"
# Form data
http -f POST https://api.example.com/login email=john@example.com password=secretReal-World Examples
Example 1: RESTful API Request
// Client-side fetch
async function createUser(userData: CreateUserDTO) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
body: JSON.stringify(userData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json();
}Request:
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
Accept: application/json
Content-Length: 56
{"name": "John Doe", "email": "john@example.com"}Response:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/123
X-Request-Id: req-abc-123
{"id": 123, "name": "John Doe", "email": "john@example.com", "createdAt": "2026-02-05T10:00:00Z"}Example 2: File Download with Range Request
# Request partial content
GET /large-file.zip HTTP/1.1
Host: cdn.example.com
Range: bytes=0-1023
# Response
HTTP/1.1 206 Partial Content
Content-Type: application/zip
Content-Range: bytes 0-1023/1048576
Content-Length: 1024
[binary data...]Example 3: Streaming Response (Server-Sent Events)
GET /events HTTP/1.1
Host: api.example.com
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"type": "connected"}
data: {"type": "message", "content": "Hello"}
data: {"type": "message", "content": "World"}Best Practices
API Design
-
Use appropriate HTTP methods
- GET for reading, POST for creating, PUT/PATCH for updating, DELETE for deleting
-
Return appropriate status codes
- 201 for created, 204 for no content, 400/422 for validation errors
-
Use consistent error format
{"error": "Validation Failed", "code": "VALIDATION_ERROR", "details": [...]} -
Version your API
https://api.example.com/v1/users
Security
- Always use HTTPS in production
- Set security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains Content-Security-Policy: default-src 'self' X-Content-Type-Options: nosniff - Configure CORS properly (don't use
*in production) - Use HttpOnly, Secure, SameSite for cookies
Performance
- Enable compression (gzip or brotli)
- Set cache headers appropriately
- Use HTTP/2 or HTTP/3 when possible
- Minimize redirects
Summary and Key Takeaways
✅ HTTP is the protocol for web communication, based on request-response model
✅ HTTP versions: 1.1 (still common), 2 (multiplexing, binary), 3 (QUIC, UDP-based)
✅ Methods: GET (read), POST (create), PUT (replace), PATCH (update), DELETE (remove)
✅ Status codes: 2xx (success), 3xx (redirect), 4xx (client error), 5xx (server error)
✅ Headers provide metadata for requests and responses
✅ HTTPS encrypts communication using TLS
✅ Caching uses Cache-Control, ETag, and Last-Modified headers
✅ CORS controls cross-origin access in browsers
✅ Debug with curl, Postman, or browser DevTools
What's Next?
Now that you understand HTTP, continue your learning:
Build REST APIs:
- What is REST API? Complete Guide — Design APIs following REST principles
- Getting Started with Spring Boot — Build REST APIs with Java
- TypeScript Phase 3: Backend Development — Build APIs with TypeScript
Document Your APIs:
- API Documentation with OpenAPI/Swagger — Auto-generate interactive docs
- Understanding OpenAPI with FastAPI — OpenAPI in Python
Deep Dive:
- Relational Database Fundamentals — Store and retrieve data efficiently
- Database Schema Design — Design your data layer
Resources
- MDN HTTP Documentation
- HTTP/2 Specification (RFC 7540)
- HTTP/3 Specification (RFC 9114)
- curl Documentation
Conclusion
HTTP is the foundation of web communication. Understanding how requests and responses work, what different status codes mean, and how features like caching and CORS work will make you a more effective developer.
Whether you're building APIs, debugging network issues, or optimizing performance, the HTTP knowledge from this guide will serve you well throughout your career.
Happy coding! 🚀
📬 Subscribe to Newsletter
Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.
We respect your privacy. Unsubscribe at any time.
💬 Comments
Sign in to leave a comment
We'll never post without your permission.