no-store
고친 사람 github-actions[bot]
no-store 는 캐시에게 이 응답을 어디에도 남기지 말라고 알립니다. 서버가 이 표시를 붙인 응답은 브라우저도, 여럿이 같이 쓰는 중간 캐시도 저장하지 않고 건네주기만 합니다. 그래서 같은 주소를 다시 요청하면 매번 서버까지 가서 새로 받아 옵니다.
쉽고 빠른 이해
no-store 는 응답을 남기지 말라는 표시입니다. 은행 사이트가 계좌 잔액 화면에
Cache-Control: no-store 를 실어 보내면, 그 화면은 어느 캐시에도 저장되지 않습니다.
이 표시가 없으면 로그인한 사람의 화면이 여럿이 같이 쓰는 캐시에 남을 수 있습니다. 그러면 같은 주소를 요청한 다음 사람에게 남의 화면이 내려갑니다.
캐시는 이렇게 움직입니다.
- 서버가 보낸 응답에서 no-store 를 봅니다
- 응답을 저장하지 않고 요청한 쪽에 건네주기만 합니다
- 같은 요청이 다시 오면 남은 것이 없으니 서버에 다시 받으러 갑니다
대가는 속도입니다. 매번 서버가 응답을 새로 만들고 본문 전체를 다시 보냅니다. 그래서 모두에게 같은 이미지나 스크립트 파일에는 붙이지 않습니다.
상세
캐시는 받은 응답을 저장해 두었다가 같은 요청이 오면 서버 대신 내주는 곳입니다. 이 절은 no-store 가 그 캐시에게 무엇을 금하는지부터 봅니다. 응답 한 벌이 캐시를 지나가는 순서는 그림으로 따라갑니다. 이름이 비슷한 no-cache · private 은 표로 나란히 놓고 가릅니다.
그다음 요청에 붙는 no-store 와 이 표시가 막지 못하는 것을 봅니다. 마지막 소절은 언제 쓰고 언제 안 쓰는지입니다.
어디에 적는 값인가
no-store 는 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 메시지의 Cache-Control 헤더에 적습니다. Cache-Control 은 캐시에게 줄 지시를 적어 보내는 헤더 필드입니다.
Cache-Control 에는 지시를 여럿 쉼표로 늘어놓을 수 있습니다. 그 지시 하나하나를 지시어라고 부릅니다. no-store 는 그중 하나입니다.
no-store 에는 값이 붙지 않습니다. 이름만 적으면 됩니다.
Cache-Control: no-store
이 한 줄이 응답에 실려 있으면 캐시는 그 응답을 남기지 못합니다.
캐시가 할 수 없게 되는 일
캐시는 두 갈래입니다. 한 사람만 쓰는 브라우저 캐시가 있습니다. 여러 사용자가 같이 쓰는 공유 캐시도 있습니다. 앞에서 말한 중간 캐시가 이것입니다. 공유 캐시의 예로는 CDN(Content Delivery Network, 콘텐츠 전송 네트워크)과 리버스 프록시가 있습니다.
no-store 는 이 두 캐시에 똑같이 걸립니다. 캐시는 이 응답의 어느 부분도 반드시 저장하지 말아야 합니다. 저장하지 않았으니 다른 요청에 이 응답을 다시 내줄 수도 없습니다.
아래 그림은 공유 캐시가 no-store 응답을 두 번 받는 모습입니다. 오리진 서버는 응답을 처음 만드는 서버를 말합니다.
sequenceDiagram
participant B as 브라우저
participant S as 공유 캐시
participant O as 오리진 서버
B->>S: 첫 요청
S->>O: 넘긴다
O-->>S: 응답 · no-store
Note over S: 저장하지 않는다
S-->>B: 건네주기만 한다
B->>S: 같은 주소를 다시 요청
Note over S: 남은 것이 없다
S->>O: 다시 넘긴다
O-->>S: 응답 · no-store
S-->>B: 건네주기만 한다
두 번째 요청도 첫 요청과 똑같이 오리진 서버까지 갑니다. 캐시가 중간에 있어도 이 응답에 대해서는 없는 것과 같습니다.
「저장하지 않는다」의 두 뜻
응답을 건네주려면 캐시도 그 응답을 메모리에 잠깐은 올려야 합니다. 그래서 금지는 두 갈래로 나뉩니다.
첫째, 전원이 꺼져도 남는 저장소에 일부러 쓰면 안 됩니다. 디스크가 그런 저장소입니다. 이것은 반드시 지켜야 하는 금지입니다.
둘째, 메모리에 올린 것은 건네준 뒤 되도록 빨리 지워야 합니다. 반드시 지켜야 하는 것은 지우려고 애쓰는 일입니다. 언제까지 지우라는 때는 정하지 않습니다.
없으면 무엇이 곤란한가
캐시는 응답이 누구의 것인지 모릅니다. 같은 주소로 온 요청이면 저장해 둔 응답을 내줄 뿐입니다.
그래서 사람마다 다른 응답이 공유 캐시에 남으면 사고가 납니다. 한 사람의 계좌 화면이 저장되면, 같은 주소를 요청한 다음 사람이 그 화면을 받습니다. 무엇을 남기면 안 되는지는 응답을 만든 서버만 압니다. no-store 는 서버가 그것을 캐시에 알리는 수단입니다.
브라우저 캐시에 남는 것도 문제가 됩니다. 여럿이 쓰는 컴퓨터라면 디스크에 남은 응답을 다음 사용자가 꺼내 볼 수 있습니다.
no-cache · private 과 가르기
이름이 비슷한 지시어가 둘 있습니다. no-cache 는 저장을 막지 않습니다. 대신 꺼내 쓰기 전에 매번 서버에 아직 맞는 응답인지 확인을 받게 합니다. 이 확인을 재검증이라고 합니다.
private 는 이 응답이 한 사용자의 것이라고 알립니다. 공유 캐시는 이 응답을 저장하면 안 됩니다. 브라우저 캐시는 저장해도 됩니다.
세 지시어가 무엇을 허락하는지를 나란히 놓으면 이렇습니다.
| 지시어 | 브라우저 캐시에 저장 | 공유 캐시에 저장 | 같은 요청이 다시 오면 |
|---|---|---|---|
| no-store | ✗ | ✗ | 남은 것이 없어 서버에서 새로 받는다 |
| no-cache | ✓ | ✓ | 서버에 확인받은 뒤 저장한 것을 쓴다 |
| private | ✓ | ✗ | 브라우저가 정해진 유효 기간 동안 저장한 것을 그대로 쓴다 |
저장 자체를 막는 것은 no-store 하나입니다. no-cache 는 이름과 달리 저장을 막지 않습니다. 그래서 둘을 바꿔 쓰면 뜻이 달라집니다.
비용도 다릅니다. no-cache 로 확인을 받을 때 내용이 안 바뀌었으면 서버는 본문 없이 304 Not Modified 라는 짧은 답만 보냅니다. no-store 는 비교할 것이 없어서 매번 본문 전체를 다시 받습니다.
요청에 붙는 no-store
no-store 는 클라이언트가 요청에 붙일 수도 있습니다. 그러면 캐시는 이 요청과 그 응답의 어느 부분도 저장하면 안 됩니다. 저장 금지의 두 뜻도 응답 쪽과 같습니다.
요청 쪽 no-store 는 캐시가 따르지 않아도 됩니다. 이 기능을 넣을지는 캐시를 만드는 쪽이 고릅니다. 저장을 확실히 막아야 하면 응답을 만드는 서버가 붙여야 합니다.
요청의 no-store 는 이미 저장해 둔 응답을 지우지 않습니다. 캐시가 전에 저장한 응답으로 이 요청에 답했다면, 그 저장된 응답은 손대지 않고 남습니다.
막지 못하는 것
no-store 는 캐시에게 하는 부탁이지 강제 장치가 아닙니다. 표준을 지키지 않는 캐시나 공격자에게 넘어간 캐시는 이 지시어를 무시할 수 있습니다.
전송 중에 엿보는 것도 못 막습니다. 응답을 암호화하는 일은 TLS(Transport Layer Security, 전송 계층 보안)가 맡습니다. no-store 만으로는 개인 정보가 지켜진다고 볼 수 없습니다.
must-understand 와 함께 올 때
no-store 는 다른 지시어와 짝을 지어 옛 캐시를 막는 데도 쓰입니다. 옛 캐시가 자기가 모르는 종류의 응답을 잘못 저장하지 않게 하는 것입니다.
응답에는 상태 코드가 붙습니다. 상태 코드는 응답의 결과를 알리는 세 자리 숫자입니다. 200 이나 404 가 그런 값입니다. 캐시가 이 응답을 저장해도 되는지와 어떻게 다룰지는 상태 코드마다 따로 정해져 있습니다.
must-understand 는 그 상태 코드의 저장 규칙을 아는 캐시만 이 응답을 저장하게 하는 지시어입니다. must-understand 를 쓰는 응답에는 no-store 도 함께 싣기를 권합니다.
두 지시어가 같이 오면 캐시마다 다르게 읽습니다. must-understand 를 알고 그 상태 코드의 규칙도 따르는 캐시는 no-store 를 무시해도 됩니다. must-understand 를 모르는 옛 캐시는 no-store 를 보고 저장하지 않습니다. 그래서 옛 캐시가 모르는 상태 코드의 응답은 남지 않고 지나갑니다.
언제 쓰고 언제 안 쓰나
사람마다 다르고 남에게 보이면 안 되는 응답에 씁니다. 계좌·결제 화면, 개인 정보를 담은 응답이 그렇습니다.
권한을 담은 값을 내주는 응답도 그렇습니다. OAuth 2.0 은 다른 서비스에 내 권한을 빌려주는 표준입니다.
액세스 토큰은 그 권한을 담아 건네는 문자열입니다. 이 문자열을 가진 쪽은 그 권한으로 다른 서비스를 부를 수 있습니다. 그래서 OAuth 2.0 에서 액세스 토큰을 내주는 응답에는 no-store 를 반드시 붙여야 합니다.
모두에게 같은 이미지나 스크립트 파일에는 쓰지 않습니다. 저장해 두고 다시 써야 빨라지는 응답이기 때문입니다.
「바뀌면 바로 새 것을 보여 주고 싶다」는 이유라면 no-cache 가 맞습니다. no-cache 를 붙인 응답은 저장됩니다. 대신 쓸 때마다 서버에 확인을 받습니다. 바뀌었으면 새 내용을 바로 받습니다. 안 바뀌었으면 본문을 다시 받지 않습니다.
관련 항목
이 지시어를 정의하는 표준 문서
RFC 9111 · RFC 9110 · RFC 7234 · RFC 2119 · HTTP · OAuth 2.0
이 지시어를 싣는 헤더와 필드
Cache-Control · 헤더 · 필드 · 지시어 · Pragma
함께 실리는 다른 Cache-Control 지시어
no-cache · private · public · max-age · s-maxage · must-revalidate · must-understand · no-transform · immutable
이 표시를 읽고 저장을 멈추는 캐시
캐시 · HTTP 캐시 · 브라우저 캐시 · 공유 캐시 · CDN · 리버스 프록시 · 오리진 서버
no-store 가 끄는 캐시 동작
캐싱 · 재검증 · 조건부 요청 · 304 Not Modified · ETag · 신선도
이 지시어로 감추는 민감한 응답
이 지시어가 대신하지 못하는 보호 수단
다른 이름: no store · Cache-Control no-store · 노 스토어