Filter concepts by levelShowing all levels.

Python · HTTP

HTTP fundamentals

Methods, status codes, headers, cookies, and the negotiation/caching/connection-reuse mechanics — verified against live endpoints

  • Safe + idempotentthe matrix that decides what is safe to auto-retry
  • 2xx/3xx/4xx/5xxthe status line, not the body, is what every layer inspects
  • ETag + If-None-Matcha real 304 skips the body transfer entirely
  • Keep-alive poolingreuse one Client — a fresh one pays a handshake every call

1.HTTP methods

Each HTTP method is a verb that states intent — GET reads, POST creates or triggers an action, PUT/PATCH update, DELETE removes — and two properties, safety and idempotency, decide whether a client or a proxy can retry a request automatically.

Key features

  • Safe methods (GET, HEAD, OPTIONS) never change server state — a browser, proxy, or crawler can call them freely
  • Idempotent methods (GET, PUT, DELETE, HEAD, OPTIONS) produce the same end state no matter how many times they repeat
  • POST is neither safe nor idempotent by default — calling it twice can create two resources, which is why "retry a POST" is dangerous
  • PUT replaces a resource ENTIRELY with the request body; PATCH applies a PARTIAL update — sending a partial body to PUT can null out omitted fields
  • HEAD returns the same headers a GET would, with no body — used to check existence/size/last-modified cheaply

The seven methods

The seven methods
MethodPurposeSafeIdempotent
GETRead a resourceYesYes
POSTCreate a resource / trigger an actionNoNo
PUTReplace a resource entirelyNoYes
PATCHPartially update a resourceNoNo
DELETERemove a resourceNoYes
OPTIONSDiscover allowed methods/CORS preflightYesYes
HEADSame as GET, headers only, no bodyYesYes
Safe to retry, or not — by method

GET, HEAD, OPTIONS

safe AND idempotent — always fine to retry

PUT, DELETE

idempotent, not safe — retry is fine, state already changes

POST, PATCH

neither — a blind retry can create a duplicate

  • GET, HEAD, OPTIONS — safe AND idempotent — always fine to retry
  • PUT, DELETE — idempotent, not safe — retry is fine, state already changes
  • POST, PATCH — neither — a blind retry can create a duplicate

Command

python
import httpx

client.get(url)                       # safe, idempotent
client.post(url, json={'name': 'x'})  # NOT idempotent -- calling twice creates two
client.put(url, json=full_resource)   # idempotent -- same end state every time
client.patch(url, json={'name': 'y'}) # NOT idempotent by the spec's own definition
client.delete(url)                    # idempotent -- deleting twice ends the same way

Safe methods never change state; idempotent methods are safe to retry — POST and PATCH are neither by default, so retry them only with a dedupe key.

2.HTTP status codes

A status code is a 3-digit number in the first line of every HTTP response, grouped by its first digit into five classes — 2xx success, 3xx redirect, 4xx the client's fault, 5xx the server's fault — and picking the specific, correct code (not just 200 or 500 for everything) is what makes an API self-describing.

Key features

  • 2xx: 200 OK (general success), 201 Created (POST that made something new), 204 No Content (success, empty body — common for DELETE)
  • 3xx: 301/308 permanent redirect, 302/307 temporary redirect, 304 Not Modified (conditional GET, cache still valid)
  • 4xx: 400 Bad Request (malformed input), 401 Unauthorized (no/bad credentials), 403 Forbidden (authenticated but not allowed), 404 Not Found, 409 Conflict, 422 Unprocessable Entity (validation failed), 429 Too Many Requests
  • 5xx: 500 Internal Server Error (unhandled exception), 502 Bad Gateway, 503 Service Unavailable (temporarily down/overloaded), 504 Gateway Timeout
  • Python's stdlib http.HTTPStatus enum gives every code a name and description — HTTPStatus.NOT_FOUND == 404

The five classes

The five classes
RangeMeaningCommon codes
2xxSuccess200, 201, 204
3xxRedirection301, 302, 304
4xxClient error400, 401, 403, 404, 409, 422, 429
5xxServer error500, 502, 503, 504
The five classes, by first digit

2xx Success

200 OK, 201 Created, 204 No Content

3xx Redirect

301 permanent, 302 temporary, 304 Not Modified

4xx Client error

400, 401, 403, 404, 409, 422, 429

5xx Server error

500, 502, 503, 504

  • 2xx Success — 200 OK, 201 Created, 204 No Content
  • 3xx Redirect — 301 permanent, 302 temporary, 304 Not Modified
  • 4xx Client error — 400, 401, 403, 404, 409, 422, 429
  • 5xx Server error — 500, 502, 503, 504

Command

python
from http import HTTPStatus

HTTPStatus.OK                    # 200
HTTPStatus.CREATED               # 201
HTTPStatus.NO_CONTENT            # 204
HTTPStatus.NOT_FOUND             # 404
HTTPStatus.UNPROCESSABLE_ENTITY  # 422
HTTPStatus.TOO_MANY_REQUESTS     # 429

2xx success / 3xx redirect / 4xx client's fault / 5xx server's fault — the status line, not the body, is what every intermediate layer inspects.

3.HTTP headers

Headers are name: value metadata pairs sent alongside every request and response, separate from the body — they describe the message (Content-Type, Content-Length), control it (Cache-Control, Authorization), or negotiate it (Accept, Accept-Encoding), without the receiver having to parse the body to know how to handle it.

Key features

  • Header names are case-insensitive (Content-Type == content-type); most HTTP libraries normalize this for you
  • Content-Type on a request/response describes the BODY's format (application/json, text/html, multipart/form-data)
  • Authorization carries credentials (Bearer <token>, Basic <base64>) — never put credentials in a URL query string
  • A single header name CAN repeat (e.g. multiple Set-Cookie lines) — some HTTP client APIs only expose the last one by default
  • Custom headers conventionally (not required) use an X- prefix (X-Request-ID), though modern practice increasingly skips it

Common headers by purpose

Common headers by purpose
HeaderDirectionPurpose
Content-TypeRequest & responseFormat of the body (application/json, text/html)
AuthorizationRequestCredentials (Bearer token, Basic auth)
AcceptRequestFormats the client can handle back (content negotiation)
Accept-EncodingRequestCompression the client can decode (gzip, br)
Cache-ControlResponseCaching rules (max-age, no-store, public/private)
ETagResponseA version fingerprint for conditional requests

Command

python
import httpx

response = client.get(
    url,
    headers={
        'Authorization': f'Bearer {token}',
        'Accept': 'application/json',
    },
)
print(response.headers['content-type'])   # reading is case-insensitive too

Headers describe/control the message without opening the body — credentials belong in Authorization, never in the URL.

4.Cookies and sessions

A cookie is a small piece of data the server asks the browser to store and resend on every subsequent request to the same site — it is what makes HTTP, which has no memory between requests on its own, able to recognize "this is the same visitor" across a session.

Key features

  • Set-Cookie (response header) creates/updates a cookie; the browser echoes it back via the Cookie header on later requests to the same site
  • HttpOnly prevents JavaScript from reading the cookie (document.cookie) — the standard defense against a stolen session cookie via XSS
  • Secure means the browser only sends the cookie over HTTPS, never plain HTTP
  • SameSite (Strict/Lax/None) restricts whether the cookie is sent on cross-site requests — the standard CSRF countermeasure at the cookie level
  • A "session" is server-side state keyed by a session ID stored in a cookie — the cookie itself typically holds only that ID, not the session data

Cookie attributes

Cookie attributes
AttributeEffect
HttpOnlyNot readable from JavaScript — blocks theft via XSS
SecureOnly sent over HTTPS
SameSite=StrictNever sent on a cross-site request
SameSite=LaxSent on top-level navigation, not on cross-site subrequests (default in modern browsers)
Max-Age / ExpiresHow long the browser keeps the cookie before dropping it
A cookie makes stateless HTTP recognize the same visitor
Browser
Server
  1. 1GET /login
  2. 2Set-Cookie: session_id=abc123; HttpOnly; Secureserver stamps the wristband once
  3. 3GET /dashboard (Cookie: session_id=abc123)browser echoes it back automatically
  4. 4looks up session server-side, from session_id alone
  1. Browser → Server: GET /login
  2. Server → Browser: Set-Cookie: session_id=abc123; HttpOnly; Secure (server stamps the wristband once)
  3. Browser → Server: GET /dashboard (Cookie: session_id=abc123) (browser echoes it back automatically)
  4. Server → Browser: looks up session server-side, from session_id alone

Command

python
import http.cookies

cookie = http.cookies.SimpleCookie()
cookie['session_id'] = 'abc123'
cookie['session_id']['httponly'] = True
cookie['session_id']['secure'] = True
cookie['session_id']['samesite'] = 'Lax'
cookie['session_id']['max-age'] = 3600
print(cookie.output())   # the literal Set-Cookie header line

HttpOnly blocks JS theft, Secure blocks plaintext transmission, SameSite blocks cross-site sending — and cookies should hold a session ID, never trusted data itself.

5.Content negotiation, compression, caching, and connection reuse

Content negotiation lets a client and server agree on a response FORMAT (Accept) and encoding (Accept-Encoding for compression) before the body is sent; caching headers let a client skip re-fetching unchanged data; and keep-alive/connection pooling let multiple requests reuse one already-open TCP connection instead of paying a new handshake every time.

Key features

  • Accept (request) states which response formats the client can handle; the server picks one and confirms it via Content-Type
  • Accept-Encoding (request) lists compressions the client can decode (gzip, br, zstd); Content-Encoding (response) states which was used
  • Cache-Control: max-age=N lets a client reuse a cached response for N seconds with zero network round-trip at all
  • ETag + If-None-Match is a conditional request: the server returns 304 Not Modified (empty body) if the client's cached copy is still current
  • Keep-alive reuses one open TCP (and TLS) connection across multiple requests; connection pooling is a client holding several such reusable connections ready, avoiding a fresh handshake per request

Negotiation and performance headers

Negotiation and performance headers
HeaderDirectionPurpose
AcceptRequestFormats the client can handle back
Accept-EncodingRequestCompressions the client can decode
Content-EncodingResponseCompression actually used on this body
Cache-ControlResponseHow long / whether a response can be reused from cache
ETag / If-None-MatchResponse / RequestConditional re-validation — 304 if unchanged
Connection: keep-aliveBothReuse this TCP connection for more requests
A conditional request — 304 skips the body entirely
Client
Server
  1. 1GET /etag/abc123
  2. 2200 OK, ETag: abc123
  3. 3GET /etag/abc123 (If-None-Match: abc123)asks: has this changed since abc123?
  4. 4304 Not Modified — no body at all
  1. Client → Server: GET /etag/abc123
  2. Server → Client: 200 OK, ETag: abc123
  3. Client → Server: GET /etag/abc123 (If-None-Match: abc123) (asks: has this changed since abc123?)
  4. Server → Client: 304 Not Modified — no body at all

Command

python
import httpx

with httpx.Client() as client:            # a Client pools/reuses connections
    r1 = client.get(url)                  # first request opens a connection
    r2 = client.get(url)                  # second request reuses it -- no new handshake

    cached = client.get(url, headers={'If-None-Match': r1.headers['etag']})
    print(cached.status_code)             # 304 if unchanged -- no body sent at all

Accept/Accept-Encoding negotiate the response shape before it is sent; ETag + If-None-Match can turn a re-fetch into an empty 304; reuse one Client to actually get connection pooling.

Remember

  • Safe methods never change state; idempotent methods are safe to retry — POST and PATCH are neither by default.
  • 2xx success / 3xx redirect / 4xx client's fault / 5xx server's fault — always match the status to the real outcome.
  • HttpOnly blocks JS theft, Secure blocks plaintext transmission, SameSite blocks cross-site sending.
  • ETag + If-None-Match can turn a re-fetch into an empty 304 — reuse one Client to actually get connection pooling.

Pro tip

  • Retry a non-idempotent POST only with a server-recognized Idempotency-Key, never blindly.
  • Use the http.HTTPStatus enum by name in code (HTTPStatus.CREATED) instead of a bare literal.
  • Header lookups are case-insensitive — response.headers.get("content-type") finds "Content-Type" too.
  • Never put an API token in a URL query string — it ends up in access logs, browser history, and proxy logs.

Next up

  • Security-related web concepts

Sources

  • MDN HTTP — developer.mozilla.org/en-US/docs/Web/HTTP
  • Every request run for real against httpbin.org with httpx 0.28.1 · checked 2026-08-21

Spotted a mistake on this page? Tell us.