TECH 으로 돌아가기
TECH HACKER NEWS 오늘 9분 읽기 28 READS

HTTP에서 제일 못생긴 헤더 'Vary', Cloudflare가 드디어 제대로 지원하기 시작했어요

HTTP에서 제일 못생긴 헤더 'Vary', Cloudflare가 드디어 제대로 지원하기 시작했어요
SOURCE IMAGE · HACKER NEWS
HTTP에서 제일 못생긴 헤더 'Vary', Cloudflare가 드디어 제대로 지원하기 시작했어요

무슨 일이 있었나요

Cloudflare가 자사 블로그에 “HTTP에서 가장 못생긴 부분인 Vary를 지원하기 시작했다”는 글을 올렸어요. 제목부터 자조적인데요, 그만큼 Vary 헤더는 CDN을 만드는 사람들이 오랫동안 피하고 싶어했던 기능이거든요. 지금까지 Cloudflare는 Vary 헤더를 사실상 무시해 왔어요. 예외라면 압축 방식을 나타내는 Accept-Encoding 정도, 그리고 이미지에 한해서 Accept 헤더에 따라 WebP나 AVIF 같은 다른 포맷을 따로 캐시해주는 “Vary for Images” 기능 정도였죠. 그런데 이번에 일반 응답에 대해서도 Vary를 캐시 키에 반영할 수 있게 됐다는 거예요.

Vary 헤더가 뭐냐면

이게 뭐냐면, 서버가 캐시에게 “이 응답은 요청 헤더 X의 값에 따라 내용이 달라져요”라고 알려주는 표시예요. 예를 들어 같은 URL /api/products라도 Accept-Language: ko로 요청하면 한국어 응답이, en으로 요청하면 영어 응답이 나온다고 해봐요. 캐시가 URL만 보고 저장하면 한국 사용자가 영어 응답을 받는 사고가 나겠죠. 그래서 서버가 Vary: Accept-Language를 붙여서 “URL뿐 아니라 Accept-Language 값까지 조합해서 캐시 키를 만들어라”라고 알려주는 거예요.

비유하자면 택배 보관함에 물건을 넣을 때 수취인 이름만으로 칸을 나누는 게 아니라, 이름과 아파트 동까지 조합해서 칸을 나누라고 지시하는 것과 비슷해요.

그런데 왜 “못생겼다”고 하나요

첫 번째 문제는 이 헤더가 너무 자유롭다는 거예요. 규격상 서버는 아무 헤더 이름이나 Vary에 넣을 수 있거든요. Vary: User-Agent를 붙이면 어떻게 될까요? 브라우저 종류, 버전, 운영체제 조합마다 다른 User-Agent 문자열이 수천, 수만 개예요. 캐시가 이걸 다 따로 저장하면 사실상 캐시가 안 되는 거나 마찬가지가 되죠. 이걸 흔히 캐시 파편화(cache fragmentation)라고 불러요. Vary: Cookie는 더 심해요. 사용자마다 쿠키가 다르니 모든 응답이 유일해져요. 그리고 Vary: *라는 것도 있어요. “무엇에든 달라질 수 있다”는 뜻이라서 사실상 캐시하지 말라는 것과 같아요.

두 번째 문제는 헤더 값의 정규화예요. Accept-Encoding: gzip, brAccept-Encoding: br,gzip은 의미가 같지만 문자열은 달라요. 순진하게 문자열 그대로 키로 쓰면 같은 응답이 여러 번 저장되고 히트율이 떨어지죠. 그렇다고 캐시가 임의로 “같다”고 판단해버리면 서버 의도를 어길 수도 있고요.

세 번째 문제는 캐시 조회 순서예요. 캐시는 요청이 들어왔을 때 URL만 알아요. 어떤 헤더로 키를 만들어야 하는지는 응답을 받아봐야 Vary를 보고 알 수 있죠. 그러니까 URL 하나에 대해 “이 URL은 어떤 헤더들에 따라 변하는지” 정보를 먼저 저장해 두고, 요청이 오면 그 정보를 참고해서 두 번째 키를 만든 뒤 실제 객체를 찾아야 해요. 조회가 두 단계가 되는 셈이고, 이걸 전 세계 수백 개 데이터센터에 퍼져 있는 분산 캐시에서 일관되게 처리하려면 설계가 꽤 복잡해져요.

HTTP 캐싱 규격인 RFC 9111에서는 Vary에 나열된 헤더들의 값이 모두 일치하는 저장 응답만 재사용할 수 있다고 정의하고 있고, 정규화는 캐시가 의미적으로 같다고 확신할 수 있을 때만 허용한다고 되어 있어요. 규격 문장은 짧지만 실제 구현은 결코 짧지 않은 거죠.

다른 CDN과 캐시들은 어떻게 해왔나

Varnish나 nginx 같은 오픈소스 캐시는 오래전부터 Vary를 지원했어요. Varnish는 응답을 저장할 때 Vary에 나열된 요청 헤더 값들을 함께 기록하고, 조회 시 그 값들이 일치하는 객체만 돌려줘요. Fastly는 Varnish 기반이라 비슷하고, VCL로 헤더를 정규화하는 패턴이 널리 쓰이죠. Akamai는 기본적으로 Vary가 붙은 응답을 캐시하지 않고 설정으로 따로 다뤄야 했고요. Cloudflare는 이 부분에서 “무시한다”는 단순한 정책을 택해왔는데, 대규모 트래픽에서 캐시 파편화로 인한 오리진 부하 폭증을 막는 게 더 중요했기 때문일 거예요. 이번 발표는 그 안전한 단순함을 포기하고 복잡함을 감수하기로 했다는 뜻이기도 해요.

브라우저 캐시도 Vary를 지원해요. Chrome이나 Firefox는 Vary에 적힌 헤더 값이 다르면 별도 항목으로 취급하죠. 그래서 CDN보다 오히려 브라우저에서 먼저 Vary 관련 버그를 겪는 분들도 많아요.

한국 개발자에게 주는 시사점

첫째, 그동안 “Cloudflare에서는 Vary가 안 먹으니까”라는 이유로 언어별, 디바이스별 응답을 URL이나 쿼리스트링으로 분리해 왔다면 이제 선택지가 하나 더 생겼어요. 다만 Vary는 여전히 위험한 도구예요. Vary: User-AgentVary: Cookie를 그대로 쓰면 히트율이 바닥으로 떨어지니, 정말 필요한 헤더만 최소한으로 넣어야 해요.

둘째, 이번 기회에 여러분 서비스의 응답 헤더를 한번 점검해 보세요. Spring, Express, Next.js 같은 프레임워크가 자동으로 Vary: Origin이나 Vary: Accept, Vary: RSC 같은 헤더를 붙이는 경우가 많거든요. 지금까지는 CDN이 무시해서 문제가 없었지만, CDN이 Vary를 존중하기 시작하면 갑자기 캐시 히트율이 떨어질 수 있어요. CORS 미들웨어가 붙이는 Vary: Origin이 대표적이에요. 요청 Origin이 다를 때마다 별도 캐시 항목이 생기니까요.

셋째, 콘텐츠 협상(content negotiation)을 제대로 설계하는 계기가 될 수 있어요. WebP와 AVIF 이미지 포맷 분기, 다국어 API, 모바일과 데스크톱 HTML 분기 같은 곳에서 Vary를 정확히 쓰면 URL을 오염시키지 않으면서도 올바르게 캐시할 수 있거든요.

마무리

한 줄로 정리하면, Cloudflare가 오랫동안 무시해왔던 Vary 헤더를 캐시 키에 반영하기 시작했고, 이건 편리함과 동시에 캐시 파편화라는 책임도 함께 넘겨준다는 이야기예요.

여러분은 CDN에서 Vary를 써본 경험이 있으신가요? 언어나 디바이스별 응답 분기를 URL로 처리하시나요, 헤더로 처리하시나요? 그리고 프레임워크가 자동으로 붙이는 Vary 헤더 때문에 곤란했던 적은 없으셨는지 궁금해요.


🔗 출처: Hacker News

SOURCE · HACKER NEWS
원문 전체 보기 → https://blog.cloudflare.com/vary-support/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...