Jour 16 Day 16 · jeudi 20 août 2026 Thursday 20 August 2026 Web Fondamental
HTTP de A à Z HTTP from A to Z
Méthodes, codes de statut, cookies, HTTP/2 et 3 : le protocole que vous utilisez cent fois par jour — et sur lequel un recruteur peut creuser pendant vingt minutes. Methods, status codes, cookies, HTTP/2 and 3: the protocol you use a hundred times a day — and one an interviewer can dig into for twenty minutes.
L’essentiel
HTTP (HyperText Transfer Protocol) est le protocole requête/réponse du web : un client (navigateur, curl, appli mobile) envoie une requête, un serveur renvoie une réponse, et la conversation s’arrête là. Il est stateless : chaque requête est autonome, le serveur n’a aucune mémoire de la précédente — tout ce qui ressemble à une « session » est reconstruit par-dessus (cookies, tokens).
Une requête tient en trois parties : une ligne de requête (méthode + chemin + version, ex. GET /users/42 HTTP/1.1), des headers (paires clé-valeur : Host, Accept, Authorization…), et un body optionnel. La réponse est symétrique : ligne de statut (HTTP/1.1 200 OK), headers, body.
Les méthodes portent une sémantique : GET lit (safe, sans effet de bord), POST crée ou déclenche, PUT remplace intégralement, PATCH modifie partiellement, DELETE supprime, HEAD = GET sans body, OPTIONS interroge les capacités (c’est lui derrière le preflight CORS). Une méthode est idempotente si la rejouer N fois produit le même état qu’une fois : GET, PUT, DELETE le sont ; POST ne l’est pas — d’où le danger de re-soumettre un paiement.
Les codes de statut à connaître par cœur, par familles :
| Code | Signification | Le réflexe |
|---|---|---|
| 200 | OK | Succès générique |
| 201 | Created | Création réussie (+ header Location) |
| 204 | No Content | Succès sans body (DELETE typique) |
| 301 | Moved Permanently | Redirection définitive, cachée par le navigateur |
| 304 | Not Modified | Cache encore valide, pas de body renvoyé |
| 400 | Bad Request | Requête malformée (syntaxe, JSON invalide) |
| 401 | Unauthorized | Non authentifié (mal nommé !) |
| 403 | Forbidden | Authentifié mais pas les droits |
| 404 | Not Found | Ressource inexistante |
| 409 | Conflict | Conflit d’état (ex. email déjà pris) |
| 422 | Unprocessable Entity | Syntaxe OK, validation métier KO |
| 429 | Too Many Requests | Rate limiting (+ Retry-After) |
| 500 | Internal Server Error | Bug côté serveur |
| 502 | Bad Gateway | Le reverse proxy n’a pas eu de réponse valide du backend |
| 503 | Service Unavailable | Serveur surchargé ou en maintenance |
Comment ça marche
Un aller-retour complet, tel que curl -v le montre :
Client Serveur
│ GET /users/42 HTTP/1.1 │
│ Host: api.example.com │
│ Accept: application/json │
│─────────────────────────────────────────▶│
│ │ routing,
│ │ contrôleur, DB
│ HTTP/1.1 200 OK │
│ Content-Type: application/json │
│ Cache-Control: max-age=60 │
│ │
│ {"id": 42, "name": "Ada"} │
│◀─────────────────────────────────────────│
│ (connexion TCP gardée ouverte │
│ → keep-alive, requête suivante) │
Sous le capot, HTTP/1.1 circule sur TCP. Ouvrir une connexion TCP coûte un aller-retour (et un handshake TLS en HTTPS), donc depuis HTTP/1.1 la connexion est persistante par défaut (keep-alive) : plusieurs requêtes réutilisent le même tuyau. Mais en 1.1, les requêtes sur une connexion sont séquentielles : une réponse lente bloque toutes les suivantes — c’est le head-of-line blocking. Les navigateurs contournent en ouvrant ~6 connexions par domaine.
HTTP/2 règle ça avec le multiplexing : un seul tube TCP, mais des dizaines de streams binaires entrelacés — plus de blocage au niveau HTTP, plus de compression des headers (HPACK). Reste un HOL blocking au niveau TCP : un paquet perdu bloque tous les streams le temps de la retransmission. HTTP/3 supprime ce dernier verrou en remplaçant TCP par QUIC (sur UDP) : chaque stream est indépendant face à la perte de paquets, TLS 1.3 est intégré au handshake, et la connexion survit à un changement de réseau (Wi-Fi → 4G).
HTTPS = HTTP dans un tunnel TLS. En deux phrases : le client et le serveur négocient version et algorithmes, le serveur prouve son identité avec un certificat signé par une autorité de confiance, et un échange de clés (Diffie-Hellman éphémère) établit une clé de session symétrique. Tout le trafic HTTP est ensuite chiffré et authentifié avec cette clé — en TLS 1.3, le handshake tient en un seul aller-retour.
🎤 En entretien — « HTTP/1.1 vs 2 vs 3 » se résume en une phrase par version : 1.1 = une requête à la fois par connexion ; 2 = multiplexing sur un TCP, mais TCP re-bloque en cas de perte ; 3 = QUIC sur UDP, streams vraiment indépendants. Dire ça calmement vaut tous les détails.
Concepts clés à maîtriser
- Négociation de contenu : le client annonce ce qu’il accepte (
Accept: application/json,Accept-Language: fr,Accept-Encoding: gzip, br), le serveur répond avec ce qu’il a choisi (Content-Type,Content-Encoding) — ou406 Not Acceptable. - Cookies : le serveur envoie
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax, le navigateur le renvoie automatiquement sur chaque requête vers ce domaine.HttpOnly= invisible pour JavaScript (anti-XSS),Secure= HTTPS uniquement,SameSite= protection CSRF. C’est LE mécanisme qui fabrique de l’état sur un protocole stateless. - Cache :
Cache-Control: max-age=3600(fraîcheur), puis revalidation avecETag/If-None-Match→ réponse304sans body. Énorme levier de perf, souvent oublié des candidats. - Idempotence & safety : safe = ne modifie rien (GET, HEAD) ; idempotent = rejouable sans changer le résultat (GET, PUT, DELETE). Les proxies et les clients HTTP s’appuient dessus pour retenter automatiquement — retenter un POST, c’est risquer un doublon.
Hostet les reverse proxies : le headerHost(obligatoire en 1.1) permet à un seul serveur/IP de servir plusieurs domaines. C’est ce que fait Traefik ou nginx pour router vers le bon backend — et pourquoi un backend mal configuré renvoie 502/503.
À décortiquer soi-même avec curl :
curl -v https://api.github.com/users/octocat
# > GET /users/octocat HTTP/2 ← ligne de requête (curl a négocié h2)
# > Host: api.github.com ← quel site sur cette IP
# > Accept: */* ← négociation de contenu
# < ← (lignes '>' = envoyé, '<' = reçu)
# < HTTP/2 200 ← ligne de statut
# < content-type: application/json; charset=utf-8
# < etag: W/"a1b2c3" ← pour revalider le cache ensuite
# < x-ratelimit-remaining: 59 ← rate limiting annoncé en header
# < {"login":"octocat", ...} ← le body JSON
💡 Réflexe à montrer — face à un bug d’API, dégainer
curl -v(ou l’onglet Network) plutôt que relire le code : la moitié des problèmes se lisent dans les headers (mauvaisContent-Type, cookie absent, redirect inattendu, CORS).
En entretien
« Quelle différence entre 401 et 403 ? » — 401 Unauthorized signifie en réalité non authentifié : le serveur ne sait pas qui vous êtes (token absent/expiré), réponse accompagnée de WWW-Authenticate. 403 Forbidden : le serveur sait qui vous êtes, mais vous n’avez pas les droits. Se réauthentifier corrige un 401, jamais un 403.
« PUT vs PATCH ? » — PUT remplace la ressource entière (ce qui n’est pas envoyé est effacé) et est idempotent par définition. PATCH applique une modification partielle ; il n’est pas idempotent par contrat (même si en pratique il l’est souvent). Bonus : PUT peut créer la ressource à une URL connue du client.
« Pourquoi POST n’est-il pas idempotent, et quelles conséquences ? » — Rejouer un POST recrée une ressource ou re-déclenche l’action (double commande, double paiement). Conséquences : les clients/proxies ne le retentent pas automatiquement, le navigateur affiche « re-soumettre le formulaire ? », et les API sérieuses proposent une clé d’idempotence (Idempotency-Key, comme Stripe) pour rendre le retry sûr.
« Que change HTTP/2 par rapport à HTTP/1.1 ? » — Protocole binaire, multiplexing de streams sur une seule connexion TCP (fini les 6 connexions par domaine et le HOL blocking HTTP), compression des headers (HPACK), priorisation. Limite restante : un paquet TCP perdu bloque tous les streams — ce que HTTP/3/QUIC résout.
« Comment un serveur garde-t-il une session sur un protocole stateless ? » — Un cookie de session (ID opaque → état stocké côté serveur) ou un token auto-porteur type JWT (état signé côté client, envoyé dans Authorization: Bearer). Comparer : cookie = envoyé automatiquement (attention CSRF), token = à joindre soi-même (attention au stockage XSS-able).
Pièges & idées reçues
⚠️ Piège classique — répondre
200 OKavec{"error": "not found"}dans le body. Les caches, monitorings, retrys et clients générés se basent sur le code de statut, pas sur le body : un vrai 404/422 n’est pas un détail cosmétique.
- « 401 = pas les droits » — non, c’est 403. Le nom
Unauthorizedde 401 est un accident historique : lisez-le « Unauthenticated ». - « HTTPS cache tout » — le contenu et le chemin sont chiffrés, mais l’IP de destination reste visible, et le nom de domaine fuite via le SNI du handshake TLS (sauf ECH, encore peu déployé). HTTPS ≠ anonymat.
- GET avec un body : techniquement toléré, mais les proxies et caches l’ignorent ou le rejettent. Une recherche complexe → POST (ou le récent QUERY, encore en draft).
- Un 301 est mis en cache agressivement par le navigateur : une redirection permanente erronée peut « coller » très longtemps. Tester avec 302/307, promouvoir en 301 ensuite.
- CORS n’est pas une sécurité serveur : c’est le navigateur qui bloque la lecture cross-origin.
curlou un backend ignorent totalement CORS — ne jamais s’en servir comme contrôle d’accès.
Pour aller plus loin
- MDN — HTTP : la référence lisible (méthodes, headers, codes)
- RFC 9110 — HTTP Semantics : la spec actuelle, étonnamment claire sur l’idempotence
- web.dev — HTTP/2 puis HTTP/3 pour visualiser le multiplexing
- Jouer avec
curl -v, httpbin.org pour fabriquer n’importe quelle réponse, et l’onglet Network des DevTools (colonne Protocol : h2, h3)
The essentials
HTTP (HyperText Transfer Protocol) is the request/response protocol of the web: a client (browser, curl, mobile app) sends a request, a server returns a response, and the conversation ends there. It is stateless: every request stands alone, the server has no memory of the previous one — anything that looks like a “session” is rebuilt on top (cookies, tokens).
A request has three parts: a request line (method + path + version, e.g. GET /users/42 HTTP/1.1), headers (key-value pairs: Host, Accept, Authorization…), and an optional body. The response mirrors it: a status line (HTTP/1.1 200 OK), headers, body.
Methods carry semantics: GET reads (safe, no side effects), POST creates or triggers, PUT replaces entirely, PATCH modifies partially, DELETE removes, HEAD = GET without a body, OPTIONS asks about capabilities (it’s the method behind CORS preflight). A method is idempotent if replaying it N times produces the same state as once: GET, PUT, DELETE are; POST is not — hence the danger of resubmitting a payment.
The status codes to know by heart, by family:
| Code | Meaning | The reflex |
|---|---|---|
| 200 | OK | Generic success |
| 201 | Created | Successful creation (+ Location header) |
| 204 | No Content | Success without a body (typical DELETE) |
| 301 | Moved Permanently | Permanent redirect, cached by the browser |
| 304 | Not Modified | Cache still valid, no body returned |
| 400 | Bad Request | Malformed request (syntax, invalid JSON) |
| 401 | Unauthorized | Not authenticated (badly named!) |
| 403 | Forbidden | Authenticated but no permission |
| 404 | Not Found | Resource doesn’t exist |
| 409 | Conflict | State conflict (e.g. email already taken) |
| 422 | Unprocessable Entity | Syntax OK, business validation failed |
| 429 | Too Many Requests | Rate limiting (+ Retry-After) |
| 500 | Internal Server Error | Server-side bug |
| 502 | Bad Gateway | Reverse proxy got no valid answer from the backend |
| 503 | Service Unavailable | Server overloaded or in maintenance |
How it works
One full round trip, as curl -v shows it:
Client Server
│ GET /users/42 HTTP/1.1 │
│ Host: api.example.com │
│ Accept: application/json │
│─────────────────────────────────────────▶│
│ │ routing,
│ │ controller, DB
│ HTTP/1.1 200 OK │
│ Content-Type: application/json │
│ Cache-Control: max-age=60 │
│ │
│ {"id": 42, "name": "Ada"} │
│◀─────────────────────────────────────────│
│ (TCP connection kept open │
│ → keep-alive, next request) │
Under the hood, HTTP/1.1 rides on TCP. Opening a TCP connection costs a round trip (plus a TLS handshake for HTTPS), so since HTTP/1.1 connections are persistent by default (keep-alive): several requests reuse the same pipe. But in 1.1, requests on one connection are sequential: one slow response blocks all the following ones — that’s head-of-line blocking. Browsers work around it by opening ~6 connections per domain.
HTTP/2 fixes this with multiplexing: a single TCP pipe, but dozens of interleaved binary streams — no more HTTP-level blocking, plus header compression (HPACK). One HOL blocking remains at the TCP level: one lost packet stalls every stream until retransmission. HTTP/3 removes that last lock by replacing TCP with QUIC (over UDP): each stream is independent under packet loss, TLS 1.3 is built into the handshake, and the connection survives a network change (Wi-Fi → 4G).
HTTPS = HTTP inside a TLS tunnel. In two sentences: the client and server negotiate version and algorithms, the server proves its identity with a certificate signed by a trusted authority, and a key exchange (ephemeral Diffie-Hellman) establishes a symmetric session key. All HTTP traffic is then encrypted and authenticated with that key — in TLS 1.3 the handshake fits in a single round trip.
🎤 In an interview — “HTTP/1.1 vs 2 vs 3” boils down to one sentence per version: 1.1 = one request at a time per connection; 2 = multiplexing over one TCP, but TCP re-blocks on loss; 3 = QUIC over UDP, truly independent streams. Saying that calmly is worth all the details.
Key concepts to master
- Content negotiation: the client announces what it accepts (
Accept: application/json,Accept-Language: fr,Accept-Encoding: gzip, br), the server answers with what it picked (Content-Type,Content-Encoding) — or406 Not Acceptable. - Cookies: the server sends
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax, and the browser automatically sends it back on every request to that domain.HttpOnly= invisible to JavaScript (anti-XSS),Secure= HTTPS only,SameSite= CSRF protection. This is THE mechanism that manufactures state on a stateless protocol. - Caching:
Cache-Control: max-age=3600(freshness), then revalidation withETag/If-None-Match→ a304response with no body. A huge performance lever candidates often forget. - Idempotence & safety: safe = modifies nothing (GET, HEAD); idempotent = replayable without changing the outcome (GET, PUT, DELETE). Proxies and HTTP clients rely on this to retry automatically — retrying a POST risks a duplicate.
Hostand reverse proxies: theHostheader (mandatory in 1.1) lets a single server/IP serve several domains. That’s what Traefik or nginx use to route to the right backend — and why a misconfigured backend returns 502/503.
Dissect it yourself with curl:
curl -v https://api.github.com/users/octocat
# > GET /users/octocat HTTP/2 ← request line (curl negotiated h2)
# > Host: api.github.com ← which site on this IP
# > Accept: */* ← content negotiation
# < ← ('>' lines = sent, '<' = received)
# < HTTP/2 200 ← status line
# < content-type: application/json; charset=utf-8
# < etag: W/"a1b2c3" ← to revalidate the cache later
# < x-ratelimit-remaining: 59 ← rate limiting announced in a header
# < {"login":"octocat", ...} ← the JSON body
💡 Reflex to show — facing an API bug, reach for
curl -v(or the Network tab) before rereading the code: half the problems are readable in the headers (wrongContent-Type, missing cookie, unexpected redirect, CORS).
In an interview
“What’s the difference between 401 and 403?” — 401 Unauthorized actually means not authenticated: the server doesn’t know who you are (missing/expired token), and the response carries WWW-Authenticate. 403 Forbidden: the server knows who you are but you lack permission. Re-authenticating fixes a 401, never a 403.
“PUT vs PATCH?” — PUT replaces the whole resource (anything not sent is wiped) and is idempotent by definition. PATCH applies a partial modification; it is not idempotent by contract (even if in practice it often is). Bonus: PUT can create the resource at a URL the client knows.
“Why isn’t POST idempotent, and what are the consequences?” — Replaying a POST re-creates a resource or re-triggers the action (double order, double payment). Consequences: clients/proxies don’t auto-retry it, the browser asks “resubmit the form?”, and serious APIs offer an idempotency key (Idempotency-Key, like Stripe) to make retries safe.
“What does HTTP/2 change compared to HTTP/1.1?” — Binary protocol, multiplexing of streams over a single TCP connection (no more 6 connections per domain and HTTP HOL blocking), header compression (HPACK), prioritization. Remaining limit: one lost TCP packet stalls all streams — which HTTP/3/QUIC solves.
“How does a server keep a session on a stateless protocol?” — A session cookie (opaque ID → state stored server-side) or a self-contained token like a JWT (signed state on the client, sent in Authorization: Bearer). Compare: cookie = sent automatically (watch out for CSRF), token = attached manually (watch out for XSS-able storage).
Pitfalls & misconceptions
⚠️ Classic trap — returning
200 OKwith{"error": "not found"}in the body. Caches, monitoring, retries and generated clients rely on the status code, not the body: a real 404/422 is not a cosmetic detail.
- “401 = no permission” — no, that’s 403. The
Unauthorizedname of 401 is a historical accident: read it as “Unauthenticated”. - “HTTPS hides everything” — content and path are encrypted, but the destination IP stays visible, and the domain name leaks through the TLS handshake’s SNI (unless ECH, still barely deployed). HTTPS ≠ anonymity.
- GET with a body: technically tolerated, but proxies and caches ignore or reject it. A complex search → POST (or the recent QUERY method, still a draft).
- A 301 is cached aggressively by the browser: a wrong permanent redirect can “stick” for a very long time. Test with 302/307, promote to 301 afterwards.
- CORS is not server security: it’s the browser that blocks cross-origin reads.
curlor a backend ignore CORS entirely — never use it as access control.
Going further
- MDN — HTTP: the readable reference (methods, headers, codes)
- RFC 9110 — HTTP Semantics: the current spec, surprisingly clear on idempotence
- web.dev — HTTP/2 then HTTP/3 to visualize multiplexing
- Play with
curl -v, httpbin.org to craft any response, and the DevTools Network tab (Protocol column: h2, h3)