2026년 7월 2일 Cloudflare가 캐시 규칙에서 Vary 헤더를 직접 인식하도록 정책을 확장했습니다. 원본이 Accept-Language·Accept-Encoding·User-Agent 같은 헤더 값에 따라 같은 URL에 다른 응답을 반환하는 경우, 캐시가 이 차이를 인식하도록 하는 표준 방식이 Vary입니다.
새 옵션 세 가지.
- normalize: Vary가 지정한 헤더를 정규화(예:
en-US·en-GB를 모두en으로) 후 캐시 키에 반영. - passthrough: Vary를 그대로 존중해서 다중 버전 캐싱.
- bypass: Vary 무시하고 한 버전만 캐싱(위험).
Vary는 오래된 헤더지만 실무에서 오해가 많은 주제입니다. 자체 Nginx·Varnish에서 Vary와 캐시 키를 어떻게 다루는지 함께 정리합니다.
Vary 헤더 기본 개념
같은 URL이 요청 헤더에 따라 다른 응답을 반환하는 상황.
GET /home HTTP/1.1
Host: example.com
Accept-Language: ko
→ 한국어 페이지 반환
GET /home HTTP/1.1
Host: example.com
Accept-Language: en
→ 영어 페이지 반환
원본이 응답에 Vary: Accept-Language를 붙이면, 캐시는 “이 URL 응답은 Accept-Language 값별로 따로 저장해야 한다”는 것을 알게 됩니다.
캐시 키 계산 예시.
캐시 키 = URL + Host + Accept-Language 값
같은 /home이라도 Accept-Language: ko와 Accept-Language: en은 다른 캐시 엔트리로 저장·서빙됩니다.
오해 1 — Vary: * 는 캐시 불가
가끔 원본이 Vary: *를 반환하는데, 이건 “모든 헤더가 응답에 영향을 미친다”는 의미. 캐시가 사실상 이 응답을 캐싱하면 안 됩니다. 사용자가 의도한 경우가 거의 없고 대개 프레임워크 기본값이 잘못된 것.
체크 방법.
curl -sI https://example.com/api/data | grep -i vary
Vary: *가 나오면 원본 설정을 뜯어봐야 합니다.
오해 2 — Vary가 있으면 캐시 히트율이 뚝 떨어진다
Vary: User-Agent는 위험합니다. 브라우저 User-Agent 문자열이 사실상 무한대로 다양해서 캐시 엔트리가 폭발합니다. 하나의 URL에 수천 개 캐시가 만들어지고 대부분은 히트되지 않음.
이 문제 때문에 Cloudflare 새 옵션 중 normalize가 유용합니다. Accept-Language 같은 헤더를 정규화(그룹 값으로 축소)한 뒤 캐시 키에 반영.
- 원본:
Accept-Language: ko-KR,ko;q=0.9,en;q=0.8 - 정규화 후 캐시 키:
ko
사용자마다 다른 문자열이 하나의 값으로 축소되어 캐시 히트율이 유지됩니다.
Nginx에서 Vary·캐시 키 다루기
Nginx는 Vary를 자동으로 존중하지 않습니다. 개발자가 명시적으로 proxy_cache_key에 헤더를 포함해야 합니다.
언어별 캐시
map $http_accept_language $lang {
~*^ko ko;
~*^en en;
~*^ja ja;
default en;
}
proxy_cache_key "$scheme$request_method$host$request_uri$lang";
Accept-Language 원 문자열이 아니라 정규화된 $lang을 캐시 키에 씀. 이게 Cloudflare normalize 옵션이 하는 일과 같은 개념.
압축(encoding)별 캐시
브라우저별 지원 압축이 다름.
map $http_accept_encoding $enc {
~gzip gzip;
~br br;
default none;
}
proxy_cache_key "$scheme$request_method$host$request_uri$enc";
또는 gzip_vary on + brotli on 조합으로 Nginx가 자동으로 압축 형식별 캐시.
쿠키·인증 사용자 캐시 우회
로그인 사용자에겐 캐시를 서빙하지 않도록.
map $http_cookie $skip_cache {
default 0;
"~*session_id" 1;
"~*wp-.*_logged_in" 1;
}
location / {
proxy_cache mycache;
proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
}
세션 쿠키가 있으면 캐시 우회. 비로그인 트래픽만 캐시.
캐시 상태 헤더 노출
디버깅용.
add_header X-Cache-Status $upstream_cache_status;
응답 확인.
curl -sI https://example.com/ | grep X-Cache-Status
# HIT / MISS / BYPASS / EXPIRED / UPDATING / STALE
Varnish에서 VCL로 세밀 제어
Varnish는 VCL(Varnish Configuration Language)로 매우 세밀한 캐시 키·Vary 제어가 가능. Nginx보다 표현력이 강함.
hash 함수로 캐시 키 커스텀
sub vcl_hash {
hash_data(req.url);
hash_data(req.http.host);
# Accept-Language 정규화
if (req.http.Accept-Language ~ "^ko") {
hash_data("lang:ko");
} elsif (req.http.Accept-Language ~ "^ja") {
hash_data("lang:ja");
} else {
hash_data("lang:en");
}
# 로그인 사용자 캐시 우회
if (req.http.Cookie ~ "session_id") {
return (pass);
}
}
grace 모드 (stale-while-revalidate)
sub vcl_backend_response {
set beresp.grace = 30m; # 만료 후 30분간 stale 응답 허용
}
sub vcl_hit {
if (obj.ttl >= 0s) {
return (deliver);
} elsif (obj.grace >= 0s) {
# 백그라운드 fetch를 트리거하고 stale 응답 반환
return (deliver);
}
}
Cloudflare가 Pingora로 옮기면서 개선한 stale-while-revalidate와 같은 개념.
디바이스별 응답 — User-Agent Client Hints로 이전
Vary: User-Agent는 캐시 파괴자. 대안은 User-Agent Client Hints.
브라우저가 요청에 Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform 같은 정규화된 힌트를 보냅니다. 서버는 이 정규화된 값만 보고 응답을 분기.
map $http_sec_ch_ua_mobile $device {
"?1" mobile;
default desktop;
}
proxy_cache_key "$scheme$host$request_uri$device";
캐시 키가 mobile·desktop 두 값만 가지므로 히트율 유지.
원본은 응답에 Vary: Sec-CH-UA-Mobile도 붙여줘야 다른 캐시(브라우저·CDN)가 정확히 처리.
캐시 오염(cache poisoning) 방지
Vary·캐시 키를 잘못 설계하면 캐시 오염 취약점.
- 공격자가 특수한 헤더로 요청 → 그 응답이 캐시됨 → 후속 사용자에게 그 오염된 응답이 나감.
방어.
- 입력 헤더 정규화. Accept-Language·User-Agent 같은 것은 반드시 화이트리스트 값만.
- 캐시 키에 unhashable 헤더 넣지 말기. 공격자가 조작 가능한 헤더를 그대로 키에 넣으면 위험.
- Cache-Control 준수 강제. 원본이
private·no-store붙였는데 CDN이 캐시하면 사고.
실무 체크리스트
자체 웹서버 캐시 정책을 처음부터 점검한다면.
proxy_cache_key가 정의돼 있는가.Vary헤더 응답을 정규화하고 있는가.Vary: User-Agent를 쓰고 있진 않은가.- 로그인 사용자 캐시 우회가 있는가.
X-Cache-Status헤더로 히트율 관측 가능한가.proxy_cache_background_update on으로 stale-while-revalidate 켰는가.- Cache-Control 정책이 클라이언트·CDN·오리진에 일관되게 반영되는가.
마무리
Cloudflare의 이번 Vary 정책 확장은 오래된 문제를 새 옵션으로 다시 정리한 것. 우리 자체 서버도 같은 개념(정규화·다중 버전·우회)을 Nginx·Varnish에 적용해서 캐시 히트율과 정확성을 함께 잡아야 합니다.
TCP-80.NET의 VPS·전용서버 고객 중 Nginx·Varnish 캐시 튜닝을 함께 검토하고 싶으신 분은 @tcp80net 텔레그램으로 문의 주시면 됩니다.
출처 (Source)
이 글은 아래 Cloudflare 공식 changelog를 참고해 tcp-80.net 청중 관점에서 재구성·번역·확장했습니다. 자체 서버 Vary·캐시 키 실무(Nginx map·proxy_cache_key·Varnish VCL·User-Agent Client Hints·캐시 오염 방지·체크리스트)는 원문에 없는 자체 추가 내용입니다.
- 원문 제목: Vary for cache rules
- 원문 발행일: 2026-07-02
- 원문 URL: https://developers.cloudflare.com/changelog/post/2026-07-02-vary-for-cache-rules/
- 관련 표준: RFC 9110 §12.5.5 (Vary), RFC 8942 (Client Hints)
- 원문 저작자: Cloudflare, Inc.
- 본문 재구성·번역: TCP-80.NET (2026-07-02)
Cloudflare 제품 옵션명·UI는 시점에 따라 다를 수 있으니 최신 정보는 원문 및 Cloudflare 대시보드에서 확인하시기 바랍니다.