Expires
고친 사람 github-actions[bot]
Expires 는 받아 둔 응답을 언제까지 다시 물어보지 않고 써도 되는지를 서버가 알려 주는 헤더 필드입니다. 캐시는 여기 적힌 시각이 지나기 전까지 저장해 둔 응답을 그대로 내줍니다. 같은 이름이 쿠키에도 붙습니다. 쿠키 쪽 Expires 는 브라우저가 그 쿠키를 언제까지 들고 있을지를 정합니다.
쉽고 빠른 이해
Expires 는 응답에 붙이는 유통기한입니다. 서버가 Expires: Thu, 01 Dec 1994 16:00:00 GMT
라고 적어 보내면 캐시는 그 시각까지 이 응답을 다시 받지 않고 씁니다.
이게 없으면 캐시는 저장해 둔 응답을 언제까지 믿어도 되는지 알 수 없습니다. 쓸 때마다 서버에 다시 묻거나, 스스로 짐작한 기간만큼 붙들고 있게 됩니다.
캐시는 이렇게 씁니다.
- 적힌 시각에서 응답이 만들어진 시각을 빼 얼마나 오래 써도 되는지를 구합니다
- 그 기간이 다 되기 전까지는 저장해 둔 응답을 곧바로 내줍니다
- 기간이 지나면 서버에 다시 물어 아직 맞는 응답인지 확인합니다
대가는 시각에 기댄다는 점입니다. 보내는 쪽과 받는 쪽의 시계가 어긋나면 만료 시점도 그만큼 어긋납니다. 기간을 초 수로 적는 max-age 가 같이 와 있으면 이 값은 쓰이지 않습니다.
상세
Expires 는 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 응답에 붙는 헤더 필드입니다. 응답 본문 앞에 딸려 오는 정보 묶음을 헤더라고 하고, 그 안의 한 줄 한 줄을 필드라고 부릅니다. 이 필드에는 응답을 만든 서버가 「이 시각이 지나면 이 응답은 낡은 것이다」라고 미리 적어 둡니다.
이 필드가 없으면 캐시는 손에 든 응답을 얼마나 오래 써도 되는지 알 수 없습니다. 원본이 바뀌었는지는 서버만 알기 때문입니다. 그래서 서버가 앞날의 만료 시각을 직접 적어 보내고, 캐시는 그 시각까지 서버에 다시 묻지 않습니다.
값의 생김새
값은 시각 하나입니다. 적는 꼴도 정해져 있습니다. 요일·일·달·해·시각을 정해진 순서로 적고 끝에 GMT(Greenwich Mean Time, 그리니치 표준시)를 붙입니다.
이 꼴을 HTTP-date 라고 부릅니다. HTTP 의 다른 시각 타임스탬프도 같은 꼴로 적습니다.
Expires: Thu, 01 Dec 1994 16:00:00 GMT
값이 날짜로 읽히지 않으면 캐시는 그것을 이미 지난 시각으로 해석해야 합니다. 숫자 0 한
글자도 그렇게 봅니다. 그래서 값이 깨진 채로 와도 캐시가 그 응답을 오래 붙들고 있는 일은
없습니다.
캐시가 이 시각을 쓰는 곳
캐시는 이 응답을 얼마 동안 써도 되는지를 기간으로 잡아 둡니다. 그 기간을 신선도 수명이라고 부릅니다. 다른 단서가 없으면 캐시는 Expires 에 적힌 시각에서 응답이 만들어진 시각을 빼 이 기간을 구합니다. 응답이 만들어진 시각은 Date 필드가 싣고 옵니다.
예를 들어 Date 가 오전 9시이고 Expires 가 오전 10시면 신선도 수명은 한 시간입니다. 캐시는 그 한 시간 동안 이 응답을 서버에 묻지 않고 내줍니다. 시간이 다 차면 응답은 낡은 데이터가 되고, 캐시는 서버에서 검증을 받은 뒤에야 다시 씁니다.
수명을 구할 단서가 Expires 하나만은 아닙니다. Cache-Control 은 캐시에게 줄 지시를 적어 보내는 헤더입니다. 그 안의 지시 하나하나를 지시어라고 부릅니다. max-age 와 s-maxage 는 신선한 기간을 초 수로 적는 지시어입니다.
둘의 차이는 누가 읽느냐입니다. max-age 는 모든 캐시가 읽습니다. s-maxage 는 여러 사용자가 같이 쓰는 캐시만 읽습니다. 그런 캐시를 공유 캐시라고 부르고 CDN(Content Delivery Network, 콘텐츠 전송 네트워크)이나 프록시 서버가 여기 듭니다.
단서가 여럿이면 캐시는 정해진 차례로 따져 처음 맞는 것을 씁니다.
flowchart TD
A["공유 캐시이고 s-maxage 가 있나"] -->|있다| S["s-maxage 값을 쓴다"]
A -->|없다| B["max-age 가 있나"]
B -->|있다| M["max-age 값을 쓴다"]
B -->|없다| C["Expires 가 있나"]
C -->|있다| E["Expires 에서 Date 를 뺀다"]
C -->|없다| H["캐시가 스스로 짐작한다"]
Expires 는 이 차례의 세 번째 칸입니다. 초 수로 적는 두 지시어가 다 없을 때만 차례가 옵니다. 셋 다 없으면 캐시가 나름의 기준으로 기간을 잡습니다.
max-age 와의 우선순위
응답에 max-age 지시어가 있으면 받는 쪽은 Expires 를 무시해야 합니다(MUST). 공유 캐시는 s-maxage 지시어가 있을 때도 같은 이유로 Expires 를 버립니다.
무시하는 까닭은 두 필드가 다투게 두지 않으려는 것입니다. 이럴 때 Expires 값은 Cache-Control 을 아직 구현하지 않은 받는 쪽만을 위한 것입니다. 그래서 서버는 둘을 같이 실어 보내도 됩니다. 새 캐시는 초 수로 적은 지시어를 읽고, 옛 캐시는 시각을 읽습니다.
시계가 없는 서버
Expires 는 절대 시각이라 적는 쪽의 시계가 맞아야 뜻이 섭니다. 응답의 원본을 갖고 있는 서버를 오리진 서버라고 부릅니다. 시계가 없는 오리진 서버는 이 필드를 만들어 보내면 안 됩니다.
예외는 둘입니다. 값이 언제나 지난 고정 시각이거나, 시계를 가진 다른 시스템이 그 값을 미리 붙여 준 경우입니다.
초 수를 세는 max-age 에는 이 제약이 없습니다. 기간은 양쪽 시계가 서로 안 맞아도 길이가 변하지 않기 때문입니다.
쿠키에 붙는 Expires 속성
이름이 같은 값이 쿠키에도 있습니다. Set-Cookie 헤더 안에 속성으로 적힙니다. 브라우저는 이 값을 보고 그 쿠키를 언제까지 저장해 둘지 정합니다. 날짜가 지나면 브라우저가 그 쿠키를 지웁니다.
쿠키 쪽 Expires 는 캐시의 신선도 규칙과 얽히지 않습니다. 앞 절들이 다룬 Expires 는 응답 하나가 언제 낡는지를 적는 응답 헤더 쪽입니다.
관련 항목
이 필드를 정의하고 캐시 규칙을 붙인 표준 문서
RFC 9111 · RFC 9110 · RFC 7234 · RFC 6265 · HTTP
같은 응답에서 신선도를 함께 정하는 필드
Cache-Control · max-age · s-maxage · Age · Date · HTTP-date
이 값이 좌우하는 캐시 개념
신선도 · 신선도 수명 · 나이 · 낡은 데이터 · 검증 · 무효화 · TTL
이 값을 읽고 재사용을 판단하는 캐시
HTTP 캐시 · 브라우저 캐시 · CDN · 공유 캐시 · 프록시 · 오리진 서버
값이 만료된 뒤에 쓰는 조건부 요청 수단
조건부 요청 · ETag · Last-Modified · If-Modified-Since · If-None-Match
이름이 겹치는 쿠키 속성과 그 이웃
쿠키 · Set-Cookie · Max-Age · 세션 · 사용자 에이전트
이것이 속하는 상위 분류
다른 이름: Expires 헤더 · Expires 필드