The distinction between 301/302 and 307/308 matters for methods. 301 and 302 allow the client to Change the method from POST to GET on redirect. 307 and 308 preserve the original method. If you Redirect a POST request, use 307/308 unless you explicitly want the method changed.
4xx — Client Errors:
Code Meaning Use Case 400 Bad Request Malformed request syntax 401 Unauthorized Authentication required 403 Forbidden Authenticated but not authorized 404 Not Found Resource does not exist 405 Method Not Allowed Method not supported for this resource 408 Request Timeout Server timed out waiting for the request 409 Conflict Request conflicts with current state 413 Payload Too Large Request body exceeds server limit 415 Unsupported Media Type Content-Type not supported 422 Unprocessable Entity Valid syntax but semantic errors 429 Too Many Requests Rate limiting
5xx — Server Errors:
Code Meaning Use Case 500 Internal Server Error Unhandled server exception 502 Bad Gateway Upstream server returned an invalid response 503 Service Unavailable Server overloaded or in maintenance 504 Gateway Timeout Upstream server did not respond in time 507 Insufficient Storage Server cannot store the representation
Request Headers:
Header Purpose HostRequired in HTTP/1.1. Identifies the target host and port. Enables virtual hosting. User-AgentClient software identification AcceptExpected response content types Content-TypeMedia type of the request body Content-LengthSize of the request body in bytes AuthorizationAuthentication credentials If-None-MatchETag for conditional requests If-Modified-SinceTimestamp for conditional requests RangeRequest a subset of the resource OriginIndicates the origin of the cross-origin request (CORS)
Response Headers:
Header Purpose Content-TypeMedia type of the response body Content-LengthSize of the response body in bytes Content-EncodingEncoding applied to the body (gzip, br, deflate) Cache-ControlDirectives for caching ETagOpaque identifier for the response content version Last-ModifiedTimestamp of the last modification Set-CookieInstructs the client to store a cookie LocationURL for redirection (3xx responses) ServerServer software identification Strict-Transport-SecurityForce HTTPS (HSTS) X-Request-IDUnique identifier for the request (for tracing)
Persistent Connections (Keep-Alive):
HTTP/1.0 opened a new TCP connection for every request. HTTP/1.1 makes persistent connections the Default — a single TCP connection can serve multiple requests and responses. This eliminates TCP Handshake overhead for subsequent requests.
Connection: keep-alive (HTTP/1.0, explicit)
Connection: close (either version, close after response)
In HTTP/1.1, connections are persistent by default. The client or server sends Connection: close To signal that the connection should be closed after the response.
Pipelining (RFC 7230): HTTP/1.1 pipelining allows the client to send multiple requests without Waiting for responses. The server must respond in order. Pipelining was never widely implemented due To head-of-line blocking (a slow response blocks all subsequent responses) and is deprecated in Practice.
When the server does not know the response size in advance (e.g., streaming data, dynamic content), It uses chunked encoding:
Transfer-Encoding: chunked
Each chunk starts with its size in hexadecimal, followed by \r\nThe data, and \r\n. A Zero-size chunk terminates the transfer.
Head-of-line blocking: Only one request/response can be in flight at a time on a connection. A slow response blocks all subsequent requests. Workaround: open 6+ connections (browsers do this), but this increases resource usage.Verbose headers: ASCII headers are uncompressed and repeated across requests on the same connection.No server push: The server cannot proactively send data to the client (beyond the response to a request).No multiplexing: Multiple requests require multiple TCP connections, each with its own congestion control state and TLS handshake.HTTP/2 (RFC 9113, originally RFC 7540) addresses the limitations of HTTP/1.1 while maintaining the Same semantics (methods, status codes, headers, URIs). The wire format is completely different.
HTTP/2 is a binary protocol. All communication is performed in binary frames. There are 10 frame Types:
Frame Type Purpose DATA Carries request/response body content HEADERS Carries request/response headers PRIORITY Specifies stream priority RST_STREAM Aborts a stream SETTINGS Configures connection parameters PUSH_PROMISE Server push notification PING Measures RTT and keepalive GOAWAY Graceful shutdown of the connection WINDOW_UPDATE Advertises flow control credits CONTINUATION Continues a header block that did not fit in one HEADERS frame
HTTP/2 introduces streams — independent, bidirectional sequences of frames within a single TCP Connection. Multiple streams can be interleaved on the same connection, eliminating HTTP/1.1’s Head-of-line blocking at the application layer.
Each stream is identified by a 31-bit integer. Client-initiated streams use odd numbers; Server-initiated streams use even numbers. Streams have three states: idle, open (local or remote), Half-closed (local or remote), and closed.
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: Single TCP connection
C->>S: HEADERS [stream 1]: GET /style.css
C->>S: HEADERS [stream 3]: GET /script.js
C->>S: HEADERS [stream 5]: GET /api/data
S->>C: HEADERS [stream 1]: 200 OK
S->>C: DATA [stream 1]: CSS content
S->>C: HEADERS [stream 5]: 200 OK
S->>C: DATA [stream 5]: JSON data
S->>C: HEADERS [stream 3]: 200 OK
S->>C: DATA [stream 3]: JS content HTTP/1.1 sends headers as uncompressed ASCII text, repeating the same headers on every request. HPACK (RFC 7541) compresses headers using:
Static table: 61 pre-defined common header fields (e.g., :method: GET``:path: / user-agent``accept-encoding).Dynamic table: Previously sent headers are stored in a FIFO buffer and referenced by index.Huffman coding: String values are encoded using Huffman coding for additional compression.HPACK is critical for performance. A typical HTTP/2 request with common headers (User-Agent, Accept, Accept-Encoding, etc.) that would be ~500 bytes in HTTP/1.1 can be compressed to ~50-100 bytes with HPACK.
HTTP/2 allows the server to proactively send resources to the client before they are requested. The Server sends a PUSH_PROMISE frame containing the headers of the promised resource, followed by the Response.
Server: 200 OK (index.html)
Server: PUSH_PROMISE: GET /style.css
Server: 200 OK (style.css)
Server: PUSH_PROMISE: GET /script.js
Server: 200 OK (script.js)
Server push was intended to reduce latency by pre-loading assets, but it has been largely Deprecated. Chrome removed support for server push in 2022. The issues include:
Cache duplication: Pushed resources may already be in the browser cache, wasting bandwidth.Prioritization complexity: Pushed resources compete with explicitly requested resources for bandwidth.Resource management: Servers can push resources the client does not need.Use <link rel="preload"> hints instead, which let the client decide what to fetch.
HTTP/2 implements flow control at the stream and connection levels. Each endpoint advertises an Initial window size (default: 65,535 bytes). The sender must not send more data than the receiver’s Window allows. WINDOW_UPDATE frames increase the available window.
This prevents a fast sender from overwhelming a slow receiver, which is particularly important with Multiplexing (many streams competing for the same connection).
HTTP/2 allows clients to assign priorities to streams using PRIORITY frames and priority fields in HEADERS frames. Each stream has a weight (1-256) and a dependency on another stream. This allows The server to allocate bandwidth based on client preferences (e.g., prioritize CSS over images).
HTTP/2 eliminates application-layer head-of-line blocking (multiple streams can be interleaved), but TCP head-of-line blocking remains . Because HTTP/2 runs over TCP, a single lost TCP segment Blocks delivery of all streams on that connection until the segment is retransmitted. On lossy Networks (mobile, long-haul), this can make HTTP/2 slower than HTTP/1.1 with multiple connections.
This is the primary motivation for HTTP/3.
HTTP/3 (RFC 9114) replaces TCP with QUIC as the transport layer. QUIC is built on UDP and provides Reliability, ordering, congestion control, and built-in encryption (TLS 1.3).
No TCP head-of-line blocking. QUIC delivers packets from independent streams independently. A lost packet in stream A does not block delivery of packets in stream B. This is the most significant improvement over HTTP/2.
0-RTT connection establishment. QUIC combines the transport and TLS handshakes, allowing the client to send data on the first flight (using pre-shared session information). With TCP + TLS 1.3, this takes 1-RTT; with QUIC, it takes 0-RTT on repeat connections.
Connection migration. QUIC uses connection IDs instead of IP:port 4-tuples. When a client’s IP address changes (Wi-Fi to cellular, roaming), the connection survives. TCP connections break on IP changes.
User-space implementation. QUIC is implemented in user space, allowing faster iteration and deployment without kernel changes. This is particularly important for protocol evolution.
HTTP/3 defines a new set of frame types carried within QUIC streams:
Frame Type Purpose DATA Carries request/response body HEADERS Carries request/response headers (QPACK compressed) CANCEL_PUSH Cancels a pushed resource SETTINGS Configuration parameters PUSH_PROMISE Server push GOAWAY Graceful shutdown MAX_PUSH_ID Limits the number of push IDs RETRY_PRIORITY Adjusts stream priority
QPACK is the successor to HPACK, designed for the QUIC transport. It addresses HPACK’s head-of-line Blocking issue — in HTTP/2, a lost header frame blocks all subsequent streams because the decoder Needs the lost frame to update its dynamic table. QPACK allows the encoder and decoder to continue Processing independently by referencing the dynamic table without requiring in-order delivery.
HTTP/3 adoption is growing rapidly:
Supported by: Chrome, Firefox, Safari, Edge, curl, nginx, Cloudflare, Fastly, GoogleNot supported by: Some older CDN configurations, legacy load balancers, some enterprise proxiesQUIC on UDP: Requires UDP port 443 to be open. Some firewalls block non-TCP traffic on port 443, which breaks HTTP/3.# Check if a server supports HTTP/3
curl --http3 https://www.cloudflare.com
curl -I --alt-svc https://www.google.com # Look for Alt-Svc header
HTTP caching is one of the most important performance mechanisms. Proper caching configuration Reduces latency, server load, and bandwidth consumption.
The Cache-Control header controls caching behavior. It is the most important header for caching Configuration.
Request directives:
Directive Meaning no-cacheAlways validate with the origin server before using a cached response no-storeDo not store any part of the request or response max-age=60Accept cached responses no older than 60 seconds max-stale=30Accept cached responses up to 30 seconds past their expiration min-fresh=10Accept cached responses that will be fresh for at least 10 more seconds only-if-cachedOnly use cached responses; do not validate or fetch from origin
Response directives:
Directive Meaning publicAny cache (including CDNs and proxies) may store this response privateOnly the browser may cache this response no-cacheCache must validate with the origin before using the response no-storeDo not store any part of the response max-age=3600Cache for 3600 seconds (1 hour) s-maxage=300Override max-age for shared caches (CDNs, proxies) must-revalidateOnce stale, must validate before use (do not serve stale) stale-while-revalidate=600Serve stale response while revalidating in background immutableResponse will never change during max-age; do not revalidate
Conditional requests allow the client to validate whether a cached response is still fresh without Re-downloading the full content.
ETag (Entity Tag):
The server includes an ETag header with a unique identifier for the response content:
Cache-Control: max-age=3600
When the cache expires, the client sends the ETag back in an If-None-Match header:
If the content has not changed, the server responds with 304 Not Modified (no body):
HTTP/1.1 304 Not Modified
Last-Modified:
Similar mechanism using timestamps:
If-Modified-Since: Wed, 01 Jan 2024 00:00:00 GMT
HTTP/1.1 304 Not Modified
The Vary header tells caches which request headers affect the response content. A response with Vary: Accept-Encoding means the cache must store separate entries for different Accept-Encoding Values (e.g., one for gzip, one for br).
Vary: Accept-Encoding, Origin