HTTP in Practice: Methods, Status Codes, Caching, HTTPS and What HTTP/2 and HTTP/3 Change

Key takeaways

HTTP requests and responses, methods and status codes, HTTP/2 multiplexing and HTTP/3 over QUIC, HTTPS, caching, and REST API design—2026 practical guide.

HTTP underneath every API

HTTP (HyperText Transfer Protocol) is the application-layer protocol behind most websites and public APIs. Tabs, mobile backends, and synchronous microservice calls share the pattern: send a request, get a response—usually over HTTPS. Versions evolved from HTTP/1.1 simplicity to HTTP/2 multiplexing and HTTP/3 over QUIC. TLS 1.3, HSTS, and CSP are baseline security. This article connects spec mechanics to curl, REST, caches, and CDNs.

A useful way to hold HTTP in your head is as two layers that evolved separately. The semantics — methods, status codes, headers, caching rules — have barely changed in twenty-five years and are now defined once, in RFC 9110, for every version. The wire format — how those messages are turned into bytes and carried over the network — is what HTTP/1.1, HTTP/2 and HTTP/3 change. That is why an API designed for HTTP/1.1 works unchanged over HTTP/3: your framework sees the same request, and only the connection underneath is different. Most real-world HTTP bugs are semantic (a wrong status code, a missing cache header, a CORS rule), not version-specific.


From CERN to HTTP/3

HTTP/1.0 through HTTP/3

HTTP began at CERN in the early 1990s; RFC 1945 (HTTP/1.0) and RFC 2616 (HTTP/1.1) popularized it. RFC 2616 has since been replaced twice — by RFCs 7230–7235 in 2014 and by RFCs 9110–9112 in 2022 — so when a blog post quotes RFC 2616 it may describe rules that have changed. HTTP/2 added multiplexing on one TCP connection; HTTP/3 (RFC 9114) runs QUIC over UDP with integrated TLS 1.3, stream-level loss isolation, and connection migration—widely supported by CDNs and browsers in 2026.

Layer 7 over TCP or QUIC

HTTP is layer 7. Traditionally TCP carries HTTP/1.1 and HTTP/2; QUIC (UDP) carries HTTP/3. TLS encrypts bytes on the wire. After DNS, clients speak 443/tcp or 443/udp (QUIC).

Stateless, resource-oriented, cache-friendly

PropertyDescription
StatelessServers do not remember prior requests by default—sessions use cookies, tokens, server-side stores.
Request–responseClient initiates; server returns status, headers, optional body.
Resource-orientedURLs identify resources; methods express intent (GET read, POST create, …).
Content negotiationAccept, Accept-Encoding, Accept-Language, …
Cache-friendlyETag and Cache-Control reduce bandwidth and latency.

Statelessness is what makes HTTP scale horizontally: because each request carries everything the server needs (the URL, the auth token, the cookie), any server behind a load balancer can answer it. The state does not disappear — it moves into the cookie, the token, or a shared store like Redis. When a service breaks this rule by keeping per-user data in one server’s memory, you get the classic “logged out at random” bug as the load balancer sends consecutive requests to different instances, and sticky sessions become a workaround rather than a design.


Requests, methods, status codes, and HTTP/2-3 framing

Request / response

Messages have a start line, header fields, blank line, optional body.

Request (HTTP/1.1)

GET /api/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json

Response

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: private, max-age=60

In HTTP/1.1 this is literally the text sent over the connection, with lines ending in CRLF. The Host header is mandatory in HTTP/1.1 — a request without it gets 400 Bad Request — because one IP address usually serves many domains and the server needs to know which site you want. The blank line after the headers marks where the body starts; the body’s length comes from Content-Length or from Transfer-Encoding: chunked. Disagreement between a proxy and a backend about where one message ends and the next begins is the root of “request smuggling” vulnerabilities, one reason HTTP/2 and HTTP/3 replaced text parsing with explicit binary frames.

Methods (safe and idempotent)

MethodTypical useSafeIdempotent (ideal)
GETReadYesYes
HEADMetadata onlyYesYes
POSTCreate / processNoNot guaranteed
PUTReplace wholeNoYes
PATCHPartial updateNoVaries
DELETEDeleteNoYes

Idempotent: repeating the request leaves server state the same—important for retries.

These properties are not just labels; infrastructure acts on them. Safe methods must not change state, so crawlers, link prefetchers and browser pre-rendering feel free to issue GETs — which is why a “delete” link implemented as GET /items/5/delete eventually gets triggered by something that was only looking. Idempotent methods can be retried automatically: HTTP clients, proxies and service meshes will resend a GET, PUT or DELETE after a dropped connection, but they must not blindly resend a POST, because the first attempt may have succeeded before the connection failed. That is exactly the gap Idempotency-Key headers close for payments, as described below. Note that idempotency is about the resulting state, not the response: a second DELETE of the same resource typically returns 404 instead of 204, and that is still idempotent.

Status codes

  • 2xx: Success (200, 201, 204)
  • 3xx: Redirects (301, 302, 307, 308—method preservation differs)
  • 4xx: Client errors (400, 401, 403, 404, 429)
  • 5xx: Server errors (500, 502, 503, 504)

The first digit is what generic clients and monitoring act on, so getting the class right matters more than the exact code. Returning 200 OK with {"error": "not found"} in the body breaks caches, retries and error-rate dashboards, all of which read the status line. The redirect pair 301/302 historically allowed clients to change a POST into a GET on redirect, and browsers do; 307 and 308 were added to forbid that, so API redirects that must keep the method and body should use them. Among errors, 401 means “who are you?” (missing or invalid credentials) and 403 means “I know who you are, and no”; the gateway codes tell you where to look — 502 means the proxy got an invalid response from the upstream, 503 that the service is unavailable or overloaded, and 504 that the upstream did not answer within the proxy’s timeout.

Important headers

HeaderRole
HostSelects the site on a shared IP (virtual hosting); over HTTPS the TLS SNI extension does the same job one layer lower, before any HTTP is sent.
AuthorizationBearer, Basic, …
Content-TypeBody MIME type.
Cache-ControlCaching policy (max-age, no-store, …).
ETag / Last-ModifiedValidation and conditional GET.

HTTP/2: multiplexing

One TCP connection, many streams, HPACK header compression. Reduces application-level HOL blocking (TCP HOL can remain). Server push declined in favor of hints and priorities.

HTTP/1.1 can only have one outstanding response per connection (pipelining existed but was never reliably usable), so browsers opened about six connections per host and sites resorted to domain sharding, sprite sheets and file concatenation to reduce request counts. HTTP/2 splits each message into frames tagged with a stream ID and interleaves them on a single connection, so a slow response no longer blocks the ones behind it at the HTTP level, and HPACK avoids resending the same cookies and headers on every request. Those old HTTP/1.1 workarounds become unnecessary, and domain sharding actively hurts because it prevents connection reuse. The limit is TCP itself: TCP delivers bytes strictly in order, so one lost packet stalls every stream on the connection until it is retransmitted. On a lossy mobile link, a single HTTP/2 connection can therefore perform worse than six HTTP/1.1 connections.

Server push, which let a server send resources before the browser asked, turned out to be hard to use well — servers pushed resources the browser already had cached, wasting bandwidth. Chrome removed support in 2022, and the 103 Early Hints status code, which merely tells the browser what to start fetching, took its place.

HTTP/3: HTTP over QUIC

TLS 1.3 integrated with QUIC; loss affects one stream more often than the whole connection—helps Wi‑Fi and lossy links.

QUIC reimplements reliable, ordered delivery per stream on top of UDP, so a lost packet only delays the stream it belonged to. Because the handshake combines transport and TLS setup, a new connection needs one round trip instead of the two or three of TCP plus TLS, and a resumed connection can send data immediately (0-RTT, which is safe only for idempotent requests since it can be replayed). QUIC connections are identified by a connection ID rather than the IP/port tuple, so a phone switching from Wi-Fi to cellular keeps its connection. Browsers discover HTTP/3 through the Alt-Svc response header (or an HTTPS DNS record) on an earlier HTTP/2 connection, which is why the first visit to a site often uses HTTP/2 and later ones HTTP/3.

flowchart TB
  subgraph app [Application]
    H3[HTTP/3]
  end
  subgraph quic [Transport & crypto]
    Q[QUIC + TLS 1.3]
  end
  subgraph net [Network]
    U[UDP]
    IP[IP]
  end
  H3 --> Q --> U --> IP
sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: HTTP Request (method, path, headers)
  S->>C: HTTP Response (status, headers, body)

curl, REST design, and caching headers

curl

curl -sI https://example.com
curl -sS -X POST 'https://api.example.com/items' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"name":"demo"}'
curl -L -v 'https://example.com/path'
curl --http2 -sI https://example.com
curl --http3-only -sI https://cloudflare-quic.com/

-I sends a HEAD request and shows only the response headers, which is often all you need to check caching and redirects; note that some servers answer HEAD differently from GET, so confirm important findings with curl -sS -o /dev/null -D - URL, which performs a real GET and prints headers only. -v shows the TLS handshake, the negotiated protocol (look for ALPN: server accepted h2) and every header in both directions — the fastest way to see what a client actually sent. -L follows redirects, and combined with -v it shows the whole chain, which is how redirect loops are diagnosed. --http3-only requires a curl built with HTTP/3 support; many distribution builds lack it and report that the option is unsupported.

When I debug an API that “works in Postman but not in the app”, comparing curl -v output against the browser’s network tab almost always finds the difference: a missing Content-Type: application/json so the server parses an empty body, an Authorization header stripped by a redirect to another host, or a trailing-slash redirect that turns a POST into a GET.

REST API design

  • Resource URLs: /users/42/orders with nouns and hierarchy.
  • Status codes: 201 + Location for creates; 400/422 bad input; 401 vs 403.
  • Versioning: /v1/ prefix or Accept versioning.
  • Pagination: cursor or offset/limit; Link headers for next pages.
  • Idempotency: Idempotency-Key for payments and orders.

The idempotency key works by having the client generate a unique ID per logical operation and send it with the POST. The server stores the key with the result of the first execution; a retry with the same key gets the stored result instead of creating a second order. The key must be stored atomically with the operation’s effect (same database transaction), or a crash between the two reintroduces duplicates. On pagination, offset/limit is simple but slow on large tables and shifts results when rows are inserted between page requests; cursor pagination (?after=<last id>) is stable and uses an index, at the cost of not supporting “jump to page 50”.

Caching

  • Static assets: hashed filenames + Cache-Control: public, max-age=31536000, immutable.
  • HTML shell: short max-age; versioned JS/CSS long-lived.
  • APIs: private or no-store for personal data; ETag + max-age for safe public reads.
  • Vary: when responses differ by Accept-Encoding or Accept-Language.

The static-asset rule relies on a simple idea: if the file name contains a hash of its content (app.3f9a1c.js), the content at that URL can never change, so it can be cached for a year and marked immutable. Deploying a new version produces a new URL, and the HTML that references it is what needs a short cache lifetime. The mistake that breaks this is caching the HTML itself for a long time — users then keep loading an old page that points at old asset URLs, and a deployment appears not to take effect. Two directives are often confused: no-cache does not mean “do not cache”; it means “store it, but revalidate with the server before every use” (usually a cheap 304 Not Modified via ETag). no-store is the one that forbids storing the response at all. Finally, private keeps responses out of shared caches like CDNs; forgetting it on a personalized API response is how one user’s data ends up served to another from a CDN.


TLS 1.3, HSTS, CSP, and API security

HTTPS and TLS 1.3

Plain HTTP exposes credentials, cookies, and payloads. TLS 1.3 shortens handshakes and modernizes ciphers—manage chains, expiry, and SANs.

HSTS

Strict-Transport-Security forces HTTPS for the domain. Preload is a policy decision.

HSTS closes the gap that redirects leave open: a user who types example.com first makes a plain-HTTP request, and an attacker on the same network can intercept that request before the redirect to HTTPS happens. Once a browser has seen Strict-Transport-Security: max-age=31536000, it rewrites every future request to HTTPS itself. The risk is the flip side of that strength: if any subdomain still needs plain HTTP and you set includeSubDomains, browsers will refuse to reach it for the whole max-age, and there is no server-side way to undo it for users who already received the header. Starting with a short max-age and increasing it after checking every subdomain is the safe rollout; preload lists hard-code the rule into browsers and take months to remove.

CSP

Content-Security-Policy mitigates XSS—tighten script-src with nonces/hashes; use frame-ancestors against clickjacking.

CSP works by telling the browser which sources it may load scripts, styles and frames from, so an injected <script> from an attacker is blocked even if the HTML sanitization failed. A policy that includes 'unsafe-inline' for scripts gives up most of that protection, because inline injection is precisely the common XSS case; nonces (a random value per response that legitimate inline scripts must carry) keep inline scripts usable without that hole. Rolling out CSP on an existing site is easiest with Content-Security-Policy-Report-Only first, which reports violations without blocking anything, so you can see which third-party scripts would break.

API security

  • OAuth 2.0 / OIDC, short-lived access tokens, refresh flows.
  • CORS protects browsers—not server-to-server calls; still authenticate backends.

CORS is widely misunderstood as a server-side protection. It is a browser rule that relaxes the same-origin policy: the server says which other origins may read its responses from JavaScript, and the browser enforces it. An attacker’s script running on their own server ignores CORS entirely, so Access-Control-Allow-Origin is never a substitute for authentication. Also note that browsers reject Access-Control-Allow-Origin: * together with credentials (cookies); a server that needs cookies must echo a specific allowed origin and add Vary: Origin so caches do not serve one origin’s CORS headers to another.


Web, APIs, microservices, and webhooks

AreaNotes
WebHTML, JS, media, CDN edge caching.
APIsJSON, OpenAPI, gateway rate limits and auth.
MicroservicesHTTP/gRPC mix; retries, timeouts, circuit breakers.
WebhooksHMAC signatures, idempotent storage.

Keep-alive, compression, CDNs, and enabling HTTP/3

  • HTTP/1.1 keep-alive: reuse TCP (default today); many small requests favor h2/h3.
  • Compression: br / gzip for text—not much gain on already compressed media.
  • CDN: edge TLS, caching, purge APIs for invalidation.
  • HTTP/3: ensure UDP 443 on load balancers and firewalls when enabling QUIC.

Timeouts deserve a deliberate design because every hop has its own. A client, a CDN, a load balancer, a reverse proxy and the application server each close idle connections after some interval, and when an outer layer’s idle timeout is shorter than the inner one’s keep-alive, a reused connection can be closed at the moment a request is sent, producing sporadic 502 errors that no one can reproduce. The usual rule is that each layer’s keep-alive timeout should be longer than that of the layer in front of it — for example, the application’s keep-alive longer than the load balancer’s idle timeout. For compression, br gives smaller text than gzip at the cost of CPU, which is why it is typically applied to static assets ahead of time and at lower levels for dynamic responses; compressing JPEGs, videos or already-gzipped files only costs CPU.


CORS errors, timeouts, redirect loops, and stale caches

SymptomCheck
CORS errorsAccess-Control-Allow-Origin, preflight method/header allowlists, credentials.
TimeoutsClient/LB/server idle and read timeouts, downstream latency, DNS.
Redirect loops301/302 chains, http↔https, www normalization, cached 301.
Stale cacheCache-Control, CDN purge, URL versioning.

Redirect loops behind a CDN or load balancer have a very common cause: TLS is terminated at the proxy, which talks plain HTTP to the application. The application sees an HTTP request, redirects to HTTPS, the browser follows, the proxy again forwards plain HTTP, and the browser eventually gives up with ERR_TOO_MANY_REDIRECTS. The fix is to have the application trust the proxy’s X-Forwarded-Proto header (or configure the proxy to redirect instead of the app). Cached 301 responses make any redirect mistake sticky: browsers cache permanent redirects indefinitely by default, so after fixing the server you still see the old behavior until you clear the browser cache — use 302 while testing redirect rules and switch to 301 once they are right.


What to check first on any HTTP service

HTTP standardizes stateless request–response with rich semantics. HTTP/2 improves multiplexing; HTTP/3 improves lossy and mobile networks. HTTPS, HSTS, and CSP are baseline security. Use this checklist for public sites, REST APIs, and gateways: performance = cache + CDN + protocol version, safety = TLS + headers + least privilege.


References

  • IETF HTTPbis (RFC 9110–9114 family)
  • MDN: HTTP, CORS, CSP
  • OWASP: API Security, Transport Layer Protection

Frequently Asked Questions (FAQ)

Q. Why does the browser send an OPTIONS request before my POST, and why does it work from curl?

A. That is a CORS preflight. Browsers send it for cross-origin requests that are not “simple”, for example a JSON Content-Type, an Authorization header, or methods such as PUT, PATCH or DELETE. The server must answer the OPTIONS request with matching Access-Control-Allow-Origin, -Methods and -Headers values, or the real request is never sent. curl and server-to-server calls do not enforce CORS at all, which is why the same endpoint works there.