Last-Modified
고친 사람 github-actions[bot]
Last-Modified 는 서버가 보내는 내용을 마지막으로 고친 때를 함께 알려 줍니다. 받는 쪽은 다음 요청에 그 시각을 적어 그 뒤로 바뀌었는지를 묻습니다. 서버는 바뀐 것이 없다는 답을 본문 없이 짧게 돌려줍니다. 같은 내용을 두 번 내려보내는 일이 그만큼 줄어듭니다.
쉽고 빠른 이해
Last-Modified 는 서버가 이 내용을 마지막으로 고친 때를 알려 주는 값입니다. 브라우저가 어제 받아 둔 로고 이미지를 오늘도 그냥 써도 되는지 따질 때 이 시각을 근거로 씁니다.
이 값이 없으면 브라우저와 캐시는 안 바뀐 파일도 매번 처음부터 다시 받습니다. 바뀌었는지 물어볼 근거가 없기 때문입니다.
어떻게 도는가:
- 서버가 내용을 보내면서 마지막으로 고친 시각을 한 줄 덧붙입니다
- 받는 쪽은 내용과 그 시각을 함께 저장합니다
- 다음 요청에 그 시각을 적어 보내면 서버는 그 뒤로 바뀐 것이 있는지만 답합니다
대가는 정밀도입니다. 1초 안에 두 번 고치면 두 판이 같은 값을 답니다. 서버 시계에 기대는 값이라 시계가 어긋나면 판단도 어긋납니다.
상세
Last-Modified 는 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 응답에 실리는 헤더 필드입니다. 서버는 지금 내려보내는 내용을 마지막으로 고쳤다고 믿는 시각을 여기 적습니다. 그 시각은 요청을 다 처리한 시점을 기준으로 정합니다.
한 페이지는 대개 여러 조각이 합쳐진 결과입니다. 글과 이미지와 설정이 각각 따로 바뀝니다. 그래서 이 값은 보통 그 조각들 중 가장 최근에 바뀐 때가 됩니다. 어느 조각을 어떻게 세는지는 서버를 만드는 쪽에 맡겨져 있습니다.
값의 생김새
값은 시각 하나입니다. 적는 꼴도 정해져 있습니다.
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
요일, 일·월·연, 시·분·초, 그리고 GMT(Greenwich Mean Time, 그리니치 표준시)가 차례로 붙습니다. 시간대는 언제나 GMT 로 적습니다. 그래서 보내는 쪽도 받는 쪽도 환산할 일이 없습니다.
초까지만 적습니다. 그보다 잘게는 나누지 않습니다.
지금은 안 쓰는 옛 꼴이 둘 더 있습니다. 오래된 서버가 아직 그 꼴로 보내서 받는 쪽은 그 둘도 읽을 줄 알아야 합니다. 같은 시각을 세 꼴로 적으면 이렇습니다.
Tue, 15 Nov 1994 12:45:26 GMT
Tuesday, 15-Nov-94 12:45:26 GMT
Tue Nov 15 12:45:26 1994
둘째는 요일 이름을 다 적고 연도를 두 자리로 줄인 꼴입니다. 셋째는 예전 C 라이브러리가 시각을 찍어 내던 꼴이라 시간대 표시가 없습니다. 보내는 쪽은 첫째 꼴 하나로만 보냅니다.
안 바뀌었는지 물어보는 쓰임
이 값의 주된 쓰임은 조건부 요청입니다. 조건부 요청은 「이 조건이 맞을 때만 해 주세요」라는 단서를 붙여 보내는 요청입니다. 브라우저는 앞서 받아 둔 시각을 그 단서로 씁니다.
sequenceDiagram
participant 브라우저
participant 서버
브라우저->>서버: 로고 이미지를 주세요
서버-->>브라우저: 본문 + 마지막으로 고친 시각
Note over 브라우저: 본문과 시각을 함께 저장한다
브라우저->>서버: 그 시각 뒤로 바뀌었나요
서버-->>브라우저: 안 바뀌었습니다 · 본문 없음
둘째 요청에 붙는 단서가 If-Modified-Since 헤더입니다. 값으로는 앞서 받아 둔 시각을 그대로 적습니다. 서버는 그 시각 이후로 바뀐 것이 없으면 본문을 만들지 않고 304 Not Modified 라는 상태 코드만 돌려줍니다. 오가는 양이 헤더 몇 줄로 줄어듭니다.
바뀌었으면 서버는 새 본문과 새 시각을 함께 보냅니다. 받는 쪽은 저장해 둔 사본을 새것으로 갈아 끼웁니다.
쓰기 요청도 이 시각을 단서로 답니다. If-Unmodified-Since 는 「내가 본 뒤로 아무도 안 고쳤을 때만 고쳐라」는 뜻입니다. 남이 먼저 고쳐 둔 내용을 모르고 덮어쓰는 일을 막습니다.
시각이라서 생기는 한계
내용이 어느 버전인지 가리키는 짧은 값을 검증자라고 부릅니다. Last-Modified 는 그 검증자 중 시각 쪽입니다.
시각을 초까지만 적으므로 같은 초 안에 두 번 고쳐진 내용은 두 버전이 같은 값을 답니다. 뒤엣것을 놓칠 수 있다는 뜻입니다. 그래서 이 값은 약한 검증자로 다룹니다.
같은 버전인지를 바이트 하나까지 따져야 하는 쓰임에는 이 값만으로 모자랍니다. 내려받다 끊긴 파일의 뒷부분만 이어받는 경우가 그렇습니다. 그럴 때는 버전마다 붙는 짧은 문자열인 ETag 를 함께 씁니다.
같은 일을 하는 두 값이 어디서 갈리는지 나란히 놓으면 이렇습니다.
| Last-Modified | ETag | |
|---|---|---|
| 값의 근거 | 마지막으로 고친 시각 | 버전마다 서버가 만드는 문자열 |
| 구분 단위 | 초 | 내용이 바뀌면 곧바로 갈린다 |
| 서버가 드는 짐 | 파일의 수정 시각을 읽으면 된다 | 버전마다 값을 만들어 들고 있어야 한다 |
그래서 값싼 Last-Modified 를 기본으로 두고, 버전을 정확히 갈라야 할 때만 ETag 를 얹습니다.
시계도 이 값의 발목을 잡습니다. Date 는 서버가 응답에 적는 현재 시각입니다. 서버는 그보다 미래인 Last-Modified 를 만들지 않습니다. 미래 시각이 들어가면 받는 쪽의 판단이 어그러지기 때문입니다.
서버 시계 자체가 어긋나면 이 값도 같이 어긋납니다. 시계가 뒤로 밀린 동안 고친 내용에는 예전 시각이 붙습니다. 받는 쪽은 자기가 들고 있는 시각보다 앞선 값을 보고 안 바뀌었다고 믿어, 낡은 사본을 계속 내줍니다.
캐시가 만료를 짐작하는 근거
서버가 「언제까지 써도 된다」고 못박아 주는 시각, 곧 만료 시각을 응답에 안 넣을 때가 있습니다. 캐시는 그럴 때 만료 시각을 스스로 짐작해도 됩니다.
저장해 둔 사본을 언제까지 그냥 내줘도 되는지를 신선도라고 부릅니다. 짐작한 값도 그 판단에 씁니다.
Last-Modified 가 있으면 그 시각 이후로 흐른 시간의 일부를 신선한 기간으로 잡습니다. 흔히 쓰는 비율은 10퍼센트 정도입니다. 반년째 안 바뀐 문서라면 앞으로 보름 남짓은 그냥 내줘도 된다고 보는 어림입니다.
관련 항목
이 시각과 함께 응답에 실리는 필드
ETag · Date · Expires · Cache-Control · Age
이 시각을 조건으로 적어 보내는 요청 헤더
If-Modified-Since · If-Unmodified-Since · If-None-Match · If-Range
조건을 따진 결과로 돌아오는 상태 코드
304 Not Modified · 412 Precondition Failed · 상태 코드
이 값의 성질을 가르는 검증자 분류
검증자 · 약한 검증자 · 강한 검증자 · 엔티티 태그 · 불투명 검증자
이 값을 읽어 판단하는 캐시 개념
조건부 요청 · 재검증 · 신선도 · 캐시 키 · HTTP 캐시 · 브라우저 캐시 · CDN
이 필드를 정의하는 표준 문서
RFC 9110 · RFC 9111 · RFC 7232 · RFC 7234
이것이 속하는 상위 분류
다른 이름: Last-Modified 헤더 · Last-Modified 필드 · 마지막 수정 시각