Egy CDN vagy reverse proxy első közelítésben URL alapján tudja elővenni a korábbi választ. Ez akkor kevés, ha a /termekek tartalma a kérés valamelyik fejléce szerint változik: más nyelv, más képformátum vagy tömörítés jár a kliensnek. A Vary válaszfejléc mondja meg a cache-nek, hogy mely kérésfejlécek voltak részei ennek a döntésnek. Nem a cache érvényességét szabályozza, hanem a cache-kulcsot pontosítja.

Nem „egy URL, egy cache-bejegyzés”
Ha a szerver az Accept-Language alapján magyar vagy angol feliratokat küld, a két válasz ugyanahhoz az URL-hez tartozik, de nem cserélhető fel. A Vary: Accept-Language ezért azt jelenti: a cache a cél-URL mellé ezt a kérésfejlécet is vegye figyelembe. Az RFC 9111 4.1. szakasza szerint a tárolt válasz csak akkor használható újra ellenőrzés nélkül, ha a Vary-ban megnevezett fejlécek egyeznek az eredeti kérésével.
Ez nemcsak nyelvnél fontos. A szerver vagy az előtte lévő CDN gyakran az Accept-Encoding alapján választ br, gzip vagy tömörítetlen reprezentációt; ilyenkor ehhez a döntéshez is tartoznia kell a Vary: Accept-Encoding jelzésnek. Ugyanez a szabály a Client Hints által befolyásolt erőforrásokra is: ha a válasz valóban a hinttől függ, annak a fejlécnek a cache-kulcsban is helye van.
Egy szándékosan kicsi Node-példa
A példa két nyelvi JSON-változatot küld, ezért pontosan egy változó fejléce van. A Node setHeader() API-ja a válasz elküldése előtt állítja be a fejlécet.
import { createServer } from 'node:http';
const server = createServer((req, res) => {
const acceptLanguage = req.headers['accept-language']?.toLowerCase() ?? '';
const isHungarian = acceptLanguage.startsWith('hu');
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'public, max-age=300');
res.setHeader('Vary', 'Accept-Language');
res.end(JSON.stringify({ label: isHungarian ? 'Kosár' : 'Cart' }));
});
server.listen(4173, '127.0.0.1');
A lényeg nem a nyelvfelismerő három sora. Ha ezt a Vary-t elhagynánk, egy megosztott cache kiszolgálhatná az elsőként eltárolt „Kosár” választ egy angol kérésre is. A fordított hiba is gyakori: bekerül a Vary-ba egy olyan fejléc, amely valójában nem befolyásol semmit. Ezzel felesleges, egymástól elkülönült cache-bejegyzéseket hozunk létre, és romlik a találati arány.
A Cookie nem cache-stratégia
Elsőre biztonságosnak tűnhet a Vary: Cookie: így két munkamenet biztosan nem ugyanazt a bejegyzést kapja. Gyakorlatban viszont a teljes Cookie fejléc nagyon sokféle, tehát szinte feladja a megosztott cache hasznát. Ennél fontosabb, hogy egy cookie jelenléte önmagában nem tesz egy választ priváttá. Személyre szabott, bejelentkezés utáni válasznál a helyes alapjelzés a Cache-Control: private; érzékeny adatnál mérlegeld a no-store-t is. A Vary azt írja le, hogyan különülnek el a variánsok, nem azt, hogy egy megosztott cache tárolhatja-e a választ.
Hasonlóan rossz általános megoldás a Vary: User-Agent. A User-Agent értékek sokfélesége gyorsan szétaprózza a cache-t; az MDN cache-útmutatója inkább feature detectiont vagy más, stabilabb felosztást javasol. Ha egy nyelv vagy képminőség fontos része az erőforrás azonosításának, gyakran áttekinthetőbb külön URL-t adni neki, például /hu/termekek vagy egy képméreteket jelölő útvonal formájában.
Mit ellenőrizz deploy előtt?
- Írd össze, mely kérésfejlécek befolyásolják ténylegesen a body-t vagy a reprezentációt.
- Csak ezeket tedd a
Vary-ba, és minden variánson következetesen ugyanazt küldd; ez a 304-es válaszokra is vonatkozik. - Személyre szabott válasznál ne a
Vary: Cookie-ra építs: legyen explicitprivatevagy indokolt esetbenno-store. - Nézd meg a CDN konfigurációját is: a proxy által hozzáadott tömörítés vagy képkonverzió saját Vary-igényt teremthet.
A Vary: * nem rövidítés erre a problémára: azt jelzi, hogy a döntést nem felsorolható kérésfejlécek befolyásolták, ezért a cache nem használhatja újra a tárolt választ friss ellenőrzés nélkül. A jó Vary-lista kicsi és megmagyarázható: pontosan az a néhány fejléc szerepel rajta, amelytől a bájtok tényleg megváltoznak.