📋 En-têtes HTTP
📖 Définition
Les en-têtes HTTP sont des métadonnées transmises entre le client et le serveur. Ils définissent des informations supplémentaires sur les requêtes et réponses, incluant l'authentification, le cache, le type de contenu, etc.
🎯 Comprendre par une analogie
Enveloppe postale
Message HTTP = Lettre
├─ Enveloppe (en-têtes)
│ ├─ Adresse de l'expéditeur (User-Agent)
│ ├─ Adresse du destinataire (Host)
│ ├─ Type d'envoi (Content-Type)
│ ├─ Niveau de priorité (Priority)
│ └─ Adresse de retour (Referer)
└─ Contenu de la lettre (corps)
En examinant l'enveloppe (en-têtes) :
- Identification de l'expéditeur
- Détermination du type de lettre
- Décision sur le traitement
💡 Structure des en-têtes
Format des en-têtes :
Nom-de-l'en-tête: valeur
Exemples :
Content-Type: application/json
Authorization: Bearer abc123
User-Agent: Mozilla/5.0
📬 En-têtes de requête (Request Headers)
Host
Nom d'hôte et port du serveur demandé
GET /api/users HTTP/1.1
Host: example.com
// fetch définit automatiquement l'en-tête Host
fetch('https://example.com/api/users');
Caractéristiques :
- Obligatoire depuis HTTP/1.1
- Permet l'hébergement de plusieurs domaines sur une même IP
- Défini automatiquement par le navigateur
User-Agent
Informations sur l'application cliente
GET /api/users HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
// Définir un User-Agent personnalisé en Node.js
fetch('https://api.example.com/users', {
headers: {
'User-Agent': 'MonApp/1.0'
}
});
Usages :
- Identification du navigateur/OS
- Collecte de statistiques
- Gestion de la compatibilité
- Détection de bots
Accept
Types de médias acceptés par le client
GET /api/users HTTP/1.1
Host: example.com
Accept: application/json, text/html
Accept-Language: fr-FR, en-US
Accept-Encoding: gzip, deflate, br
fetch('https://api.example.com/users', {
headers: {
'Accept': 'application/json',
'Accept-Language': 'fr-FR,fr;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br'
}
});
Types d'en-têtes Accept :
Accept
├─ Accept: application/json - Préfère JSON
├─ Accept: text/html - Préfère HTML
└─ Accept: */* - Accepte tous les types
Accept-Language
├─ Accept-Language: fr-FR - Préfère français
└─ Accept-Language: en-US,en;q=0.9 - Anglais acceptable (priorité moindre)
Accept-Encoding
├─ Accept-Encoding: gzip - Supporte gzip
└─ Accept-Encoding: br, gzip - Préfère Brotli, gzip possible
Accept-Charset (rarement utilisé)
└─ Accept-Charset: utf-8 - Encodage UTF-8
Authorization
Informations d'authentification
GET /api/profile HTTP/1.1
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// Authentification JWT
fetch('https://api.example.com/profile', {
headers: {
'Authorization': 'Bearer ' + token
}
});
// Authentification Basic
const credentials = btoa('utilisateur:motdepasse');
fetch('https://api.example.com/profile', {
headers: {
'Authorization': 'Basic ' + credentials
}
});
Méthodes d'authentification :
Token Bearer (le plus courant)
Authorization: Bearer <token>
├─ Utilisé avec JWT, OAuth 2.0
└─ Ex : Bearer eyJhbGci...
Authentification Basic
Authorization: Basic <credentials-base64>
├─ username:password encodé en Base64
├─ Non sécurisé (HTTPS obligatoire)
└─ Ex : Basic dXRpbGlzYXRldXI6bW90ZGVwYXNzZQ==
Clé API
Authorization: ApiKey <clé-api>
ou
X-API-Key: <clé-api>
Content-Type
Type MIME du corps de la requête
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
{
"name": "Jean Dupont",
"email": "jean@example.com"
}
// Envoi JSON
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Jean Dupont',
email: 'jean@example.com'
})
});
// Envoi de données de formulaire
const formData = new FormData();
formData.append('name', 'Jean Dupont');
formData.append('file', inputFichier.files[0]);
fetch('https://api.example.com/upload', {
method: 'POST',
body: formData
// Content-Type automatiquement défini (multipart/form-data)
});
Types Content-Type principaux :
application/json
├─ Données JSON
└─ Format le plus courant pour les API
application/x-www-form-urlencoded
├─ Données de formulaire (défaut)
└─ key1=value1&key2=value2
multipart/form-data
├─ Téléchargement de fichiers
└─ Composé de plusieurs parties
text/plain
├─ Texte simple
└─ Données basiques
application/xml
├─ Données XML
└─ API legacy
Cookie
Cookies enregistrés par le client
GET /api/profile HTTP/1.1
Host: example.com
Cookie: sessionId=abc123; userId=456
// Le navigateur envoie automatiquement les cookies
fetch('https://example.com/api/profile', {
credentials: 'include' // Inclure les cookies
});
Referer
URL de la page précédente
GET /api/products/123 HTTP/1.1
Host: example.com
Referer: https://example.com/products
Usages :
- Analyse du parcours
- Validation de sécurité
- Journalisation
Autres en-têtes de requête
Origin
├─ Origine de la requête
└─ Utilisé pour la vérification CORS
If-None-Match
├─ Comparaison avec la valeur ETag
└─ Validation de cache
If-Modified-Since
├─ Vérifier si modifié après une date
└─ Validation de cache
Range
├─ Demander uniquement une partie des données
└─ Ex : bytes=0-1023
📤 En-têtes de réponse (Response Headers)
Content-Type
Type MIME du corps de la réponse
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"id": 123,
"name": "Jean Dupont"
}
fetch('https://api.example.com/users/123')
.then(response => {
console.log(response.headers.get('Content-Type'));
// "application/json; charset=utf-8"
return response.json();
});
Content-Length
Taille du corps de la réponse (en octets)
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 256
{"id":123,"name":"Jean Dupont"}
Set-Cookie
Enregistrement de cookies côté client
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict
Set-Cookie: userId=456; Max-Age=3600
// Définir un cookie côté serveur (Node.js/Express)
res.cookie('sessionId', 'abc123', {
httpOnly: true, // Bloque l'accès JavaScript
secure: true, // HTTPS uniquement
sameSite: 'strict',
maxAge: 3600000 // 1 heure
});
Attributs des cookies :
HttpOnly
├─ Accès JavaScript impossible
└─ Protection contre les attaques XSS
Secure
├─ Transmission HTTPS uniquement
└─ Protection contre les attaques de l'homme du milieu
SameSite
├─ Strict : uniquement sur le même site
├─ Lax : autorisation partielle entre sites
└─ None : autorisation totale (Secure obligatoire)
Max-Age / Expires
├─ Max-Age: 3600 (en secondes)
└─ Expires: Mer, 21 Oct 2025 07:28:00 GMT
Domain / Path
├─ Domain: example.com (inclut les sous-domaines)
└─ Path: / (tous les chemins)
Cache-Control
Politique de mise en cache
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
ETag: "abc123"
Last-Modified: Mer, 26 Jan 2025 10:00:00 GMT
fetch('https://api.example.com/users')
.then(response => {
const cacheControl = response.headers.get('Cache-Control');
console.log(cacheControl); // "public, max-age=3600"
});
Directives de cache :
Cache-Control: no-cache
├─ Validation serveur avant utilisation
└─ Garantit les données les plus récentes
Cache-Control: no-store
├─ Aucune mise en cache
└─ Données sensibles (mots de passe, etc.)
Cache-Control: public, max-age=3600
├─ Autorisation du cache public
├─ Mise en cache pendant 1 heure
└─ Ressources statiques (images, CSS, etc.)
Cache-Control: private, max-age=3600
├─ Mise en cache par navigateur uniquement
└─ Données spécifiques à l'utilisateur
Cache-Control: must-revalidate
├─ Validation obligatoire à l'expiration
└─ Prévention des données obsolètes
Location
Emplacement de redirection ou de la ressource créée
HTTP/1.1 201 Created
Location: /api/users/123
HTTP/1.1 301 Moved Permanently
Location: https://example.com/nouvelle-page
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Jean Dupont' })
})
.then(response => {
if (response.status === 201) {
const location = response.headers.get('Location');
console.log('Nouvelle ressource :', location); // "/api/users/123"
}
});
Access-Control-* (CORS)
Partage de ressources inter-origines
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
// Configuration CORS côté serveur (Node.js/Express)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
En-têtes CORS :
Access-Control-Allow-Origin
├─ Origine autorisée
├─ * : toutes les origines (attention sécurité)
└─ https://example.com : origine spécifique
Access-Control-Allow-Methods
├─ Méthodes HTTP autorisées
└─ GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers
├─ En-têtes de requête autorisés
└─ Content-Type, Authorization
Access-Control-Allow-Credentials
├─ Autorisation des cookies
└─ true (incompatible avec Origin *)
Access-Control-Max-Age
├─ Durée de cache des résultats de préflight (secondes)
└─ 86400 (24 heures)
🔐 En-têtes de sécurité
Strict-Transport-Security (HSTS)
Forçage HTTPS
HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains
// Configuration serveur (Node.js/Express)
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
next();
});
Content-Security-Policy (CSP)
Protection contre les attaques XSS
HTTP/1.1 200 OK
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.com
Directives CSP :
default-src 'self'
├─ Politique par défaut : même origine
└─ Valeur par défaut pour tous les types de ressources
script-src 'self' https://trusted.com
├─ Limitation des sources de scripts
└─ Protection contre les attaques XSS
style-src 'self' 'unsafe-inline'
├─ Limitation des sources de feuilles de style
└─ Autorisation des styles en ligne
img-src * data:
├─ Limitation des sources d'images
└─ Toutes les origines, URI data autorisées
frame-ancestors 'none'
├─ Blocage de l'insertion d'iframe
└─ Protection contre le clickjacking
X-Content-Type-Options
Prévention du MIME sniffing
HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
X-Frame-Options
Protection contre le clickjacking
HTTP/1.1 200 OK
X-Frame-Options: DENY
Options :
DENY
├─ Blocage total de l'insertion dans un iframe
└─ Le plus sécurisé
SAMEORIGIN
├─ Autorisation uniquement sur la même origine
└─ Couramment utilisé
ALLOW-FROM https://example.com
├─ Autorisation pour une origine spécifique
└─ Compatibilité avec les navigateurs anciens
📊 En-têtes personnalisés
Préfixe X- (non standard, legacy)
GET /api/users HTTP/1.1
Host: example.com
X-API-Key: abc123xyz
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
fetch('https://api.example.com/users', {
headers: {
'X-API-Key': 'abc123xyz',
'X-Request-ID': crypto.randomUUID()
}
});
En-têtes personnalisés courants :
X-API-Key
├─ Authentification par clé API
└─ Alternative : en-tête Authorization
X-Request-ID
├─ ID unique pour le suivi de requête
└─ Journalisation, débogage
X-Forwarded-For
├─ IP d'origine via proxy
└─ Ex : X-Forwarded-For: 203.0.113.1, 198.51.100.178
X-Real-IP
├─ IP client réelle
└─ Utilisé par Nginx, etc.
X-Correlation-ID
├─ Suivi entre services dans les microservices
└─ Systèmes distribués
💡 Exemple pratique complet
Requête/réponse complète
Requête :
POST /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: application/json
Accept-Language: fr-FR,fr;q=0.9
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Length: 58
Origin: https://example.com
Referer: https://example.com/register
{
"name": "Jean Dupont",
"email": "jean@example.com"
}
Réponse :
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Content-Length: 156
Location: /api/users/123
Cache-Control: no-cache, no-store, must-revalidate
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"id": 123,
"name": "Jean Dupont",
"email": "jean@example.com",
"createdAt": "2025-01-26T10:00:00Z"
}
Lecture/écriture d'en-têtes (JavaScript)
// Configuration des en-têtes de requête
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
'Accept': 'application/json',
'X-Request-ID': crypto.randomUUID()
},
body: JSON.stringify({
name: 'Jean Dupont',
email: 'jean@example.com'
})
});
// Lecture des en-têtes de réponse
const contentType = response.headers.get('Content-Type');
const location = response.headers.get('Location');
const requestId = response.headers.get('X-Request-ID');
console.log('Content-Type :', contentType);
console.log('Location :', location);
console.log('ID de requête :', requestId);
// Affichage de tous les en-têtes
response.headers.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
Configuration des en-têtes côté serveur (Node.js/Express)
app.post('/api/users', (req, res) => {
// Création d'un nouvel utilisateur
const newUser = {
id: 123,
name: req.body.name,
email: req.body.email
};
// Configuration des en-têtes de réponse
res.status(201);
res.set({
'Content-Type': 'application/json',
'Location': `/api/users/${newUser.id}`,
'Cache-Control': 'no-cache, no-store, must-revalidate',
'X-Request-ID': req.headers['x-request-id'] || generateId(),
'Strict-Transport-Security': 'max-age=31536000',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY'
});
// Configuration des cookies
res.cookie('sessionId', 'abc123', {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000
});
res.json(newUser);
});
🤔 Questions fréquentes
Q1. Règles de nommage des en-têtes personnalisés ?
R :
Passé (préfixe X-) :
X-API-Key: abc123
X-Custom-Header: value
Actuellement (recommandé) :
API-Key: abc123
Custom-Header: value
Raisons :
- Préfixe X- déconseillé par la RFC 6648
- Prévention des confusions lors de la standardisation
- Quelques en-têtes X- largement utilisés restent valides
Exceptions :
X-Forwarded-For - Très largement utilisé
X-Real-IP - Standard Nginx
X-Request-ID - Standard de journalisation/suivi
Q2. Limites de taille des en-têtes ?
R :
Limites courantes :
├─ Apache : 8 Ko (défaut)
├─ Nginx : 4-8 Ko
├─ Node.js : 80 Ko (http.maxHeaderSize)
└─ Cloudflare : 32 Ko
En cas de dépassement :
- 431 Champs d'en-tête de requête trop volumineux
- 413 Entité de requête trop volumineuse
Solutions :
1. Utiliser le corps au lieu des en-têtes
2. Réduire la longueur des tokens
3. Modifier la configuration serveur (avec précaution)
❌ Mauvais exemple :
Authorization: Bearer <token JWT de 5 Ko>
✅ Bon exemple :
- Tokens courts
- ID de session + stockage côté serveur
Q3. Distinction majuscules/minuscules ?
R :
Noms d'en-têtes : non sensible à la casse
├─ Content-Type = content-type = CONTENT-TYPE
└─ HTTP/2 convertit tout en minuscules
Valeurs d'en-têtes : sensible à la casse
├─ Content-Type: application/json (OK)
├─ Content-Type: Application/JSON (KO)
└─ La valeur respecte la casse
Recommandations :
- Noms d'en-têtes : première lettre de chaque mot en majuscule (Content-Type)
- Valeurs : selon la spécification
Q4. Est-il sûr de placer des informations sensibles dans les en-têtes ?
R :
✅ Utilisation sécurisée des en-têtes :
Authorization: Bearer <token>
├─ HTTPS obligatoire
├─ Masquage des logs
└─ Durée de vie courte
❌ Utilisation risquée :
Authorization: Bearer <token> (HTTP)
├─ Transmission en texte clair
└─ Vulnérable aux attaques de l'homme du milieu
X-API-Key: <secret>
├─ Risque d'exposition dans les URL
└─ Présent dans l'historique du navigateur
Précautions :
1. HTTPS obligatoire
2. Exclure les en-têtes sensibles des logs
3. Visible dans les outils de développement du navigateur
4. Transmis aux proxies, CDN
Q5. Qu'est-ce qu'une requête Preflight ?
R :
Preflight = Requête préliminaire CORS
Quand se produit-elle ?
├─ Méthodes autres que GET, POST, HEAD
├─ Utilisation d'en-têtes personnalisés
└─ Content-Type spécifique (hors application/json, etc.)
Processus :
1. Requête OPTIONS (Preflight)
OPTIONS /api/users HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
2. Réponse serveur
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
3. Requête réelle
POST /api/users HTTP/1.1
Content-Type: application/json
Authorization: Bearer token
...
Optimisation :
- Mise en cache via Access-Control-Max-Age (24 heures)
- Respect des conditions de requête simple (éviter Preflight)
🎓 Mise en pratique
1. Débogage des en-têtes
// Journalisation de tous les en-têtes de requête/réponse
async function debugFetch(url, options = {}) {
console.log('=== Requête ===');
console.log('URL :', url);
console.log('Méthode :', options.method || 'GET');
console.log('En-têtes :', options.headers || {});
const response = await fetch(url, options);
console.log('=== Réponse ===');
console.log('Statut :', response.status, response.statusText);
console.log('En-têtes :');
response.headers.forEach((value, key) => {
console.log(` ${key}: ${value}`);
});
return response;
}
// Utilisation
await debugFetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ name: 'Jean Dupont' })
});
2. Middleware d'en-têtes (Express)
// Configuration automatique des en-têtes de sécurité
function securityHeaders(req, res, next) {
res.set({
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'"
});
next();
}
// Configuration CORS
function corsHeaders(req, res, next) {
res.set({
'Access-Control-Allow-Origin': req.headers.origin || '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
});
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
}
app.use(securityHeaders);
app.use(corsHeaders);
🔗 Documents associés
- Qu'est-ce que HTTP ? - Concepts de base de HTTP
- Méthodes HTTP - GET, POST, PUT, DELETE
- Codes de statut HTTP - 200, 404, 500
- Cookies et sessions - Gestion des états
- Qu'est-ce que CORS ? - Politique de partage inter-origines
- Qu'est-ce que HTTPS ? - Communication sécurisée
🎬 Conclusion
Les en-têtes HTTP sont un moyen crucial de transmettre des métadonnées entre clients et serveurs. L'utilisation appropriée des en-têtes améliore la sécurité, les performances et la compatibilité !
Prochaine étape : Lisez Cookies et sessions pour comprendre comment surmonter la nature sans état de HTTP.