Infrastructure

HAProxy — Masquer les headers serveur et servir des pages d'erreur personnalisées multi-backends

Masquer sélectivement les headers Server/X-Powered-By par domaine et remplacer les pages d'erreur HAProxy pour tous les backends, pas seulement en cas de panne détectée par HAProxy lui-même.

Table des matières

Un reverse proxy HAProxy qui expose plusieurs services internes (panel, stockage, jeux, monitoring…) pose deux problèmes récurrents : certains services révèlent trop d’informations dans leurs headers HTTP, et les pages d’erreur par défaut ne sont jamais cohérentes d’un service à l’autre. Ce guide couvre les deux.

Masquer les headers serveur par domaine

Objectif : cacher Server/X-Powered-By pour tous les domaines internes, sauf le site vitrine où on assume volontairement la stack technique.

Premier réflexe naturel — et premier piège :

acl is_main_domain hdr(host) -i 50bvd.com
http-response del-header Server if !is_main_domain

En contexte http-response, hdr(host) n’est plus valable : ce fetch n’existe que côté requête. HAProxy refuse la règle avec “will never match because it only involves keywords that are incompatible with ‘frontend http-response header rule’“.

La solution consiste à capturer le host dès la requête dans une variable de transaction, puis à la relire côté réponse :

http-request set-var(txn.host) req.hdr(host)
acl is_main_domain var(txn.host) -m str -i 50bvd.com

http-response del-header Server     if !is_main_domain
http-response del-header X-Powered-By if !is_main_domain
-m str obligatoire

Une ACL basée sur var() n’a pas de type implicite — sans -m str, HAProxy refuse de démarrer avec ‘matching method must be specified first’.

Pages d’erreur personnalisées sur tous les backends

Les directives errorfile classiques (dans defaults) ne couvrent que les erreurs générées par HAProxy lui-même (backend injoignable → 502/503/504, requête malformée → 400). Elles n’interceptent jamais un 404 ou un 500 renvoyé par un backend qui répond normalement.

Pour remplacer systématiquement n’importe quel code d’erreur — qu’il vienne d’un backend sain ou d’une panne détectée par HAProxy — sur frontend https_front :

http-response return status 404 content-type text/html \
    file /etc/haproxy/errors/404.http \
    hdr X-Frame-Options "SAMEORIGIN" \
    hdr X-Content-Type-Options "nosniff" \
    hdr Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" \
    hdr Referrer-Policy "strict-origin-when-cross-origin" \
    if { status 404 }

À répéter pour chaque code voulu (400, 403, 404, 408, 500, 502, 503, 504).

return remplace toute la réponse

http-response return écrase entièrement la réponse d’origine, y compris les en-têtes de sécurité ajoutés par des règles précédentes — il faut les réinjecter directement sur la ligne return via hdr.

Limite connue : les SPA qui répondent toujours 200

Un panel type Pterodactyl gère ses routes côté client (React/Vue) : une URL inexistante dans l’app renvoie quand même un HTTP 200 côté serveur, avec un “not found” purement visuel. Aucune règle HAProxy ne peut intercepter ce cas — seules les vraies pannes (backend injoignable, timeout) restent couvertes.

Vérification

haproxy -c -f /etc/haproxy/haproxy.cfg
rcctl reload haproxy   # ou rcctl restart haproxy si reload insuffisant

# Header masqué sur un service interne
curl -sI https://portainer.exemple.com/ | grep -i server

# Header conservé sur le site vitrine
curl -sI https://50bvd.com/ | grep -i server

# Page d'erreur personnalisée sur une route inexistante
curl -s https://50bvd.com/route-qui-nexiste-pas/ | head -c 200
ksh, pas bash
Sur OpenBSD, HAProxy tourne via rcctl et le shell par défaut est ksh — les astuces bash (sed -i sans argument, systemctl…) ne s’appliquent pas telles quelles. sed nécessite un argument vide explicite : sed -i ‘’ ‘s ’.

Résultat

  • Headers Server/X-Powered-By masqués sur tous les domaines internes, conservés uniquement sur le domaine choisi.
  • Une seule page d’erreur cohérente et personnalisée, appliquée uniformément sur tous les backends et tous les codes d’erreur.

Comments