Le middleware headers de Traefik est le fourre-tout pour la manipulation d’en-têtes HTTP — en-têtes de sécurité (HSTS, X-Frame-Options, X-Content-Type-Options), CORS (origine, méthodes, identifiants), et réécriture arbitraire requête/réponse. Les valeurs par défaut sont raisonnables mais chaque champ a sa particularité : HSTS ne s’applique que quand la réponse est en HTTPS ; certains réglages CORS interagissent de façon non évidente ; et la chaîne vide customResponseHeaders est la façon documentée de retirer un en-tête. Cet article est une référence de travail pour les champs qui comptent.
Comment vérifier
curl -sI https://app.example.com/ | grep -iE 'strict-transport|x-frame|x-content|referrer-policy|content-security'
curl -sI -X OPTIONS https://api.example.com/ \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: GET" \
| grep -iE 'access-control'
# Une requête vers une route avec secure-headers ne DOIT PAS inclure Server: traefik
curl -sI https://app.example.com/ | grep -i ^server
Ce qui se passe
Le middleware headers opère dans les deux directions : en-têtes de requête (transférés au backend) et en-têtes de réponse (renvoyés au client). Les champs se divisent en trois groupes.
En-têtes de sécurité — stsSeconds, stsIncludeSubdomains, stsPreload, contentTypeNosniff, frameDeny, customFrameOptionsValue, referrerPolicy, permissionsPolicy. Ils ajoutent des en-têtes de réponse que les navigateurs honorent pour la politique de sécurité. HSTS est celui à plus fort impact : une fois réglé avec une soumission preload, vous vous engagez sur HTTPS uniquement pour ce nom d’hôte pour la durée de l’en-tête.
CORS — accessControlAllowOriginList, accessControlAllowMethods, accessControlAllowHeaders, accessControlExposeHeaders, accessControlAllowCredentials, accessControlMaxAge, addVaryHeader. Traefik gère le préflight OPTIONS automatiquement quand les réglages CORS sont présents. L’interaction qui mord : accessControlAllowCredentials: true est incompatible avec accessControlAllowOriginList: ["*"] selon la spec navigateur — il faut énumérer les origines.
En-têtes personnalisés — customRequestHeaders (ajoutés à la requête avant qu’elle n’atteigne le backend), customResponseHeaders (ajoutés à la réponse avant qu’elle ne quitte Traefik). Une valeur de chaîne vide retire l’en-tête. C’est la façon de retirer Server: traefik et toute autre empreinte.
La référence
En-têtes de sécurité
http:
middlewares:
secure-headers:
headers:
# HSTS — un an, inclure sous-domaines, prêt pour preload
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
# Reniflage MIME
contentTypeNosniff: true
# Clickjacking
frameDeny: false # utilisez customFrameOptionsValue pour SAMEORIGIN
customFrameOptionsValue: "SAMEORIGIN"
# Referer
referrerPolicy: "strict-origin-when-cross-origin"
# Remplacements / ajouts modernes
contentSecurityPolicy: "default-src 'self'; img-src 'self' data: https:; script-src 'self'; style-src 'self' 'unsafe-inline'"
permissionsPolicy: "geolocation=(), camera=(), microphone=()"
# Retrait d''empreinte
customResponseHeaders:
Server: ""
X-Powered-By: ""
CORS
http:
middlewares:
cors-api:
headers:
accessControlAllowOriginList:
- "https://example.com"
- "https://app.example.com"
accessControlAllowMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
accessControlAllowHeaders:
- Authorization
- Content-Type
- X-Request-Id
accessControlExposeHeaders:
- X-Request-Id
- X-Rate-Limit-Remaining
accessControlAllowCredentials: true
accessControlMaxAge: 600
addVaryHeader: true
accessControlAllowOriginList accepte des origines exactes ; la correspondance par regex est disponible via accessControlAllowOriginListRegex pour des schémas comme https://[a-z0-9-]+\.example\.com.
Réécriture de requête
http:
middlewares:
injecter-tenant:
headers:
customRequestHeaders:
X-Tenant: "acme"
X-Internal: "true"
# Retirer un en-tête avant transfert
Authorization: ""
Réécriture de réponse
http:
middlewares:
ajouter-cache-control:
headers:
customResponseHeaders:
Cache-Control: "public, max-age=3600"
X-Served-By: "edge-traefik"
Préréglage de production combiné
http:
middlewares:
web-standard:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
contentTypeNosniff: true
customFrameOptionsValue: "SAMEORIGIN"
referrerPolicy: "strict-origin-when-cross-origin"
contentSecurityPolicy: "default-src 'self'; img-src 'self' data: https:; script-src 'self'"
customResponseHeaders:
Server: ""
api-standard:
headers:
stsSeconds: 31536000
accessControlAllowOriginList:
- "https://app.example.com"
accessControlAllowMethods: [GET, POST, PUT, DELETE, OPTIONS]
accessControlAllowHeaders: [Authorization, Content-Type]
accessControlAllowCredentials: true
accessControlMaxAge: 600
addVaryHeader: true
customResponseHeaders:
Server: ""
Pièges fréquents
- HSTS
stsPreload: truesans avoir réellement soumis à la liste preload — l’en-tête ment ; soumettez sur hstspreload.org ou retirez le drapeau. - Régler à la fois
frameDeny: trueetcustomFrameOptionsValue—frameDenygagne et émetDENY; choisissez-en un. - CORS avec
accessControlAllowOriginList: ["*"]plusaccessControlAllowCredentials: true— les navigateurs refusent la réponse selon la spec ; énumérez les origines. - Oublier
addVaryHeader: truequand CORS varie selon l’origine — les caches fusionnent les réponses pour différentes origines. - Retirer
AuthorizationdanscustomRequestHeaderspour enlever un jeton avant le backend — fonctionne pour la requête visible mais l’original est parti pour l’authentification aval ; ne le faites que volontairement.
Le middleware headers est l’un des morceaux de configuration au niveau plateforme les plus utilisés — une définition unique secure-headers référencée par chaque router public est la façon dont les clients tournent une posture de sécurité uniforme à travers de nombreuses applis. L’audit de « qu’est-ce que chaque route émet » fait partie de la cadence des opérations infogérées. Pour la surface plus large des middlewares, voir traefik-routers-services-middlewares.