mirim-app/deploy/Caddyfile
AWESOMEDEV 9ae3cf2709 fix(cache): 문서 iframe ?v= 캐시버스터 + SPA 표준 캐시 정책
옛 X-Frame-Options:DENY 응답이 브라우저 캐시에 박혀 서버 수정 후에도 계속
'연결 거부'가 나던 문제의 근본 대응:
- DocViewerPage iframe src에 ?v=2 — 캐시 키를 바꿔 새 응답을 받게(캐시 우회)
- Caddy 캐시 정책 정리: /assets/*(해시파일) 영구 immutable, 그 외 HTML·문서 no-cache
  → 앞으로 헤더/내용 변경이 캐시에 눌어붙지 않고 즉시 반영

검증: 문서=no-cache+SAMEORIGIN, index=no-cache, assets=immutable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:38:15 +09:00

61 lines
3.3 KiB
Caddyfile

# Caddy 설정 — 웹서버 + 리버스 프록시 + 자동 HTTPS
# 학습 포인트: 운영에서 사용자는 Caddy(80/443)만 만나요.
# - 정적 파일(React 빌드 결과)은 Caddy가 직접 서빙
# - /api/* 요청은 백엔드 컨테이너(backend:8080)로 넘김 — 개발 때 vite 프록시와 같은 역할
# - 도메인이 연결되면 HTTPS 인증서를 자동으로 받아요 (Let's Encrypt)
# ── 공통 보안 응답 헤더 (snippet) ──
# 학습 포인트: 브라우저는 서버가 주는 헤더를 보고 "이 사이트를 어떻게 다룰지" 정한다.
# 아래 헤더 몇 줄이 흔한 공격을 브라우저 단에서 막아 준다. 학생 세션쿠키·개인정보를
# 다루는 사이트라 특히 중요하다. (import security 로 각 사이트 블록에서 끌어 쓴다)
(security) {
header {
# HTTPS만 쓰라고 브라우저에 각인 (1년) — 평문 접속·다운그레이드 공격 차단
Strict-Transport-Security "max-age=31536000; includeSubDomains"
# 다른 사이트가 우리 화면을 iframe에 넣는 것 금지 → 클릭재킹 방어.
# ⚠️ DENY(전면 금지)가 아니라 SAMEORIGIN(같은 사이트끼리는 허용)이어야 한다 —
# 학습 문서 뷰어(DocViewerPage)가 우리 문서를 우리 iframe으로 띄우기 때문이다.
# DENY로 두면 우리 문서조차 "연결 거부"로 안 뜬다(실제로 그 버그가 났었다).
X-Frame-Options "SAMEORIGIN"
# 브라우저가 파일 종류를 멋대로 추측(MIME 스니핑)하지 못하게
X-Content-Type-Options "nosniff"
# 다른 사이트로 이동할 때 우리 주소(경로)를 흘리지 않음
Referrer-Policy "no-referrer"
# 서버 종류를 굳이 알려주지 않음
-Server
}
}
# 도메인 접속 (DNS 연결 후 자동 HTTPS)
edu.awesomedevapp.com {
import security
encode gzip
handle /api/* {
reverse_proxy backend:8080
}
# ── 캐시 정책 (SPA 표준) ──
# 학습 포인트: 무엇을 오래 캐시하고 무엇을 매번 확인할지 나눈다.
# 1) /assets/* — 빌드가 파일명에 해시를 박는다(index-A1B2.js). 내용이 바뀌면 이름이
# 바뀌므로, 이 파일들은 "영원히" 캐시해도 안전하다(immutable) → 빠르다.
# 2) 그 외(HTML·학습 문서) — 항상 서버에 재검증(no-cache). 그래야 헤더나 내용이 바뀔 때
# 즉시 반영된다. 예전에 잘못된 보안헤더가 문서 캐시에 눌어붙어 "연결 거부"가 났던
# 그 문제를 원천 차단한다. (HTML은 작아 재검증 비용도 작다.)
@assets path /assets/*
header @assets Cache-Control "public, max-age=31536000, immutable"
@nocache not path /assets/*
header @nocache Cache-Control "no-cache"
handle {
root * /srv
try_files {path} /index.html # 새로고침해도 React 라우터가 동작하게 하는 설정
file_server
}
}
# 학습 포인트: 예전엔 여기에 `:80 { }`(모든 HTTP를 평문으로 서빙) 블록이 있었다.
# 그런데 그게 Caddy의 자동 HTTP→HTTPS 리다이렉트를 "가로채" 평문 접속을 열어 뒀다.
# 이 블록을 지우면 Caddy가 edu.awesomedevapp.com의 80포트를 자동으로 443으로 돌려보낸다
# → 평문으로 로그인 정보가 오갈 표면 자체가 사라진다. (DNS가 붙었으니 IP 평문 접속은 불필요.)