Le WordPress d'aujourd'hui, décodé pour les développeurs

Multilingue

Vary Accept-Language absent, un CDN qui sert la mauvaise langue en cache

Un en-tête de cache oublié suffit à faire servir la même page, dans une seule langue figée, à tous les visiteurs passant par le même point de sortie du CDN.

Par Clément Hadrot • 11 septembre 2023 • 5 min de lecture • Aucun commentaire
Vary Accept-Language absent, un CDN qui sert la mauvaise langue en cache

Vary: Accept-Language : trois mots dans un en-tête HTTP, absents de la configuration d’un CDN, ont suffi à faire vivre plusieurs semaines un incident déroutant sur un site institutionnel qui proposait ses pages dans la langue du navigateur du visiteur via négociation de contenu côté serveur, avant qu’un CDN ne soit ajouté devant l’infrastructure pour absorber un pic de charge.

Ce diagnostic revient sur la cause précise de l’incident, sur la raison pour laquelle il n’apparaissait que pour une partie des visiteurs, et sur la correction retenue, plus large qu’un simple ajout d’en-tête.

Symptôme : une langue figée selon le point d’entrée du CDN

Après la mise en place d’un CDN pour absorber un pic de trafic attendu lors d’une campagne de communication, plusieurs visiteurs ont signalé recevoir systématiquement la version anglaise du site alors que leur navigateur était configuré en français, tandis que d’autres visiteurs, au contraire, recevaient la version française malgré un navigateur configuré en allemand. Le comportement semblait aléatoire, mais restait constant pour un même visiteur à chaque rechargement.

Diagnostic : une seule version mise en cache par URL

Le site ne différenciait pas ses URLs par langue : une seule adresse, https://exemple.org/a-propos/, servait un contenu différent selon l’en-tête Accept-Language envoyé par le navigateur, une pratique de négociation de contenu gérée côté PHP par une fonction de détection de langue accrochée au hook determine_locale. Cette approche fonctionnait correctement sans cache intermédiaire : chaque requête atteignait le serveur d’origine, qui adaptait sa réponse à chaque fois.

L'essentiel à retenir : Sans en-tête Vary adapté, un CDN met en cache une seule version linguistique ; La négociation de contenu par navigateur ne fonctionne pas avec un cache mal configuré ; La solution passe le plus souvent par l'URL plutôt que par la négociation de contenu

L’ajout du CDN a changé la donne : la première requête reçue pour /a-propos/ sur un point de présence donné du CDN a été mise en cache telle quelle, dans la langue déterminée pour ce premier visiteur, puis servie identique à tous les visiteurs suivants passant par ce même point de présence, quel que soit leur propre en-tête Accept-Language. Le CDN, sans configuration explicite lui indiquant de varier son cache selon cet en-tête, traitait toutes les requêtes vers la même URL comme identiques.

Pourquoi l’en-tête Vary est nécessaire mais pas toujours suffisant

L’en-tête de réponse HTTP Vary: Accept-Language, envoyé par le serveur d’origine, indique explicitement aux caches intermédiaires qu’ils doivent conserver des versions distinctes d’une même URL selon la valeur de cet en-tête de requête. Ajouté au niveau du serveur d’origine, il corrige le principe du problème :

header( 'Vary: Accept-Language' );

Mais cette correction, à elle seule, expose un nouveau problème de fond : l’en-tête Accept-Language envoyé par les navigateurs contient rarement une seule valeur simple. Il liste souvent plusieurs langues avec des poids de préférence, dans des combinaisons quasiment infinies d’un visiteur à l’autre, ce qui réduit considérablement le taux de succès du cache : chaque combinaison distincte d’en-tête devient une entrée de cache séparée, ce qui revient presque à ne plus mettre en cache la page du tout pour un site à fort trafic international.

La solution retenue : des URLs distinctes par langue

Plutôt que de s’appuyer sur la négociation de contenu combinée à Vary, la correction durable a consisté à migrer le site vers une structure d’URLs distinctes par langue, en sous-dossiers, à l’aide de Polylang : /fr/a-propos/ et /en/about/ comme deux ressources entièrement séparées aux yeux du CDN, chacune mise en cache indépendamment sans avoir besoin de varier quoi que ce soit selon l’en-tête de requête.

ApprocheCompatibilité CDNEffet sur le taux de cache
Négociation de contenu + VaryFonctionne en théorieTaux de cache très dégradé, entrées multipliées
URLs distinctes par langueNativement compatibleCache efficace, une entrée stable par langue

Ce qu’il reste à prévoir : une redirection initiale intelligente

Passer à des URLs distinctes par langue déplace la détection de la langue du navigateur au tout premier accès seulement, sur la page d’accueil non préfixée, via une redirection HTTP vers le sous-dossier de langue approprié. Cette redirection unique, elle, doit rester non mise en cache de façon prolongée par le CDN, sous peine de reproduire le même problème à un autre endroit du parcours.

  • Configurer une durée de cache très courte, voire nulle, sur la redirection initiale de langue.
  • Vérifier que chaque sous-dossier de langue répond avec ses propres en-têtes de cache, indépendants des autres.
  • Documenter dans la configuration du CDN pourquoi aucun en-tête Vary n’est nécessaire une fois les URLs séparées.

En résumé

Un CDN sans en-tête Vary adapté traite une même URL négociée par langue comme une ressource unique, et sert la première version mise en cache à tous les visiteurs suivants d’un même point de présence. Ajouter l’en-tête corrige le principe mais dégrade fortement l’efficacité du cache ; séparer les langues par URL distincte reste la solution la plus robuste dès qu’un CDN entre dans l’architecture d’un site multilingue.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi