CDN, Cache, 그리고 Edge Delivery에서는 사용자 가까이에 복사본을 두는 큰 그림을 봤어요. 그리고 Cache-Control과 Age 헤더에서는 사본이 아직 fresh인지, Cache Key와 Vary에서는 같은 URL처럼 보여도 사본이 왜 갈라질 수 있는지 읽었죠. 이번에는 브라우저 Network 탭이나HIT이면 무조건 빠르고,MISS면 무조건 문제가 있는 걸까요? 사실은 그 한 단어만으로는 캐시가 왜 그렇게 판단했는지까지 알 수 없어요.
curl -I에서 자주 보이는 이런 줄을 볼게요.
cf-cache-status: HIT만 눈에 들어와요.
“아, CDN 캐시에서 나온 거구나.”맞아요. 그런데 여기서 끝내면 아직 반쪽이에요.
HIT인지 MISS인지, Age가 있는지, Cache-Control이 무엇인지, Vary가 어떤 요청 헤더를 비교하는지까지 같이 봐야 왜 이 요청이 캐시에서 끝났는지를 좁힐 수 있어요.
오늘 질문은 이거예요.
“이 응답은 캐시에 있었나요, 왜 없었나요, 있어도 그대로 써도 되는 사본이었나요?”HTTP 캐시의 기본 판단은 RFC 9111: HTTP Caching을 바닥에 두고, 캐시가 자신이 한 일을 설명하는 표준 헤더로는 RFC 9211: Cache-Status가 있어요. 반면
CF-Cache-Status, X-Cache, X-Cache-Status는 표준 Cache-Status의 다른 이름이 아니라 제품별 헤더예요. 실제 운영 화면에서는 이런 벤더 헤더를 더 자주 볼 수 있으므로, 예를 들어 Cloudflare의 HIT, MISS, BYPASS, DYNAMIC, STALE, REVALIDATED, UPDATING은 Cloudflare 문서를 기준으로 읽어야 해요.
여기서는 특정 CDN 설정 화면을 외우기보다, CDN 캐시 상태 헤더를 다른 HTTP 캐시 신호와 함께 읽는 순서를 잡아요. 상태 이름은 제품마다 다를 수 있으니, 실제 장애 분석에서는 해당 CDN 문서를 같이 확인해야 해요.
먼저 여섯 가지 신호를 한 화면에서 봐요
HIT이나 MISS는 택배의 처리 도장처럼 이번 요청의 결과를 보여줘요. 원인을 찾으려면 요청과 응답을 한 화면에 놓고 아래 순서로 읽는 편이 빨라요.
이 순서가 중요한 이유는,
MISS가 항상 “캐시가 안 된다”는 뜻이 아니기 때문이에요. 첫 요청이라서 MISS일 수 있고, key가 달라서 MISS일 수 있고, stale 사본을 재검사하느라 오리진에 갔을 수도 있어요. 다만 상태 이름을 붙이는 기준은 제품과 시기에 따라 다르므로, 실제 CDN 문서를 함께 봐야 해요.
상태 헤더 이름은 제품마다 달라요
브라우저나curl에서 볼 수 있는 캐시 상태 헤더는 한 가지가 아니에요.
Cache-Status는 RFC 9211에 정의된 표준 헤더예요. 반면 CF-Cache-Status나 X-Cache 계열은 제품이나 배포 환경에서 오래 써온 관측용 헤더라서, 이름이나 값이 비슷해도 표준 Cache-Status 문법과 의미를 그대로 적용하면 안 돼요.
처음 보는 헤더라면 표준인지, CDN 제품이 붙였는지, 중간 프록시가 만든 값인지부터 확인해요. 그래야
HIT, MISS, BYPASS를 다른 제품에 그대로 일반화하지 않게 돼요.
HIT은 “저장된 사본을 썼다”에 가까워요
가장 읽기 쉬운 장면부터 볼게요.
여기서는
Age가 크다고 해서 바로 오래된 나쁜 응답이라고 보면 안 돼요. 해시가 붙은 정적 파일이고 max-age가 1년이라면, 하루 된 사본은 여전히 fresh일 수 있어요.
이 그림은 HIT이 “오리진을 매번 가지 않았다”는 뜻에 가깝다는 걸 보여줘요. 하지만 HIT이 항상 올바른 응답이라는 보장은 아니에요. cache key나 Vary가 잘못됐으면 잘못된 사본도 HIT으로 나갈 수 있어요.
MISS는 “이번 요청에는 바로 쓸 사본이 없었다”에 가까워요
이번에는 첫 요청 장면이에요.HIT이 될 수도 있죠.
MISS를 봤다면 아래를 같이 확인해요.
BYPASS와 DYNAMIC은 “캐시를 쓰지 않기로 했다”에 가까워요
이번에는 응답이 캐시에 저장되기 어려운 장면이에요.
여기서 중요한 반전이 있어요.
“캐시가 안 됐으니 나쁜 설정이다”항상 그렇지는 않아요. 로그인 페이지, 장바구니, 결제 응답, 개인화 API는 공유 캐시에 저장되지 않는 편이 맞을 수 있어요. 캐시 상태 헤더는 성능 점수표가 아니라 정책 판단의 흔적이에요.
EXPIRED, STALE, REVALIDATED, UPDATING은 “오래된 사본을 어떻게 처리했나”를 봐야 해요
캐시에는 사본이 있었지만 freshness lifetime이 끝난 장면도 있어요.304 Not Modified로 “그 사본 그대로 써도 돼요”라고 확인해주면, 캐시는 본문을 다시 받지 않고 기존 사본을 내보낼 수 있어요.
반대로 STALE이나 UPDATING류의 값은 stale 사본을 어떻게 다뤘는지를 봐야 해요. 어떤 CDN은 오리진이 잠시 닿지 않을 때 오래된 사본을 대신 줄 수 있고, 어떤 설정에서는 stale 사본을 주면서 백그라운드로 갱신할 수 있어요. 이 영역은 제품과 설정 차이가 크기 때문에, 상태값 이름만 외우지 말고 아래 질문을 같이 남겨야 해요.
같은 HIT이어도 캐시 키가 틀리면 위험해요
HIT이 나왔는데 사용자에게 엉뚱한 언어가 보인다고 해볼게요.
HIT이 아니라 무엇을 같은 사본으로 봤는지일 수 있어요. 언어별로 본문이 달라진다면 Vary: Accept-Language나 CDN의 커스텀 cache key가 그 차이를 반영해야 해요.
이런 장면에서는 캐시 상태 헤더만 보면 오히려 속을 수 있어요. HIT은 캐시가 사본을 찾았다는 뜻이지, 그 사본이 이 사용자에게 맞는지까지 대신 보증하지 않아요.
브라우저 캐시와 CDN 캐시를 섞어 읽지 마세요
Network 탭에는 이런 표시도 나와요.(memory cache)나 (disk cache)는 브라우저 쪽 캐시 표시예요. CDN의 CF-Cache-Status: HIT과는 관측 위치가 달라요.
그래서 “내 브라우저에서는 빠른데 다른 사용자는 느리다” 같은 문제에서는 브라우저 캐시를 끄거나 hard reload,
curl -H 'Cache-Control: no-cache', 다른 지역/네트워크 테스트처럼 관측 조건을 나눠야 해요. 단, 요청에 Cache-Control: no-cache를 붙이면 캐시 동작 자체가 달라질 수 있으니, 평소 사용자 요청과 디버깅 요청을 구분해서 봐야 해요.
잘못 읽기 쉬운 함정
HIT이면 항상 최신이라고 보기
HIT은 캐시에서 나왔다는 뜻에 가까워요. 최신인지 아닌지는 Cache-Control, Age, 재검사 정책, purge 여부를 같이 봐야 해요.
MISS이면 캐시 설정이 실패했다고 보기
첫 요청, 새 배포 파일, 새 query, 새 언어 조건, 새 엣지 위치에서는 MISS가 자연스러울 수 있어요. 같은 조건의 두 번째 요청도 계속 MISS인지 봐야 해요.
Age가 없으면 CDN을 안 탔다고 단정하기
제품별로 Age를 붙이는 조건이 다를 수 있어요. 예를 들어 어떤 CDN은 캐시에서 실제로 제공한 응답에만 Age를 붙이고, MISS나 동적 응답에는 붙이지 않을 수 있어요. Age 없음 하나로 경로 전체를 단정하지 마세요.
벤더 상태값을 모든 CDN에 똑같이 적용하기
BYPASS, DYNAMIC, UPDATING 같은 이름은 제품별 의미와 조건이 달라요. 글에서는 읽기 감각을 잡지만, 실제 운영에서는 해당 CDN 문서와 설정을 같이 봐야 해요.
HIT만 높이면 무조건 좋다고 보기
히트율은 중요하지만, 사용자별 응답까지 공유 캐시에 섞이면 더 큰 문제가 돼요. 캐시는 빠른 것도 중요하지만, 맞는 사본을 맞는 사용자에게 주는 것이 먼저예요.
장애나 이상 증상을 만나면 이렇게 좁혀봐요
실전에서는 아래 질문을 남기면 좋아요.자, 정리해볼까요?
CDN 캐시 상태 헤더는 캐시가 이번 요청을 어떻게 처리했는지 보여주는 관측 신호예요.
Cache-Status는 표준 헤더이고, CF-Cache-Status, X-Cache 같은 헤더는 제품별 의미를 문서로 확인해야 해요.HIT은 저장된 사본을 썼다는 뜻에 가깝지만, 그 사본이 최신인지 또는 이 요청에 맞는지는 별도로 봐야 해요.현재 Cloudflare에서 MISS는 캐시할 수 있지만 로컬 캐시에 없던 응답, BYPASS는 캐시할 수 없는 응답을 뜻해요.과거 Cloudflare 배포나 다른 CDN에서는 MISS, BYPASS, DYNAMIC, STALE, REVALIDATED, UPDATING 같은 값의 기준이 다를 수 있으므로 해당 시점과 제품의 문서를 함께 읽어야 해요.캐시 상태 헤더는 Cache-Control, Age, Vary, 요청 헤더, 쿠키, 인증 조건과 같이 봐야 이유가 보여요.브라우저 캐시와 CDN 캐시는 관측 위치가 다르므로 (memory cache) 표시와 CDN 상태 헤더를 섞어 읽으면 안 돼요.HIT인지 MISS인지 본 다음, “왜 그렇게 됐을까?”를 주변 헤더와 요청 조건으로 좁혀가는 게 핵심이에요.
이어서 보면 좋은 글
- CDN, Cache, 그리고 Edge Delivery — 캐시 히트와 미스, 오리진과 엣지의 큰 그림으로 돌아가고 싶을 때 좋아요.
- Cache-Control과 Age 헤더는 어떻게 같이 읽어야 할까요? — 캐시 사본의 freshness와 나이를 먼저 정리하고 싶을 때 좋아요.
- Cache Key와 Vary는 왜 같이 읽어야 할까요? —
HIT인데도 사본이 이상하게 섞이는 이유를 더 깊게 볼 수 있어요. - ETag와 조건부 요청은 어떻게 304를 만들까요? —
REVALIDATED나304 Not Modified가 왜 보이는지 이어서 읽어볼 수 있어요.