Cache-Control 생성기
리소스 유형에 맞는 Cache-Control 헤더를 생성합니다.
Cache-Control 헤더는 브라우저와 CDN이 응답을 얼마나, 어떤 조건으로 캐싱할지 결정하는 가장 중요한 HTTP 헤더입니다. 잘못 설정하면 사용자에게 오래된 콘텐츠가 노출되거나, 반대로 캐시가 전혀 동작하지 않아 서버·대역폭 비용이 치솟습니다. 이 도구는 리소스 유형을 고르고 디렉티브를 토글하기만 하면 올바른Cache-Control 문자열을 실시간으로 조립해 줍니다.
정적 해시 자산, HTML 문서, 민감한 API 응답, 이미지처럼 자주 쓰이는 시나리오는 프리셋 버튼 한 번으로 적용할 수 있고, 필요하면 max-age·s-maxage·stale-while-revalidate 값을 직접 조정해 미세 튜닝할 수 있습니다. 모든 계산은 브라우저 안에서만 이뤄지며 입력값이 외부로 전송되지 않습니다.
Cache-Control: public, max-age=31536000, immutable
주요 디렉티브의 의미
Cache-Control은 쉼표로 구분된 디렉티브 목록입니다. 자주 쓰는 항목은 다음과 같습니다.
- public / private:
public은 CDN 등 공유 캐시에 저장을 허용하고,private은 최종 사용자 브라우저에만 저장하도록 제한합니다(로그인 사용자별 응답에 사용). - max-age=N: 응답이 신선하다고 간주되는 시간(초). 이 시간 동안은 서버에 다시 묻지 않습니다.
- s-maxage=N: 공유 캐시(CDN)에만 적용되는 신선도.
max-age를 덮어씁니다. - no-cache: 캐시는 하되, 사용 전 항상 서버에 재검증(304 확인)을 요청합니다.
- no-store: 어떤 캐시에도 저장하지 않습니다. 민감 데이터에 사용합니다.
- must-revalidate: 신선도가 만료되면 반드시 재검증해야 하며, 오프라인 시 오래된 응답 제공을 금지합니다.
- immutable: 신선한 동안에는 사용자가 새로고침해도 재검증 요청을 보내지 않습니다.
- stale-while-revalidate=N: 만료 후 N초 동안은 오래된 응답을 즉시 제공하면서 백그라운드에서 갱신합니다.
해시 파일은 immutable + 1년 캐시
빌드 도구가 파일명에 콘텐츠 해시를 붙인 자산(예: app.4f3a9c.js)은 내용이 바뀌면 파일명 자체가 바뀝니다. 따라서 같은 URL의 내용은 절대 변하지 않으므로 최대한 길게 캐시해도 안전합니다. 권장값은public, max-age=31536000, immutable(1년)입니다. immutable을 붙이면 사용자가 새로고침을 눌러도 브라우저가 조건부 요청을 보내지 않아 왕복이 완전히 사라집니다.
- 해시가 없는 HTML 진입점:
no-cache로 항상 최신 버전을 받게 합니다. - 해시가 붙은 JS/CSS/폰트:
public, max-age=31536000, immutable. - 이미지/미디어: 자주 안 바뀌면
public, max-age=86400(1일) 정도로 시작합니다. - 개인화·민감 응답:
no-store, private.
흔한 충돌과 주의점
no-store는 캐싱 자체를 금지하므로 max-age나 immutable과 함께 쓰면 모순입니다. 이 도구는 그런 조합을 감지하면 경고를 표시합니다. 또한 no-cache는 “캐시 금지”가 아니라 “매번 재검증”이라는 점을 혼동하지 마세요. 캐시를 완전히 막으려면 no-store를 써야 합니다.stale-while-revalidate 기간을 데이터 변경 빈도에 맞춰 정하려면 stale-while-revalidate 설계기가 도움이 됩니다.
리소스 유형별 권장 Cache-Control 표
실무에서 바로 복사해 쓸 수 있는 조합을 리소스 유형별로 정리했습니다. max-age 값은 모두 초 단위입니다.
| 리소스 유형 | 권장값 | 이유 |
|---|---|---|
| 해시된 JS/CSS/폰트 | public, max-age=31536000, immutable | URL이 내용 변경 시 바뀌므로 영구 캐시 안전 |
| HTML 진입점 | no-cache | 새 배포 즉시 반영, 단 ETag로 304 절약 |
| 자주 안 바뀌는 이미지 | public, max-age=86400, stale-while-revalidate=604800 | 하루 신선, 만료 후 일주일은 즉시 제공+백그라운드 갱신 |
| 공개 API(JSON) | public, max-age=0, s-maxage=60 | 브라우저는 매번 확인, CDN만 60초 캐시 |
| 로그인 사용자 응답 | private, no-cache | CDN 저장 금지, 브라우저는 재검증 후 재사용 |
| 비밀번호·결제·토큰 | no-store | 어디에도 저장 금지(뒤로가기 캐시 포함) |
worked example: public API 응답 해석
예를 들어 응답 헤더가 Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=30이라면 실제 동작은 이렇게 갈립니다. 브라우저(개인 캐시)는 max-age=0이므로 매 요청마다 신선하지 않다고 보고 조건부 요청을 보냅니다. 반면 CDN(공유 캐시)은 s-maxage=60이 max-age를 덮어쓰므로 60초간은 원본 서버에 묻지 않고 그대로 응답합니다. 60초가 지나면 stale-while-revalidate=30 덕분에 이후 30초 동안은 오래된 응답을 즉시 돌려주면서 뒤에서 갱신을 시도합니다. 결국 원본 서버는 최악의 경우에도 60초에 한 번만 호출되어 트래픽 폭주에도 안정적입니다.
흔한 실수 / 함정
- max-age=0 ≠ no-store:
max-age=0은 “바로 만료, 하지만 저장은 허용(재검증 후 재사용 가능)”입니다. 캐시 자체를 막으려면 반드시no-store를 써야 합니다. - private + s-maxage 조합은 무의미:
s-maxage는 공유 캐시 전용 디렉티브인데private이 공유 캐시 저장을 이미 금지하므로 함께 쓰면s-maxage는 그냥 무시됩니다.
자주 묻는 질문
no-cache와 no-store의 차이가 뭔가요?
max-age는 어느 단위인가요?
immutable을 붙이면 배포 후 변경이 반영 안 되나요?
s-maxage와 max-age를 모두 설정하면 어떻게 되나요?
입력한 값이 서버로 전송되나요?
관련 가이드
- Cache-Control 완전 정리: 브라우저 캐시 제대로 쓰기max-age·no-cache·no-store·immutable의 진짜 의미와 리소스 유형별 권장 캐시 정책.