Caddy 란?
Caddy는 Go로 작성된 웹서버이자 리버스 프록시로, 가장 큰 특징은 자동 HTTPS입니다. 도메인을 명시하면 Caddy가 ACME 프로토콜(Let's Encrypt / ZeroSSL)로 인증서를 자동 발급·갱신하고, HTTP를 HTTPS로 리다이렉트하며, OCSP Stapling까지 알아서 처리합니다. Nginx나 Apache처럼 인증서 발급 도구(certbot)를 따로 운영하거나 cron 갱신을 신경 쓸 필요가 없습니다.
설정 파일인 Caddyfile은 선언적이고 간결합니다. 수십 줄짜리 Nginx server 블록이 Caddy에서는 몇 줄로 끝나는 경우가 많습니다. 이 가이드에서는 Caddy 2.x 기준으로 설치, Caddyfile 문법, 리버스 프록시, TLS 옵션, 그리고 무중단 재로딩을 위한 Admin API까지 다룹니다.
Caddy는 인증서 발급을 위해 도메인이 실제로 해당 서버를 가리키고(A/AAAA 레코드), 80/443 포트가 외부에서 접근 가능해야 합니다. ACME HTTP-01 챌린지는 80 포트로 들어옵니다.
설치
# Debian/Ubuntu 공식 저장소
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
# 버전 확인
caddy versionapt로 설치하면 systemd 서비스(caddy.service)와 전용 사용자 caddy가 함께 생성되며, 설정 파일 경로는 /etc/caddy/Caddyfile입니다.
sudo systemctl enable --now caddy
systemctl status caddyCaddyfile 기본 문법
가장 단순한 정적 사이트:
example.com {
root * /var/www/example.com
file_server
encode gzip zstd
}이 세 줄로 example.com에 대해 인증서가 자동 발급되고, HTTP→HTTPS 리다이렉트가 걸리며, 정적 파일이 서빙됩니다. 블록 첫 줄의 사이트 주소가 곧 인증서 발급 대상입니다.
여러 도메인을 한 블록에 묶을 수 있습니다.
example.com, www.example.com {
redir https://example.com{uri} permanent
}{uri}, {host}, {remote_host} 같은 플레이스홀더를 쓸 수 있습니다(템플릿 변수). 와일드카드 인증서를 쓰려면 DNS-01 챌린지가 필요하며, 이는 DNS 제공자 플러그인을 빌드해 넣어야 합니다.
리버스 프록시
Caddy의 핵심 용도 중 하나입니다. 백엔드 애플리케이션 앞단에 두면 됩니다.
app.example.com {
reverse_proxy localhost:3000
}이것만으로 TLS 종료(termination) + 프록시가 완성됩니다. 업스트림이 여러 개면 자동 로드밸런싱이 적용됩니다.
api.example.com {
reverse_proxy {
to localhost:8001 localhost:8002 localhost:8003
lb_policy round_robin # least_conn, ip_hash 등
health_uri /healthz
health_interval 10s
health_timeout 3s
}
}경로별로 다른 백엔드로 분기하려면 matcher를 씁니다.
example.com {
# /api/* 는 백엔드로
reverse_proxy /api/* localhost:8080
# 나머지는 정적 파일
handle {
root * /var/www/spa
try_files {path} /index.html # SPA fallback
file_server
}
}업스트림으로 헤더 전달
Caddy는 기본적으로 X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host를 자동으로 붙입니다. 추가 헤더는 다음과 같이 조작합니다.
reverse_proxy localhost:3000 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_down -Server # 응답에서 Server 헤더 제거
}TLS 세부 옵션
자동 HTTPS를 그대로 두는 것이 권장이지만, 운영 요구에 따라 조정할 수 있습니다.
example.com {
tls [email protected] { # ACME 계정 이메일
protocols tls1.2 tls1.3
}
reverse_proxy localhost:3000
}내부망/테스트에서 사설 인증서를 쓰거나 인증서 발급을 끄려면:
# 직접 발급한 인증서 사용
tls /etc/ssl/site.crt /etc/ssl/site.key
# 내부 CA로 자체 서명 (개발용)
tls internal발급 한도 테스트 중에는 Let's Encrypt 운영 환경 대신 스테이징을 쓰세요. 전역 옵션 블록에서 acme_ca https://acme-staging-v02.api.letsencrypt.org/directory를 지정하면 rate limit 소진을 피할 수 있습니다.
전역 옵션과 로깅
파일 맨 위 중괄호 블록이 전역 옵션입니다.
{
email [email protected]
admin localhost:2019 # Admin API 엔드포인트
log {
output file /var/log/caddy/access.log
format json
}
}
example.com {
reverse_proxy localhost:3000
log {
output file /var/log/caddy/example.access.log {
roll_size 50mb
roll_keep 10
}
format console
}
}JSON 로그는 jq로 바로 파싱할 수 있어 관측성 파이프라인에 유리합니다.
설정 검증과 무중단 재로딩
# 문법 검증 및 포맷팅
caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
# 무중단 리로드 (graceful, 연결 끊김 없음)
sudo systemctl reload caddy
# 또는
caddy reload --config /etc/caddy/Caddyfilereload는 프로세스를 죽이지 않고 새 설정을 적용하므로 진행 중인 요청이 끊기지 않습니다. 이 점이 운영 환경에서 큰 장점입니다.
Admin API
Caddy는 기본적으로 localhost:2019에서 REST API를 노출합니다. 설정을 JSON으로 조회·교체할 수 있습니다.
# 현재 동작 중인 전체 설정 확인
curl -s localhost:2019/config/ | jq .
# 업스트림 헬스 상태 확인
curl -s localhost:2019/reverse_proxy/upstreams | jq .
# 설정 일부를 동적으로 교체 (PATCH)
curl -X POST localhost:2019/load \
-H "Content-Type: application/json" \
-d @new-config.jsonAdmin API는 기본적으로 로컬에만 바인딩되지만 인증이 없습니다. 절대 외부에 노출하지 마세요. 컨테이너 환경에서 0.0.0.0에 바인딩하지 않도록 주의합니다.
트러블슈팅
| 증상 | 원인 / 확인 |
|---|---|
| 인증서 발급 실패 | 80 포트 방화벽 차단, A 레코드 미설정, rate limit |
journalctl -u caddy 에 ACME 에러 | DNS 전파 미완료 — dig example.com 으로 확인 |
| 502 Bad Gateway | 업스트림 미기동, reverse_proxy 포트 오타 |
| 설정 반영 안 됨 | caddy validate 통과 후 reload 했는지 확인 |
| TLS handshake 에러 | protocols 설정과 클라이언트 호환성 확인 |
로그는 항상 systemd 저널에서 확인합니다.
journalctl -u caddy -f --no-pager정리
| 항목 | 핵심 |
|---|---|
| 자동 HTTPS | 도메인만 명시하면 ACME로 인증서 자동 발급·갱신 |
| Caddyfile | 선언적·간결, 사이트 주소가 인증서 대상 |
| 리버스 프록시 | reverse_proxy 한 줄, 다중 업스트림 시 LB·헬스체크 |
| matcher | 경로·헤더별 분기, SPA는 try_files |
| 무중단 리로드 | systemctl reload caddy / caddy reload |
| Admin API | localhost:2019, 외부 노출 금지 |
Caddy는 "설정이 적을수록 좋다"는 철학의 웹서버입니다. 자동 HTTPS와 간결한 프록시 문법 덕분에 소규모~중규모 서비스의 엣지 게이트웨이로 빠르게 올릴 수 있습니다.
이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.
질문 & 답변 (Q&A)
이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.