사전 RFC 9111
표준

RFC 9111

gabury1고친 사람 github-actions[bot]

RFC 9111 은 웹 응답을 저장해 두었다가 다시 내줘도 되는지를 판단하는 규칙을 정한 표준 문서입니다. 브라우저나 프록시 같은 캐시가 어떤 응답을 저장하고 언제까지 다시 쓸지를 이 문서가 정합니다. 서버는 응답에 헤더를 붙여 그 판단을 조정합니다.

쉽고 빠른 이해

RFC 9111 은 웹 캐시가 지킬 규칙을 모은 문서입니다. 브라우저가 한 번 받은 이미지를 다시 받지 않고 꺼내 쓰는 판단이 이 규칙을 따릅니다.

규칙이 없으면 캐시마다 제멋대로 판단합니다. 어떤 캐시는 이미 바뀐 페이지를 계속 내줍니다. 어떤 캐시는 한 사람의 개인 정보가 담긴 응답을 다른 사람에게 내줍니다.

돌아가는 방식은 이렇습니다.

  1. 응답이 오면 서버가 붙인 헤더를 보고 저장해도 되는지 정합니다
  2. 같은 요청이 다시 오면 저장한 응답이 아직 쓸 만한 나이인지 봅니다
  3. 너무 오래됐으면 서버에 바뀌었는지만 물어보고, 안 바뀌었으면 저장한 것을 내줍니다

대가도 있습니다. 규칙을 지켜도 캐시가 내주는 응답은 서버의 지금 값보다 늦을 수 있습니다. 얼마나 늦어도 되는지는 서버가 헤더로 정해 줘야 합니다.

상세

RFC(Request for Comments)는 IETF(Internet Engineering Task Force, 인터넷 기술 표준을 만드는 단체)가 번호를 붙여 내는 문서입니다. RFC 9111 은 그중 「HTTP Caching」이라는 제목을 단 문서입니다. HTTP(HyperText Transfer Protocol) 캐시가 무엇인지, 그리고 캐시의 동작을 조정하는 헤더 필드를 정의합니다.

이 문서는 2022년 6월에 나왔습니다. 이전 판인 RFC 7234 를 대체합니다. RFC 가운데 인터넷 표준 단계까지 오른 문서는 STD(Standard, 표준) 번호를 하나 더 받습니다. RFC 9111 이 받은 번호는 STD 98 입니다.

요청과 응답이 무슨 뜻인지는 RFC 9110 이 정합니다. 이 문서는 그중 캐시에 관한 것만 떼어 담았습니다.

HTTP 캐시

캐시는 앞서 받은 응답을 저장해 두었다가 같은 요청에 다시 내주는 저장소입니다. 회사 앞 편의점이 본사 창고까지 가지 않고 진열대에서 바로 물건을 내주는 것과 비슷합니다. 다만 편의점 물건과 달리 응답은 원본이 바뀌면 낡은 것이 됩니다.

HTTP 캐시는 응답을 담는 저장소와, 그 안의 응답을 저장하고 찾고 지우는 판단을 함께 가리킵니다. 판단을 규칙으로 묶어 둔 까닭은 캐시가 요청과 응답 사이에 끼어 있기 때문입니다. 규칙이 없으면 서버는 자기 응답이 중간에서 얼마나 오래 떠돌지 알 수 없습니다.

캐시를 두는 것 자체는 선택입니다. HTTP 는 캐시가 없어도 돕니다. 캐시가 응답을 저장하지 않아도 규칙을 어긴 것이 아닙니다. 이 문서가 정하는 것은 캐시를 둔다면 지켜야 할 선입니다.

사설 캐시와 공유 캐시

캐시는 누가 쓰느냐로 둘로 갈립니다. 둘을 가르는 까닭은 한 사람만 봐야 하는 응답이 있기 때문입니다.

사설 캐시 공유 캐시
쓰는 사람 한 사용자 여러 사용자
흔한 예 브라우저 캐시 프록시 · CDN(Content Delivery Network, 콘텐츠 전송 네트워크)
로그인한 사용자의 응답 저장해도 된다 서버가 허락할 때만 저장한다

공유 캐시가 한 사람의 장바구니 페이지를 저장하면 다음 사람이 그 페이지를 받게 됩니다. 그래서 공유 캐시에만 걸리는 제한이 있습니다. 공유 캐시에만 통하는 헤더 값도 있습니다.

요청이 캐시를 지나는 흐름

캐시가 요청을 받으면 몇 가지를 차례로 따집니다. 아래 그림은 그 순서를 보입니다.

flowchart TD
    A["요청이 도착한다"] --> B{"캐시 키로 찾은<br/>저장 응답이 있나"}
    B -->|없다| C["오리진 서버로 넘기고<br/>받은 응답을 저장할지 정한다"]
    B -->|있다| D{"아직 신선한가"}
    D -->|신선하다| E["저장 응답을 바로 내준다"]
    D -->|낡았다| F["오리진 서버에<br/>바뀌었는지 묻는다"]
    F -->|안 바뀌었다| G["저장 응답을 갱신해 내준다"]
    F -->|바뀌었다| C

요청이 오면 캐시는 먼저 저장해 둔 응답이 있는지 찾습니다. 없으면 오리진 서버에 요청을 넘깁니다. 오리진 서버는 그 자원을 실제로 가진 서버입니다. 이 뒤로 그냥 「서버」라고 쓰면 오리진 서버를 뜻합니다. 저장 응답이 있으면 아직 써도 되는지 따집니다. 낡았으면 서버에 확인한 뒤 내줍니다.

저장해도 되나

캐시는 받은 응답을 아무거나 저장하지 않습니다. 조건은 셋입니다.

  1. 요청 메서드가 캐시를 허용해야 합니다. GET 이 대표적입니다
  2. 상태 코드도 캐시가 뜻을 아는 것이어야 합니다. 200 OK 가 대표적입니다
  3. 서버가 저장을 막지 않았어야 합니다

서버가 저장을 막는 대표적인 방법은 Cache-Control 헤더에 no-store 를 적는 것입니다. 이 값이 붙은 응답은 어느 캐시도 저장하면 안 됩니다.

이 표준은 규칙마다 강도를 갈라 적습니다. 반드시 지킬 것(MUST)이 있고, 이유가 있으면 어겨도 되는 것(SHOULD)이 있습니다. no-store 금지는 반드시 지킬 쪽입니다. 이 금지를 어긴 캐시는 표준을 안 지킨 캐시입니다. 강도의 종류는 맨 끝 「요구 강도」 소절에 모았습니다.

캐시 키

같은 요청인지 가리려면 기준이 있어야 합니다. 그 기준이 캐시 키입니다. 캐시 키는 최소한 요청 메서드와 대상 URI(Uniform Resource Identifier, 자원을 가리키는 주소)로 이루어집니다.

주소가 같아도 응답이 갈리는 경우가 있습니다. 같은 주소가 한국어 사용자에게는 한국어 페이지를, 영어 사용자에게는 영어 페이지를 주는 식입니다. 서버는 이럴 때 Vary 헤더에 Accept-Language 처럼 응답을 가른 요청 헤더의 이름을 적습니다. 캐시는 그 헤더 값까지 같아야 저장 응답을 내줍니다.

신선도

저장한 응답을 서버에 묻지 않고 내줘도 되는 기간이 신선도 수명입니다. 응답이 만들어진 뒤로 흐른 시간은 나이라고 부릅니다. 나이가 신선도 수명보다 작으면 응답은 신선합니다. 같거나 크면 낡은 것입니다.

캐시는 나이를 스스로 셉니다. 응답을 받은 뒤 자기 안에 머문 시간을 잽니다. 앞선 캐시를 거쳐 온 응답이면 그 캐시가 Age 헤더에 적어 보낸 초를 더합니다. 응답을 내줄 때는 이렇게 센 나이를 다시 Age 헤더에 적습니다.

아래는 max-age=600 이 붙은 응답을 오리진 서버에서 바로 받아 100초를 둔 뒤의 계산입니다.

신선도 수명 = max-age   // 600초
받을 때 Age = 없음      // 0초
나이 = 0 + 머문 시간    // 100초
신선한가 = 100 < 600    // 신선하다
남은 시간 = 600 - 100   // 500초

신선도 수명은 헤더에서 읽습니다. 공유 캐시라면 공유 캐시 전용 수명인 s-maxage 를 먼저 보고, 그다음 max-age 를 봅니다. 둘 다 없으면 Expires 헤더가 적은 만료 시각에서 응답이 만들어진 시각을 뺍니다. 응답이 만들어진 시각은 서버가 붙인 Date 헤더에서 읽습니다.

헤더에 기간이 하나도 없으면 캐시가 스스로 수명을 추정해도 됩니다. 이것을 휴리스틱 신선도라고 부릅니다. 추정하는 계산법은 이 문서가 고정하지 않고 캐시에 맡깁니다.

검증

낡은 응답을 버리고 처음부터 다시 받으면 낭비일 수 있습니다. 응답이 안 바뀌었을 수도 있기 때문입니다. 그래서 캐시는 서버에 「이 버전이 아직 최신인가」만 묻습니다. 이 절차를 검증이라고 부릅니다.

묻는 데는 검증자를 씁니다. 검증자는 응답의 버전을 가리키는 값입니다. 서버가 준 ETag(entity tag, 버전 표시 값)나 Last-Modified 시각이 그것입니다. 캐시는 이 값을 If-None-Match 나 If-Modified-Since 헤더에 실어 조건부 요청을 보냅니다.

서버가 304 Not Modified 로 답하면 버전이 안 바뀐 것입니다. 캐시는 본문을 다시 받지 않고, 저장 응답의 헤더만 새로 고쳐 내줍니다. 버전이 바뀌었으면 서버가 새 응답을 전부 보냅니다. 캐시는 그것을 저장합니다.

낡은 응답을 검증 없이 내주는 것은 좁은 경우에만 됩니다. 오리진 서버와 연결이 끊긴 때가 그렇습니다. 서버가 must-revalidate 를 붙였으면 그런 때에도 낡은 응답을 내주면 안 됩니다.

무효화

무효화는 저장한 응답을 더는 쓰지 않게 지우거나 낡은 것으로 표시하는 일입니다. 자원을 바꾸는 요청이 지나간 뒤에 필요합니다. 게시글을 고쳤는데 캐시가 고치기 전 글을 계속 내주면 안 되기 때문입니다.

POST · PUT · DELETE 처럼 서버의 상태를 바꿀 수 있는 요청에 오류가 아닌 응답이 오면, 캐시는 그 대상 URI 로 저장한 응답을 무효화해야 합니다. 이것도 어기면 안 되는 규칙입니다.

이 무효화는 그 요청이 지나간 캐시에서만 일어납니다. 다른 경로에 있는 캐시는 그 요청을 못 봤습니다. 그래서 옛 응답을 계속 가집니다. 모든 캐시를 한 번에 비우는 방법은 이 문서에 없습니다.

이 문서가 정의하는 헤더 필드

캐시 동작을 조정하는 헤더 필드는 아래와 같습니다.

필드 하는 일
Cache-Control 저장 여부 · 신선도 수명 · 검증 요구를 지시어로 적는다
Age 응답이 캐시에서 보낸 시간을 초로 알린다
Expires 응답이 낡는 시각을 날짜로 적는다
Pragma HTTP/1.0 시절의 필드다. 폐지 예정으로 남아 있다

Cache-Control 에는 쉼표로 여러 지시어를 적습니다. 자주 만나는 것들을 뜻별로 모으면 이렇습니다.

지시어 뜻
no-store 어느 캐시도 저장하지 않는다
no-cache 저장은 해도 되지만 내주기 전에 매번 검증한다
max-age=초 신선도 수명을 초로 정한다
s-maxage=초 공유 캐시에만 쓰는 신선도 수명이다
private 사설 캐시만 저장한다
public 보통은 저장 못 할 응답도 저장을 허락한다(예: 공유 캐시에서 로그인한 사용자의 응답)
must-revalidate 낡은 뒤에는 검증 없이 내주지 않는다

no-cache 는 이름과 뜻이 어긋나 자주 헷갈립니다. 캐시를 안 쓴다는 뜻이 아니라, 저장은 하되 매번 서버에 확인하라는 뜻입니다. 저장 자체를 막는 것은 no-store 입니다.

요구 강도

이 문서는 규칙마다 요구 강도를 대문자 낱말로 붙입니다. 이 낱말들의 뜻은 RFC 2119 가 정합니다. 강도를 구분해야 어디까지 어겨도 표준을 지킨 것인지 알 수 있습니다.

낱말 뜻 이 문서의 예
MUST · MUST NOT 반드시 지킨다 no-store 응답을 저장하지 않는다
SHOULD 이유가 있으면 어길 수 있다 서버와 연결이 끊긴 때가 아니면 낡은 응답을 내주지 않는다
MAY 해도 되고 안 해도 된다 헤더에 기간이 없을 때 수명을 추정한다

관련 항목

이 문서와 함께 HTTP 를 정의하는 표준 문서

RFC 9110 · RFC 9112 · RFC 9113 · RFC 9114 · RFC 7234 · RFC 2119

RFC 9111 을 넓히는 확장 문서

RFC 5861 · RFC 8246 · RFC 9213 · stale-while-revalidate · stale-if-error · immutable

이 문서가 정의하는 헤더 필드

Cache-Control · Age · Expires · Pragma · Warning

Cache-Control 에 들어가는 지시어

no-store · no-cache · max-age · s-maxage · private · public · must-revalidate · proxy-revalidate · max-stale · min-fresh · only-if-cached · no-transform · must-understand

캐시가 응답을 다시 쓸지 가리는 개념

신선도 · 신선도 수명 · 나이 · 휴리스틱 신선도 · 캐시 키 · Vary · 검증자 · 재검증 · 조건부 요청 · 무효화 · 낡은 데이터

검증에 쓰는 헤더와 상태 코드

ETag · Last-Modified · If-None-Match · If-Modified-Since · 304 Not Modified

이 규칙을 따르는 캐시의 종류

HTTP 캐시 · 사설 캐시 · 공유 캐시 · 브라우저 캐시 · 프록시 · 리버스 프록시 · CDN

HTTP 캐시에서 나는 보안 문제

캐시 포이즈닝 · 타이밍 공격 · 웹 캐시 속임 · 서비스 거부 공격

이것이 속하는 상위 분류

HTTP · 캐싱 · IETF · RFC · 인터넷 표준

다른 이름: HTTP Caching · STD 98