HTTP en profondeur - 06 - Les headers HTTP

Les 30 headers HTTP que chaque dev devrait connaître : Content-Type, Authorization, Cache-Control, Cookie et les autres.

06 - Les headers HTTP

Ce que tu vas apprendre

  • Les 30 headers les plus utilises et leur rôle precis
  • La différence entre headers de requête et de réponse
  • Pourquoi le prefixe X- n'est plus recommande
  • Les headers de sécurité a mettre partout

Prerequisites

Avoir lu 02 - La requête HTTP et 03 - La réponse HTTP.


Les headers HTTP, c'est un peu comme les metadonnees d'un email. Personne ne les lit sauf quand ca tombe en panne. Et quand ca tombe en panne, c'est toujours a cause d'un header manquant, mal orthographie ou avec la mauvaise valeur.

J'ai passe un apres-midi entier a debugger un problème de CORS qui venait d'un Content-type au lieu de Content-Type. Le serveur backend etait en Go et faisait une comparaison case-sensitive sur le header. Techniquement, les headers HTTP ne sont pas sensibles a la casse. Pratiquement, certaines implementations le sont quand meme.

Anatomie d'un header

Un header, c'est une paire clé-valeur séparée par deux-points et un espace :

Content-Type: application/json
^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^
nom            valeur

Quelques regles :

  • Le nom n'est pas sensible a la casse (content-type = Content-Type)
  • La valeur peut contenir des espaces
  • Un meme nom peut apparaître plusieurs fois (les valeurs sont concatenees avec des virgules)
  • Pas de limite officielle de taille, mais les serveurs imposent des limites (nginx : 8 KB par header par défaut)

Les headers de requête essentiels

Host

Host: api.example.com

Obligatoire en HTTP/1.1. Permet au serveur de savoir quel site tu vises quand plusieurs domaines sont heberges sur la meme IP (virtual hosting). Sans Host, le serveur ne sait pas ou router ta requête.

Content-Type

Content-Type: application/json
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=----FormBoundary

Le type MIME du body. Le serveur l'utilise pour savoir comment parser les donnees. Si tu envoies du JSON sans Content-Type: application/json, beaucoup de frameworks vont ignorer le body ou essayer de le parser comme du form-urlencoded.

Content-Length

Content-Length: 42

La taille du body en octets. Indispensable pour que le serveur sache quand le body se termine (sauf en chunked encoding).

Accept

Accept: application/json
Accept: text/html, application/xhtml+xml, */*;q=0.8

Ce que le client veut recevoir. Le q est le facteur de qualité (préférence) entre 0 et 1. */*;q=0.8 signifie "j'accepte n'importe quoi avec une préférence de 0.8." Le serveur utilise ca pour la negociation de contenu.

User-Agent

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
User-Agent: curl/8.4.0
User-Agent: MyApp/1.0

Identifie le client. Les User-Agent des navigateurs sont devenus des chaînes absurdes a cause de decennies de sniffing. Tout le monde pretend etre Mozilla parce que les serveurs servaient du contenu degrade aux clients non-Mozilla dans les annees 2000.

Pour tes API, mets un User-Agent descriptif. Ca aide énormément pour le debugging cote serveur.

Authorization

Authorization: Basic dXNlcjpwYXNz
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Les credentials d'authentification. Basic encode le login:password en base64 (pas de chiffrement, juste de l'encodage -- ne l'utilise jamais sans HTTPS). Bearer envoie un token opaque ou JWT.

Cookie: session=abc123; theme=dark; lang=fr

Les cookies stockes par le navigateur pour ce domaine. Envoyes automatiquement a chaque requête. On en parle en détail dans l'article 07.

Accept-Encoding

Accept-Encoding: gzip, deflate, br

Les algorithmes de compression que le client supporte. Le serveur choisit un de ces algorithmes et l'indique dans Content-Encoding.

Accept-Language

Accept-Language: fr-FR, fr;q=0.9, en;q=0.8

Les langues preferees du client. Utilise pour la negociation de contenu linguistique. En pratique, peu de serveurs l'exploitent -- la plupart gerent la langue via l'URL ou un cookie.

If-None-Match / If-Modified-Since

If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT

Headers conditionnels pour le cache. "Renvoie-moi la ressource seulement si elle a change." Si elle n'a pas change, le serveur répond 304 sans body.

Les headers de réponse essentiels

Content-Type (réponse)

Content-Type: application/json; charset=utf-8

Meme header qu'en requête, cote réponse. Mets toujours le charset pour les contenus textuels. charset=utf-8 devrait etre ton reflexe.

Set-Cookie: session=xyz; Path=/; HttpOnly; Secure; SameSite=Lax

Le serveur demande au navigateur de stocker un cookie. Chaque Set-Cookie ne concerne qu'un seul cookie (contrairement au header Cookie qui les envoie tous d'un coup).

Cache-Control

Cache-Control: public, max-age=3600
Cache-Control: no-store
Cache-Control: no-cache, must-revalidate

La directive de cache la plus puissante. max-age=3600 : cache pendant 1 heure. no-store : ne cache jamais. no-cache : cache mais revalide a chaque utilisation (le nom est trompeur).

ETag

ETag: "abc123"
ETag: W/"abc123"

Un identifiant de version de la ressource. Le W/ indique un ETag "faible" (weak) -- la ressource est semantiquement équivalente meme si les octets ne sont pas identiques.

Location

Location: /api/users/42
Location: https://example.com/new-url

La cible d'une redirection (3xx) ou l'URL d'une ressource créée (201).

Vary

Vary: Accept-Encoding, Accept-Language

Dit aux caches : "la réponse depend de ces headers de requête." Si le cache a une version gzip et que le client demande du brotli, le cache ne doit pas servir la version gzip.

Vary: * signifie "la réponse est unique a chaque requête, ne cache jamais." C'est l'équivalent de no-store mais plus subtil.

Connection

Connection: keep-alive
Connection: close

En HTTP/1.1, keep-alive est le défaut : la connexion TCP reste ouverte pour les requêtes suivantes. close dit au serveur de fermer la connexion apres cette réponse. En HTTP/2, ce header est ignore (le multiplexage gere tout).

Transfer-Encoding

Transfer-Encoding: chunked

Le body arrive en morceaux. Detaille dans l'article 03.

Content-Encoding

Content-Encoding: gzip
Content-Encoding: br

Le body est compresse avec cet algorithme. Ne pas confondre avec Transfer-Encoding : Content-Encoding est une propriété du contenu lui-meme, Transfer-Encoding est une propriété du transfert.

Server

Server: nginx/1.25.3

Identifie le serveur. Comme je l'ai mentionne dans l'article 03, c'est souvent trop d'information. Masque la version en production.

X-Forwarded-For

X-Forwarded-For: 203.0.113.50, 70.41.3.18

L'IP d'origine du client quand la requête passe par un ou plusieurs proxies. Chaque proxy ajoute l'IP du hop précédent. Le premier IP est celui du client original.

Attention : ce header est trivial a falsifier. Ne l'utilise pour la sécurité que si tu fais confiance a tous tes proxies intermediaires. Le standard moderne est Forwarded mais X-Forwarded-For reste bien plus repandu.

Strict-Transport-Security (HSTS)

Strict-Transport-Security: max-age=31536000; includeSubDomains

Dit au navigateur : "ne me contacte plus jamais en HTTP, seulement en HTTPS, pendant un an." Protection contre le downgrade d'HTTPS vers HTTP.

X-Content-Type-Options

X-Content-Type-Options: nosniff

Desactive le content sniffing du navigateur. A mettre partout.

Headers custom : plus de X-

Pendant des annees, la convention etait de prefixer les headers custom par X- : X-Request-ID, X-Correlation-ID. En 2012, la RFC 6648 a déprécié cette convention. La raison : quand un header custom finit par devenir standard, on se retrouve avec deux noms (X-Forwarded-For et Forwarded).

Aujourd'hui, la recommandation est de ne plus utiliser le prefixe X-. Mais en pratique, la moitie de l'internet continue de le faire. X-Request-ID reste bien plus courant que Request-ID.

Mon approche pragmatique : pour les nouveaux headers, pas de X-. Pour les existants, garde ce que tout le monde utilise.

Sur paltemps.fr, on utilise Request-ID pour le tracing et X-Forwarded-For parce que c'est ce que nos proxies envoient.

Résumé

  • Les headers sont des metadonnees clé-valeur, insensibles a la casse pour le nom
  • Host est le seul header obligatoire en HTTP/1.1
  • Content-Type avec charset=utf-8 devrait etre un reflexe
  • Cache-Control est plus puissant que Expires (qui est déprécié)
  • X-Content-Type-Options: nosniff et Strict-Transport-Security sur chaque réponse
  • Le prefixe X- n'est plus recommande pour les nouveaux headers
  • X-Forwarded-For est facile a falsifier

Precedent : 05 - Les codes de statut Suivant : 07 - Les cookies

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.