콤퓨타수선집은 문체부지정 공식
저작권대리중개업체 육하원칙과 함께합니다.

콤퓨타지식 HTTP Vary란? 같은 URL의 서로 다른 응답을 구분하는 캐시의 작동 원리

콘텐츠 협상부터 캐시 키, 요청 헤더 정규화, Cache-Control과의 차이까지 HTTP 캐싱의 핵심 구조를 이해한다.

페이지 정보

본문

작성일

같은 주소를 요청했는데 어떤 사람에게는 한국어 페이지가, 다른 사람에게는 영어 페이지가 표시될 수 있다. 서버가 요청 조건에 맞춰 응답을 선택하기 때문이다. 문제는 중간에 캐시가 있을 때 발생한다. 이미 저장한 응답을 다른 요청에도 그대로 전달해도 될까? HTTP의 Vary 헤더는 이 질문에 답하기 위해 설계된 장치다.

CORE PRINCIPLE / 핵심 개념

Vary는 응답을 만들어 주는 헤더가 아니다. 원본 서버가 응답을 선택할 때 참고한 요청 헤더를 캐시에 알려, 저장된 응답을 이후 어떤 요청에 재사용할 수 있는지 판단하게 한다.

캐시가 저장해도 되는지와 얼마나 오래 신선한지는 Cache-Control 등 별도의 규칙이 결정한다.

01 / CONTENT NEGOTIATION

URL이 같아도 콘텐츠가 달라지는 이유

URL은 어떤 자원을 요청하는지 나타내지만, 서버가 그 자원을 항상 동일한 형태로 표현한다는 뜻은 아니다. HTTP에서는 클라이언트가 선호하는 미디어 형식, 언어, 압축 형식 등을 요청 헤더로 전달하고, 서버가 이를 참고해 응답 표현(Representation)을 선택할 수 있다. 이 과정을 콘텐츠 협상(Content Negotiation)이라고 한다.

예를 들어 /guide는 동일한 문서를 가리키지만, Accept-Language 값에 따라 한국어와 영어 HTML을 반환할 수 있다. 아래는 원리를 설명하기 위한 예시이며, 실제 언어 선택에는 서버 설정과 기본 언어 정책도 영향을 준다.

동일한 자원GET /guide
↓
요청 A · Accept-Language: ko한국어 HTML안녕하세요
요청 B · Accept-Language: en영어 HTMLHello

그림 1. 서버가 요청 헤더에 따라 서로 다른 표현을 선택하는 개념도.

캐시가 두 요청을 단지 URL이 같다는 이유로 동일하게 취급하면, 한국어 사용자의 요청에 앞서 저장된 영어 HTML을 반환할 수 있다. 반대로 서로 다른 언어 표현을 구분해 저장하면 각 요청에 맞는 응답을 재사용할 수 있다. 근거: RFC 9110 §12, MDN HTTP Caching

02 / RESPONSE HEADER

Vary 헤더의 정의와 문법

Vary는 서버가 응답을 선택할 때 영향을 줄 수 있었던 요청 헤더의 이름을 열거하는 응답 헤더다. RFC 9110 §12.5.5에 정의되어 있으며, 캐시는 이 정보를 이용해 이후 요청이 저장된 응답과 일치하는지 판단한다. 값에는 헤더 이름을 쉼표로 구분해 적거나 별표(*)를 사용할 수 있다.

HTTP RESPONSE / 언어별 응답 예시
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: ko
Vary: Accept-Language
Cache-Control: public, max-age=3600

<!doctype html>
<html lang="ko">...</html>

여기서 Content-Language: ko는 반환된 표현의 언어를 설명하고, Vary: Accept-Language는 캐시가 이후 요청의 언어 선호 헤더를 비교해야 함을 알린다. 두 헤더는 서로 다른 역할을 한다.

Vary 값의미예시
Accept클라이언트가 요청한 미디어 형식에 따라 응답 선택HTML / JSON
Accept-Language언어 선호에 따라 응답 선택한국어 / 영어
Accept-Encoding지원하는 콘텐츠 인코딩에 따라 응답 선택gzip / 비압축
Accept, Accept-Language나열한 두 요청 필드가 모두 선택 기준미디어 형식과 언어
*열거된 요청 헤더 비교만으로는 선택 기준을 특정할 수 없음저장 응답을 후속 요청에 그대로 일치시킬 수 없음
Vary: *는 Cache-Control: no-store와 다르다.표준에서 별표는 저장된 응답이 후속 요청과 일치하지 않는다는 뜻이다. 캐시 구현체가 실무적으로 저장을 생략할 수는 있지만, 헤더 자체가 모든 캐시에 저장 금지를 명령하는 것은 아니다.

서버가 실제로 응답을 구분하는 기준은 빠짐없이 선언해야 한다. 예컨대 HTML과 JSON을 선택하면서 Vary: Accept를 누락하면, 표준을 따르는 캐시도 올바른 변형을 선택하는 데 필요한 정보를 얻지 못한다. 근거: RFC 9110 §12.5.5

03 / CACHE MATCHING

캐시 키와 저장 응답 선택 과정

캐시 키(Cache Key)는 저장된 응답을 검색할 때 사용하는 식별 기준이다. HTTP 캐시는 일반적으로 요청 메서드와 대상 URI를 바탕으로 후보 응답을 찾으며, 저장된 응답에 Vary가 있으면 해당 응답을 만들었던 원래 요청의 헤더 값과 새 요청의 값을 추가로 대조한다. 이를 보조 키(Secondary Key)에 따른 선택으로 이해할 수 있다. 실제 저장소의 키 문자열이나 인덱스 구조는 캐시 제품마다 다르다.

새로운 요청GET /guide · Accept-Language: ko
↓
메서드·대상 URI 등으로 저장 응답 후보 탐색
↓
후보 응답의 Vary 필드에 명시된 요청 헤더 대조원래 요청의 값과 새 요청의 값이 일치하는가?
↓
일치하는 후보신선도·캐시 지시문 등 나머지 재사용 조건 확인
일치하지 않는 후보그 응답은 그대로 재사용할 수 없음 · 다른 후보 또는 서버 확인

그림 2. RFC 9111의 응답 일치 조건을 설명하기 위해 단순화한 흐름. 실제 구현에서는 후보 선택과 검증의 순서가 달라질 수 있다.

예를 들어 Vary: Accept-Language인 한국어 응답을 저장했다면, 이후 같은 URL의 영어 요청에는 해당 한국어 응답을 검증 없이 사용할 수 없다. 반면 Vary가 지정한 필드가 모두 일치하더라도 응답이 만료되었거나 다른 캐시 조건을 충족하지 못했다면 곧바로 재사용할 수 있는 것은 아니다.

REQUEST COMPARISON / 동일 URL의 저장 응답 대조
저장 당시 요청:
GET /guide HTTP/1.1
Accept-Language: ko

저장된 응답:
Vary: Accept-Language

새 요청 1: Accept-Language: ko  → Vary 일치
새 요청 2: Accept-Language: en  → Vary 불일치

RFC 9111 §4.1은 Vary에 명시된 요청 필드가 원래 요청과 일치하지 않는 저장 응답을 재검증 없이 사용해서는 안 된다고 규정한다. 근거: RFC 9111 §4.1

04 / NORMALIZATION

정규화와 캐시 효율의 관계

헤더 값이 문자열로 다르더라도 HTTP 문법상 같은 의미일 수 있다. 정규화(Normalization)는 허용된 범위에서 이러한 차이를 정리해 불필요한 캐시 변형을 줄이는 방법이다. RFC 9111은 공백의 추가·제거, 의미가 보존되는 필드 값의 결합, 필드 정의에 따라 허용되는 정렬이나 대소문자 처리 등을 통해 두 요청이 일치한다고 판단할 수 있도록 한다.

요청 AAccept-Encoding: gzip, br
요청 BAccept-Encoding: br, gzip
↓
의미 보존이 확인된 경우 동일한 선택 기준으로 처리 가능단, 서버의 실제 협상 규칙과 캐시 구현의 정규화 정책이 일치해야 한다.

그림 3. 순서 차이가 의미에 영향을 주지 않는다고 확인된 경우의 개념 예시. 모든 헤더에 일괄 적용할 수 있는 규칙은 아니다.

정규화는 문자열을 무조건 소문자로 바꾸거나 쉼표 뒤 항목을 임의로 정렬하는 작업이 아니다. 헤더마다 문법이 다르며, 우선순위 가중치(q)와 제외 조건이 있는 Accept·Accept-Language는 순서와 값의 의미를 신중하게 해석해야 한다. 서버가 구별하는 두 요청을 캐시만 동일하게 취급하면 잘못된 응답을 반환할 수 있다.

헤더정규화 검토 대상주의할 점
Accept미디어 범위·가중치·문법상 동등성서버가 실제로 선택하는 미디어 형식과 일치해야 함
Accept-Language언어 범위·우선순위지역 코드와 q=0 등 제외 조건을 임의로 제거하지 않음
Accept-Encoding허용 인코딩과 가중치클라이언트가 지원하지 않는 인코딩을 선택하지 않음
User-Agent일반적으로 값의 종류가 매우 많음원본 값 전체를 변형 기준으로 삼으면 캐시 효율 저하 가능

정규화가 가능한 범위는 HTTP 표준이 허용하는 동등성과 실제 캐시 제품이 구현한 기능을 구분해서 판단해야 한다. 특정 CDN의 정규화 동작을 모든 HTTP 캐시에 공통인 것처럼 일반화해서는 안 된다. 근거: RFC 9111 §4.1

05 / CACHE POLICY

Vary와 Cache-Control은 무엇이 다른가

Vary는 어떤 요청에 어떤 저장 응답이 일치하는지를 설명한다. Cache-Control은 그 응답을 저장할 수 있는지, 공유할 수 있는지, 얼마나 오래 신선하게 사용할 수 있는지를 제어한다. 두 헤더는 서로 대체하지 않고 함께 작동한다.

응답 선택 조건Vary요청 헤더 비교 · 표현의 구분
저장·재사용 정책Cache-Control저장 허용 · 공유 범위 · 신선도 · 재검증
HTTP RESPONSE / 두 헤더를 함께 사용하는 예
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: ko
Vary: Accept-Language
Cache-Control: public, max-age=3600

이 예시는 언어별 응답을 구분하면서, 다른 캐시 조건도 충족하는 경우 해당 응답을 최대 3,600초 동안 신선한 것으로 취급할 수 있음을 뜻한다. max-age가 지났다고 반드시 저장 데이터가 즉시 삭제되는 것은 아니며, 재검증 등을 거쳐 다시 사용할 수 있다.

지시문핵심 의미Vary와의 관계
public공유 캐시의 저장을 명시적으로 허용응답 변형의 구분은 별도로 필요
private공유 캐시에 저장하지 않고 개인 캐시에는 저장 가능개인화 응답 보호를 Vary에만 맡기지 않음
no-cache저장은 가능하지만 재사용 전에 검증 필요Vary가 일치해도 검증 절차 필요
no-store표준을 따르는 캐시에 저장 금지저장하지 않으므로 변형 선택의 실익이 없음
max-age=3600응답 신선도 수명을 초 단위로 지정신선한 응답이라도 Vary 불일치 시 그대로 재사용 불가
주의: no-cache는 저장 금지가 아니다.저장을 막는 지시문은 no-store다. 또한 Vary: Cookie만으로 개인화 응답이 다른 사용자에게 공유되지 않는다고 가정해서는 안 된다. 민감한 응답은 명시적인 캐시 정책과 인증·권한 검사가 필요하다.

근거: RFC 9111 §5.2, MDN Cache-Control

06 / FAILURE MODES

잘못된 Vary 설계가 만드는 세 가지 문제

① 응답 선택 기준이 누락되면 잘못된 콘텐츠를 재사용할 수 있다

서버가 Accept에 따라 HTML과 JSON을 반환하면서 Vary: Accept를 빠뜨리면 캐시는 두 응답을 구별하는 데 필요한 신호를 얻지 못한다. 같은 URL의 요청에 다른 형식의 콘텐츠가 전달될 수 있다. 여러 서버 인스턴스나 오류 응답 경로에서도 필요한 Vary 선언이 일관되어야 한다.

② 변형 기준이 지나치게 많으면 캐시가 잘게 쪼개진다

예를 들어 Vary: User-Agent는 브라우저·운영체제·버전별로 매우 다양한 값이 들어오므로 재사용 가능한 응답이 여러 항목으로 분산될 수 있다. 캐시 적중률이 낮아지고 저장 공간과 원본 서버 요청이 늘어날 가능성이 있다. 필요한 기능만 구분하거나 서버와 합의된 소수의 범주로 정규화할 수 있는지 검토해야 한다.

③ 개인화 응답을 Vary에만 의존하면 정보 노출 위험이 남는다

쿠키 값이나 요청 헤더를 캐시 키에 포함하는 것만으로 권한 경계가 보장되지는 않는다. 사용자별 페이지에는 Cache-Control: private 또는 저장 자체가 부적절한 경우 no-store를 검토하고, 원본 서버의 접근 제어를 유지해야 한다. private는 브라우저 같은 개인 캐시에 저장할 수 있다는 뜻이므로, 브라우저에도 남겨서는 안 되는 데이터라면 별도의 정책이 필요하다.

구분 부족잘못된 표현 재사용
적절한 구분서버의 실제 선택 기준 반영
구분 과다캐시 분산·적중률 저하

그림 4. 캐시 변형 설계에서 동시에 고려해야 하는 정확성과 재사용 효율.

근거: MDN HTTP Caching — Vary, RFC 9111 §3

07 / DESIGN PRINCIPLE

서버와 캐시는 같은 응답 선택 기준을 공유해야 한다

Vary를 올바르게 사용하는 출발점은 서버가 실제로 무엇을 기준으로 응답을 바꾸는지 파악하는 것이다. 언어에 따라 콘텐츠를 바꾼다면 언어 선택 헤더를, 미디어 형식에 따라 HTML과 JSON을 바꾼다면 형식 선택 헤더를 응답에 명시해야 한다. 캐시는 그 선언을 참고해 저장된 표현을 요청에 맞게 선택한다.

그다음에는 저장과 재사용의 범위를 결정해야 한다. 공개적으로 공유할 수 있는 콘텐츠인지, 개인화되어 개인 캐시에만 남겨야 하는지, 저장 자체를 피해야 하는지에 따라 Cache-Control을 별도로 설정한다. 마지막으로 변형의 개수가 지나치게 늘어나지 않는지, 정규화가 서버의 의미 해석과 일치하는지 확인해야 한다.

THE TAKEAWAY / 핵심 정리

Vary는 정확한 응답을 선택하기 위한 약속이다. 캐시가 같은 URL을 무조건 같은 콘텐츠로 취급하지 않도록, 서버가 응답 선택에 영향을 준 요청 필드를 알린다.

캐시의 정확성은 Vary, 저장과 재사용의 안전성은 Cache-Control, 효율은 적절한 변형 기준과 의미 보존 정규화를 함께 고려할 때 확보할 수 있다.

REFERENCE / GLOSSARY

용어 정리

콘텐츠 협상
Content Negotiation
요청에 담긴 선호 정보를 참고해 서버가 자원의 표현을 선택하는 HTTP 메커니즘.
표현
Representation
같은 자원을 HTML, JSON, 언어별 문서 등 특정 형태로 나타낸 응답 데이터와 관련 메타데이터.
캐시 키
Cache Key
캐시가 저장된 응답을 검색하고 구분하기 위해 사용하는 식별 기준.
응답 변형
Variant
같은 자원에 대해 협상 조건 등에 따라 선택될 수 있는 서로 다른 응답 표현.
정규화
Normalization
의미를 유지하는 범위에서 헤더 값의 표현 차이를 정리해 비교하는 처리.
신선도
Freshness
저장된 응답을 서버 재검증 없이 재사용할 수 있는지 판단하는 캐시 상태.
재검증
Revalidation
저장된 응답을 계속 사용할 수 있는지 서버에 조건부 요청 등으로 확인하는 절차.
공유 캐시
Shared Cache
여러 사용자에게 응답을 재사용할 수 있는 중간 캐시. 개인 브라우저 캐시와 구별된다.
REFERENCE / PRIMARY SOURCES

출처

표준 문서를 핵심 근거로 사용하고, MDN 문서는 실무 예시와 용어 설명을 보조하는 자료로 활용했다.

  1. IETF / RFC Editor — RFC 9110: HTTP Semantics, §12 Content Negotiation 및 §12.5.5 Vary.
    https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.5
  2. IETF / RFC Editor — RFC 9111: HTTP Caching, §4.1 Calculating Cache Keys with the Vary Header Field.
    https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1
  3. IETF / RFC Editor — RFC 9111: HTTP Caching, §3 Storing Responses in Caches 및 §5.2 Cache-Control.
    https://www.rfc-editor.org/rfc/rfc9111.html
  4. Mozilla Developer Network — HTTP caching.
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching
  5. Mozilla Developer Network — Cache-Control.
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control