Tags: web-dev concept

HTTP Semantics

Date: 2026-08-17


What the parts of an HTTP message mean, as opposed to how they’re transmitted. Methods, status codes and headers are a contract that caches, proxies, crawlers and browsers all act on without asking you — so using them wrongly breaks things you never touched.


HTTP semantics are the agreed meanings of methods, status codes and headers. They survive across HTTP/1.1, /2 and /3 — those versions change the wire format, not the meaning — HTTP Versions.

Methods, and their contract

METHOD   SAFE  IDEMPOTENT  BODY   TYPICAL USE
GET       ✓        ✓        no    fetch
HEAD      ✓        ✓        no    headers only
OPTIONS   ✓        ✓        no    CORS preflight
PUT       ✗        ✓        yes   full replace
DELETE    ✗        ✓        no    remove
POST      ✗        ✗        yes   create, act
PATCH     ✗     depends     yes   partial update

Safe means no state change. Idempotent means repeating it is harmless — Idempotency.

These aren’t conventions, they’re licences. A browser will prefetch a GET, a proxy will cache one, a crawler will follow one, and a load balancer will retry an idempotent request. A GET that mutates state will therefore be triggered by things you didn’t write — this is how “the crawler deleted our content” happens.

Status codes that carry meaning

2xx  SUCCESS
200  OK
201  Created — with a Location header
204  No Content — success, nothing to send

3xx  REDIRECTION
301  Moved Permanently — cached hard,
     passes link equity
302  Found — temporary
304  Not Modified — your cached copy
     is still good
307/308  like 302/301 but the METHOD
     is preserved

4xx  CLIENT ERROR
400  Bad Request — malformed
401  Unauthorised — you are not
     authenticated
403  Forbidden — you are, and still
     may not
404  Not Found
409  Conflict — state collision
410  Gone — deliberately, permanently
422  Unprocessable — valid syntax,
     invalid content
429  Too Many Requests

5xx  SERVER ERROR
500  Internal Server Error
502  Bad Gateway — upstream broke
503  Service Unavailable — with Retry-After
504  Gateway Timeout

401 versus 403 is the pair to get right. 401 means “authenticate and try again”; 403 means “authentication won’t help”. Returning 401 for a permissions failure sends clients into a pointless login loop.

301 versus 302 matters for SEO. A permanent redirect passes signals and is cached aggressively — including by browsers, which makes a mistaken 301 painful to undo — Redirects and Link Equity.

Headers worth knowing by heart

REQUEST
Accept              what I can handle
Content-Type        what I'm sending
Authorization       credentials
If-None-Match       I have this ETag
Cookie              state

RESPONSE
Content-Type        what this is
Cache-Control       who may cache, how long
ETag                a version identifier
Location            where to go
Set-Cookie          store this
Vary                which request headers
                    change this response

Vary is the one that quietly breaks caching. It tells caches which request headers make the response different. Vary: Accept-Encoding is fine. Vary: User-Agent means a separate cache entry per browser string — effectively no caching at all — HTTP Caching, CDN Caching.

Conditional requests

The mechanism that makes revalidation cheap:

1  response includes ETag: "abc123"
2  browser caches it
3  later, browser sends
     If-None-Match: "abc123"
4  unchanged → 304 Not Modified,
     no body
   changed   → 200 with the new body

A 304 costs a round trip but no transfer, which is the right trade for large, rarely-changing resources and the wrong one for small ones on high-latency links.

Content negotiation

The client states preferences; the server chooses:

Accept: text/html, application/json;q=0.9
Accept-Encoding: br, gzip
Accept-Language: en-GB, en;q=0.8

Anything you negotiate on must appear in Vary, or a cache will serve one variant to everyone — the classic version of which is serving a Brotli-compressed body to a client that can’t decode it.

Practical rules

  • Use the method that matches the semantics, not the one that’s convenient
  • Return the specific status code. A 200 with {"error": ...} in the body defeats every layer that reads status codes — monitoring, retries, caches
  • Set Cache-Control deliberately on everything. Absent, caches guess, and their guesses differ
  • Retry-After on 429 and 503 turns a client’s blind retry storm into cooperation — Error Handling Strategies