Back to blog

HTTP Protocol Complete Guide

httpweb fundamentalsnetworkingbackendapi
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.html

HTTP/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-alive

Still 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.png

HTTP/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

ComponentDescriptionExample
MethodAction to performPOST
PathResource location/api/users
VersionHTTP versionHTTP/1.1
HeadersMetadata key-value pairsContent-Type: application/json
BodyData payload (optional){"name": "John"}

HTTP Response Structure

Every HTTP response has this structure:

HTTP/version StatusCode StatusMessage
Header1: Value1
Header2: Value2
 
Response Body

Example 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

ComponentDescriptionExample
VersionHTTP versionHTTP/1.1
Status CodeNumeric result code201
Status MessageHuman-readable statusCreated
HeadersMetadataContent-Type: application/json
BodyResponse 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/json

Use 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 Content

Or 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.com

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 245
Last-Modified: Thu, 05 Feb 2026 10:00:00 GMT

Use 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.com

Response:

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, Authorization

Method Comparison

MethodRequest BodyResponse BodyIdempotentSafeCacheable
GETNoYesYesYesYes
POSTYesYesNoNoRarely
PUTYesOptionalYesNoNo
PATCHYesYesNoNoNo
DELETEOptionalOptionalYesNoNo
HEADNoNoYesYesYes
OPTIONSOptionalOptionalYesYesNo

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.

CodeNameDescription
100ContinueServer received headers, client should send body
101Switching ProtocolsServer switching to different protocol (WebSocket)
102ProcessingServer is processing (WebDAV)

Example: WebSocket upgrade

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

2xx — Success

Request was successful.

CodeNameWhen to Use
200OKSuccessful GET, PUT, PATCH, DELETE with body
201CreatedResource created (POST). Include Location header
202AcceptedRequest accepted for async processing
204No ContentSuccess, no response body (DELETE, PUT)
206Partial ContentRange 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 Content

3xx — Redirection

Client must take additional action.

CodeNameWhen to Use
301Moved PermanentlyResource permanently moved. Update bookmarks
302FoundTemporary redirect (original method may change to GET)
303See OtherRedirect to different resource (after POST)
304Not ModifiedCached version is still valid
307Temporary RedirectTemporary redirect (preserves method)
308Permanent RedirectPermanent redirect (preserves method)

301 — Permanent redirect

HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/resource

304 — Use cached version

HTTP/1.1 304 Not Modified
ETag: "abc123"

307 vs 302:

  • 302 may change POST to GET (historical behavior)
  • 307 guarantees method is preserved

4xx — Client Errors

Something wrong with the request.

CodeNameWhen to Use
400Bad RequestInvalid syntax, validation failed
401UnauthorizedAuthentication required or failed
403ForbiddenAuthenticated but not authorized
404Not FoundResource doesn't exist
405Method Not AllowedHTTP method not supported
408Request TimeoutClient took too long
409ConflictRequest conflicts with current state
410GoneResource permanently deleted
413Payload Too LargeRequest body too big
415Unsupported Media TypeContent-Type not supported
422Unprocessable EntityValidation errors (semantic)
429Too Many RequestsRate 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.

CodeNameWhen to Use
500Internal Server ErrorGeneric server error
501Not ImplementedFeature not implemented
502Bad GatewayUpstream server returned invalid response
503Service UnavailableServer overloaded or in maintenance
504Gateway TimeoutUpstream 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 → 200

HTTP 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 tracing

Common 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 timestamp

Content 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-8

The 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: gzip

Security 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=block

Custom 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 flags

Cookies 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 domain

Setting 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=31536000

Browser sends cookie in subsequent requests:

GET /api/dashboard HTTP/1.1
Cookie: session_id=abc123; theme=dark
AttributeDescriptionExample
PathURL path where cookie is sentPath=/api
DomainDomain(s) where cookie is sentDomain=.example.com
ExpiresExpiration dateExpires=Thu, 05 Feb 2027 10:00:00 GMT
Max-AgeSeconds until expirationMax-Age=86400 (1 day)
SecureOnly send over HTTPSSecure
HttpOnlyNot accessible via JavaScriptHttpOnly
SameSiteCSRF protectionSameSite=Strict or Lax or None
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

ValueBehavior
StrictOnly sent for same-site requests. Safest.
LaxSent for top-level navigation (link clicks). Default in modern browsers.
NoneSent 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 database

Token-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

FeatureHTTPHTTPS
Port80443
URLhttp://https://
EncryptionNoneTLS
CertificateNot requiredRequired
PerformanceSlightly fasterSmall overhead
SecurityVulnerable to MITMSecure

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; preload

This 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: close

HTTP/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
DirectiveMeaning
publicCan be cached by CDN, proxy, browser
privateOnly browser can cache (not CDN)
no-cacheMust revalidate with server before using
no-storeDon't cache at all
max-age=NCache for N seconds
s-maxage=NCache duration for shared caches (CDN)
must-revalidateMust check with server when stale
immutableContent will never change

Caching Examples

Static assets (long cache):

Cache-Control: public, max-age=31536000, immutable

API data (short cache):

Cache-Control: private, max-age=60

No caching:

Cache-Control: no-store

Cache but validate:

Cache-Control: no-cache

ETag 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 Modified

Caching Strategy Summary

Content TypeStrategy
Static files (JS, CSS, images)Long max-age, versioned URLs
HTML pagesShort max-age or no-cache with ETag
API responsesprivate with short max-age or no-cache
User-specific dataprivate, no-store
Public datapublic 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:443 and https://example.com:3000 are 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.com

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.example.com

Preflight 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:

HeaderDescription
Access-Control-Allow-OriginAllowed origin(s). Use specific origin or *
Access-Control-Allow-MethodsAllowed HTTP methods
Access-Control-Allow-HeadersAllowed request headers
Access-Control-Allow-CredentialsAllow cookies/auth headers (true)
Access-Control-Expose-HeadersHeaders client can access
Access-Control-Max-AgePreflight 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, br

Server response:

Content-Encoding: gzip
AlgorithmCompression RatioSpeedBrowser Support
gzipGoodFastUniversal
deflateGoodFastUniversal
br (Brotli)BestSlowerModern 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\n

Useful 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, length

2. 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 priority

Enabling 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.com

Major CDNs support HTTP/3:

  • Cloudflare
  • Google
  • Fastly
  • Akamai

Debugging HTTP

Using curl

Basic GET:

curl https://api.example.com/users

With headers:

curl -H "Authorization: Bearer token123" https://api.example.com/users

POST 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/users

Only response headers:

curl -I https://api.example.com/users

Follow redirects:

curl -L https://example.com

Save response to file:

curl -o response.json https://api.example.com/users

Timing information:

curl -w "@curl-format.txt" -o /dev/null -s https://api.example.com/users

Where 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}s

Using Postman

  1. Create new request
  2. Set method and URL
  3. Add headers in Headers tab
  4. Add body in Body tab (for POST/PUT)
  5. View response, headers, timing

Browser DevTools

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Make request
  4. 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=secret

Real-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

  1. Use appropriate HTTP methods

    • GET for reading, POST for creating, PUT/PATCH for updating, DELETE for deleting
  2. Return appropriate status codes

    • 201 for created, 204 for no content, 400/422 for validation errors
  3. Use consistent error format

    {"error": "Validation Failed", "code": "VALIDATION_ERROR", "details": [...]}
  4. Version your API

    https://api.example.com/v1/users

Security

  1. Always use HTTPS in production
  2. Set security headers
    Strict-Transport-Security: max-age=31536000; includeSubDomains
    Content-Security-Policy: default-src 'self'
    X-Content-Type-Options: nosniff
  3. Configure CORS properly (don't use * in production)
  4. Use HttpOnly, Secure, SameSite for cookies

Performance

  1. Enable compression (gzip or brotli)
  2. Set cache headers appropriately
  3. Use HTTP/2 or HTTP/3 when possible
  4. 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:

Document Your APIs:

Deep Dive:


Resources


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.