Caddy로 자동 HTTPS 웹서버 구축하기
HTTPS 인증서 자동 발급·갱신을 위해 certbot 크론을 걸고, Nginx 설정을 복사하며 씨름한 경험이 있다면 Caddy를 한 번 써볼 가치가 있습니다. Caddy는 ACME 클라이언트가 서버에 내장돼 있어, 도메인만 지정하면 TLS 인증서 발급부터 자동 갱신까지 모두 처리합니다. 이 글에서는 Caddy 설치, Caddyfile 문법, 리버스 프록시 구성, 그리고 실무에서 자주 마주치는 설정 패턴을 정리합니다.
Caddy가 해결하는 문제
Nginx를 기준으로 보면, HTTPS를 갖춘 단순 리버스 프록시 하나를 띄우기 위해 보통 다음이 필요합니다.
certbot으로 초기 인증서 발급- Nginx
server블록(80/443) 각각 작성 ssl_certificate,ssl_certificate_key, OCSP stapling, HSTS 등 수십 줄certbot renew크론 및 hook
Caddy는 이 모든 것을 기본값으로 끌어안습니다. 별도 모듈 없이도 현대적 TLS 기본값(HTTP/2, OCSP, HSTS, 자동 리다이렉트)이 켜진 상태로 시작합니다.
설치
Rocky Linux 9
# 공식 저장소 추가
dnf install -y 'dnf-command(copr)'
dnf copr enable -y @caddy/caddy
dnf install -y caddy
systemctl enable --now caddy
Ubuntu 22.04 / 24.04
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 -y caddy
Caddyfile 기본 문법
기본 설정 파일 위치는 /etc/caddy/Caddyfile입니다. 가장 단순한 정적 사이트는 두 줄이면 끝입니다.
example.com {
root * /var/www/example.com
file_server
}
위 설정으로 Caddy는 자동으로 다음을 수행합니다.
example.com에 대한 Let’s Encrypt 인증서 발급- HTTP(80) → HTTPS(443) 자동 리다이렉트
- HTTP/2 활성화 및 TLS 1.3 우선
- 90일마다 인증서 자동 갱신
리버스 프록시 구성
내부 애플리케이션을 HTTPS로 노출하는 가장 흔한 패턴입니다.
app.example.com {
reverse_proxy 127.0.0.1:3000
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
encode zstd gzip
log {
output file /var/log/caddy/app.log
format json
}
}
reverse_proxy 지시문은 기본적으로 X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host 헤더를 설정합니다. 백엔드가 WebSocket을 쓰더라도 별도 설정이 필요 없습니다.
헬스체크와 로드밸런싱
api.example.com {
reverse_proxy 10.0.0.11:8080 10.0.0.12:8080 10.0.0.13:8080 {
lb_policy round_robin
health_uri /healthz
health_interval 10s
health_timeout 3s
health_status 200
}
}
여러 사이트를 한 파일로 운영
단일 Caddyfile에서 수십 개 사이트를 운영해도 문제없습니다.
{
email [email protected]
# 스테이징 환경에서는 Let's Encrypt staging 사용
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
blog.example.com {
reverse_proxy 127.0.0.1:4000
}
shop.example.com {
root * /var/www/shop
file_server
try_files {path} {path}/ /index.html
}
admin.example.com {
reverse_proxy unix//run/admin/admin.sock
basicauth {
admin JDJhJDEwJEVCNmdaNEg2Ti5iejRMYkF3MFZhZ3VtV3E1SzBWZEZ5Q3UvZ1lKLzNKNldnSkI2d2VKcmtq
}
}
basicauth의 해시는 caddy hash-password 명령으로 생성합니다.
인증서 저장 위치와 마이그레이션
Caddy는 인증서와 키를 /var/lib/caddy/.local/share/caddy/certificates/ 아래에 저장합니다. 서버 이전 시 이 디렉터리 전체를 복사하면 재발급 없이 인증서를 그대로 옮길 수 있어, Let’s Encrypt rate limit을 피할 수 있습니다.
# 원본 서버
tar czf caddy-data.tar.gz -C /var/lib/caddy/.local/share caddy
# 대상 서버
systemctl stop caddy
tar xzf caddy-data.tar.gz -C /var/lib/caddy/.local/share
chown -R caddy:caddy /var/lib/caddy
systemctl start caddy
설정 검증과 무중단 리로드
# 문법 검증
caddy validate --config /etc/caddy/Caddyfile
# 무중단 리로드 (in-flight 요청 유지)
systemctl reload caddy
systemctl reload는 내부적으로 Caddy의 admin API(/load)를 호출해 새 설정을 적용합니다. 기존 연결은 끊어지지 않습니다.
Nginx와 비교할 때 주의할 점
| 항목 | Caddy | Nginx |
|---|---|---|
| 자동 HTTPS | 내장 | certbot 별도 |
| 설정 문법 | 간결, 기본값 안전 | 유연, 기본값 약함 |
| 모듈 확장 | 빌드 시 포함 (xcaddy) |
동적/정적 모듈 |
| 성능 | 단일 서버 수준에서 동등 | 동등, 튜닝 여지 많음 |
| 상세 튜닝 | 제한적 | 풍부한 디렉티브 |
트래픽이 매우 많고 세밀한 튜닝이 필요하다면 Nginx가 여전히 유리합니다. 반면 운영자 한 명이 수십 개의 사이트를 돌봐야 하는 상황이라면 Caddy의 설정 간결성은 압도적입니다.
자동 HTTPS는 단순히 편의 기능이 아니라 운영 실수로 인증서가 만료되는 사고를 구조적으로 차단합니다. 소규모 서비스, 스테이징·개발 환경, 혹은 내부용 리버스 프록시라면 Caddy를 기본 선택지로 두어도 손해볼 것이 없습니다.
댓글남기기