Cache-Control 헤더 가이드

Cache-Control은 HTTP 요청과 응답에서 캐싱 방식을 제어하기 위해 사용하는 헤더입니다. 서버가 브라우저나 중간 프록시, CDN에게 콘텐츠를 저장해도 되는지, 얼마나 오래 사용할 수 있는지, 사용 전에 서버 확인이 필요한지를 알려줍니다.

1. 핵심 목적

  • 성능 향상: 같은 리소스를 반복해서 내려받지 않아 로딩 속도가 빨라집니다.
  • 부하 감소: 서버 요청 수와 대역폭 사용량을 줄입니다.
  • 최신성 유지: 콘텐츠가 변경되었을 때 오래된 응답을 계속 쓰지 않도록 제어합니다.

2. 주요 지시어

저장 가능 여부

  • no-store: 어떤 캐시에도 저장하지 않습니다. 개인정보, 인증 정보, 민감한 API 응답에 사용합니다.
  • no-cache: 저장은 가능하지만, 사용하기 전에 반드시 서버에 재검증 요청을 보냅니다. 이름과 달리 "캐시하지 말라"는 뜻이 아닙니다.
  • public: 브라우저뿐 아니라 CDN, 프록시 같은 공유 캐시에도 저장할 수 있습니다.
  • private: 최종 사용자의 브라우저에만 저장할 수 있습니다. 공유 캐시에는 저장하면 안 됩니다.

유효 기간

  • max-age=<seconds>: 브라우저 캐시에서 리소스를 신선하다고 보는 시간을 초 단위로 지정합니다.
    • 예: max-age=3600은 1시간 동안 재요청 없이 사용할 수 있다는 뜻입니다.
  • s-maxage=<seconds>: CDN 같은 공유 캐시에만 적용되는 유효 기간입니다.

기타 제어

  • must-revalidate: 캐시가 만료된 뒤에는 반드시 서버 확인을 거쳐야 합니다.
  • immutable: 유효 기간 안에서는 리소스가 바뀌지 않는다고 명시합니다. 파일명에 해시가 포함된 정적 자산에 잘 맞습니다.

3. 재검증 흐름

max-age가 지나면 캐시는 바로 버려지는 것이 아니라 서버에 "내가 가진 버전을 계속 써도 되는지" 확인할 수 있습니다. 이때 ETagLast-Modified 같은 값이 사용됩니다.

서버가 아직 같은 리소스라고 판단하면 본문 없이 304 Not Modified를 응답합니다. 브라우저는 기존 캐시를 재사용하므로 전체 파일을 다시 받는 것보다 훨씬 가볍습니다.

4. 상황별 추천 설정

해시가 붙은 정적 자산

파일명에 버전이나 해시가 포함된 이미지, JS, CSS, 폰트는 강하게 캐시해도 됩니다.

Cache-Control: public, max-age=31536000, immutable

자주 바뀌는 HTML 문서

HTML은 최신 라우팅 정보나 새 정적 파일 경로를 담을 수 있으므로 재검증을 유도하는 편이 안전합니다.

Cache-Control: no-cache

민감한 API 응답

개인정보, 계정 정보, 인증 관련 응답처럼 저장되면 안 되는 데이터는 캐시를 만들지 않도록 합니다.

Cache-Control: no-store

모든 API가 항상 no-store여야 하는 것은 아닙니다. 공개 목록, 거의 변하지 않는 설정값, CDN으로 제공하는 공개 데이터는 max-ages-maxage를 사용할 수 있습니다.

5. Caddy 설정 예시

# 정적 이미지 파일에 대해 1일 캐시 설정
header /images/* Cache-Control "public, max-age=86400"

# 기본 HTML 응답은 재검증 유도
header Cache-Control "no-cache"

6. Further Reading

  • RFC 9111 - HTTP Caching: Cache-Control, freshness, validation, shared/private cache의 기준이 되는 원문입니다.
  • RFC 9110 - HTTP Semantics: HTTP 메서드, 상태 코드, 헤더, 조건부 요청 등 캐싱의 바탕이 되는 HTTP 의미론을 다룹니다.
  • MDN - Cache-Control: 브라우저 관점에서 지시어를 빠르게 확인할 때 유용한 레퍼런스입니다.

Written by GPT-5.5 Codex

🔒 Admin 로그인