Caddy는 웹 서버, 정적 파일 서버, 리버스 프록시 역할을 수행하면서 HTTPS 인증서 발급·갱신을 자동화하는 Go 기반 오픈소스 서버입니다. Nginx + Certbot + Cron + Reload Hook 조합으로 운영하던 인증서 관리 부담을 줄이고, Docker·내부 도구·소규모 서비스·고객 도메인 연결 환경에서 특히 강점을 보입니다.
Ⅰ. 왜 Caddy가 필요한가?
웹 서비스를 운영할 때 가장 먼저 마주치는 문제는 애플리케이션 실행이 아니라 외부 접속 구조를 안전하게 만드는 일입니다.
서비스를 공개하려면 보통 다음 작업이 필요합니다.
| 도메인 연결 → 웹 서버 설치 → Reverse Proxy 구성 → HTTPS 인증서 발급 → 인증서 경로 설정 → 인증서 자동 갱신 설정 → 갱신 후 웹 서버 reload → 로그 수집 → 장애 대응 |
기존에는 이 역할을 주로 Nginx, Apache, HAProxy가 담당했습니다.
이 방식은 안정적이지만, HTTPS 인증서 관리까지 포함하면 운영 구조가 복잡해집니다.
예를 들어 Nginx 환경에서는 보통 다음 구성이 필요합니다.
| Nginx 설정 파일 Certbot 또는 acme.sh Let's Encrypt 인증서 Cron 또는 systemd timer 갱신 후 nginx reload hook 방화벽 80/443 포트 정책 로그 수집기 |
이 구조에서는 다음 문제가 자주 발생합니다.
| ⅰ. 인증서가 만료되었는데 갱신 hook이 동작하지 않음 ⅱ. 인증서는 갱신되었지만 Nginx reload가 누락됨 ⅲ. Docker 컨테이너 재생성 후 인증서 저장 경로가 사라짐 ⅳ. 80번 포트가 막혀 ACME HTTP-01 Challenge 실패 ⅴ. 설정 파일 문법 오류로 웹 서버 재시작 실패 ⅵ. 새 도메인을 추가할 때마다 반복 작업 발생 ⅶCaddy는 이 문제에 대해 간단한 방향을 제시합니다. |
인증서 운영은 외부 도구가 아니라 웹 서버 본체가 책임져야 합니다.
Ⅱ. Caddy 기술이란?
Caddy는 Go 언어로 작성된 오픈소스 웹 서버입니다. 정적 파일 서버, Reverse Proxy, TLS 종료, 자동 HTTPS, 로드밸런싱, 헬스체크, Admin API 등을 제공합니다. 가장 큰 특징은 Automatic HTTPS입니다.
도메인만 Caddy 서버를 바라보도록 설정되어 있다면, Caddy는 다음 작업을 자동으로 수행합니다.
| ⅰ. HTTPS 인증서 발급 ⅱ. 인증서 갱신 ⅲ. HTTP → HTTPS 리다이렉트 ⅳ. TLS 기본 보안 설정 ⅴ. OCSP Stapling 처리 ⅵ. HTTP/2 지원 ⅶ. HTTP/3 사용 가능 환경 구성 |
즉, Caddy는 단순한 웹 서버라기보다 HTTPS 운영을 기본값으로 내장한 현대적인 Reverse Proxy라고 보는 것이 정확합니다.
Ⅲ. Caddy의 핵심 특징
ⅰ. 자동 HTTPS
일반적인 웹 서버는 인증서를 외부 도구로 발급하고, 웹 서버는 그 인증서 파일을 읽어 사용합니다. 반면 Caddy는 인증서 발급과 갱신 과정을 자체적으로 처리합니다.
| 기본 방식 | Caddy 방식 |
| Certbot이 인증서 발급 | Caddy가 도메인 확인 |
| 인증서 파일 생성 | 인증서 자동 발급 |
| Nginx가 인증서 경로 참조 | 인증서 자동 저장 |
| Cron으로 갱신 | 만료 전 자동 갱신 |
| 갱신 후 Nginx reload | 서비스 중단 없이 적용 |
운영자 입장에서는 인증서 작업이 크게 줄어듭니다.
ⅱ. Caddyfile 기반 간결한 설정
Caddy는 사람이 읽기 쉬운 Caddyfile을 제공합니다.
| example.com { reverse_proxy 127.0.0.1:3000 } |
이 몇 줄만으로 다음 작업을 수행할 수 있습니다.
| ⅰ. example.com 요청 수신 ⅱ. HTTPS 인증서 자동 발급 ⅲ. HTTP 요청을 HTTPS로 리다이렉트 ⅳ. 백엔드 127.0.0.1:3000으로 Reverse Proxy ⅴ. 기본 TLS 설정 적용 |
Nginx에 비해 설정이 짧고 직관적입니다.
ⅲ. Reverse Proxy 기능
Caddy는 Reverse Proxy를 기본 기능으로 제공하여 다음 서비스 앞단에 배치할 수 있습니다.
| 1) Node.js 2) Spring Boot 3) FastAPI 4) Django 5) Grafana 6) Prometheus 7) Jenkins 8) GitLab 9) Redmine 10) Wiki 서비스 11) 내부 관리자 페이지 |
예시:
| grafana.example.com { reverse_proxy grafana:3000 } jenkins.example.com { reverse_proxy jenkins:8080 } wiki.example.com { reverse_proxy wiki:3000 } |
ⅳ 정적 파일 서버
Caddy는 정적 파일 서버로도 사용할 수 있습니다.
| example.com { root * /var/www/html file_server } |
활용 예시는 다음과 같습니다.
| 1) 정적 HTML 사이트 2) 기술 문서 사이트 3) 다운로드 서버 4) 내부 포털 5) SPA 배포 서버 |
ⅴ. JSON 기반 Admin API
내부적으로 Caddy는 설정을 JSON 구조로 변환하여 동작합니다.
| caddy adapt --config /etc/caddy/Caddyfile --pretty |
이 구조 덕분에 Caddy는 API 기반 설정 변경도 가능합니다.
운영 자동화나 CI/CD 환경에서는 다음과 같은 방식도 사용할 수 있습니다.
| curl -X POST http://localhost:2019/load \ -H "Content-Type: application/json" \ --data-binary @new-config.json |
단, Admin API는 기본적으로 외부 공개용이 아닙니다.
운영 환경에서는 반드시 localhost, Unix Socket, SSH Tunnel, Sidecar 방식 등으로 제한해야 합니다.
Ⅳ. Caddy 동작 방식
ⅰ. 기본 구성 요소
| 구성 요소 | 역할 |
| Client | 브라우저, 모바일 앱, API 호출자 |
| DNS | 도메인을 Caddy 서버 IP로 연결 |
| Firewall / Security Group | 80, 443 포트 접근 제어 |
| Caddy | HTTPS 처리, Reverse Proxy, 정적 파일 제공 |
| Backend App | 실제 애플리케이션 서비스 |
| ACME | CA 인증서 발급 기관 |
| Log / Monitoring | 접근 로그, 에러 로그, 메트릭 수집 |
ⅱ. 기본 요청 흐름
| 사용자 브라우저 ↓ DNS 조회 ↓ Caddy 서버 80/443 포트 수신 ↓ TLS 인증서 확인 ↓ 인증서가 없으면 자동 발급 ↓ 요청 라우팅 ↓ 백엔드 애플리케이션으로 전달 ↓ 백엔드 응답 반환 ↓ Caddy가 클라이언트에게 HTTPS 응답 |
Ⅴ. Caddy 구성도
ⅰ. Caddy 기반 Reverse Proxy 구성도

ⅱ. TLS 처리 흐름도
| sequenceDiagram participant U as Client participant C as Caddy participant A as ACME CA participant B as Backend App U->>C: HTTPS 접속 요청 C->>C: SNI 기준 인증서 확인 alt 인증서 없음 C->>A: 인증서 발급 요청 A-->>C: 인증서 발급 C->>C: 인증서 저장 else 인증서 있음 C->>C: 기존 인증서 사용 end C->>B: Reverse Proxy 요청 전달 B-->>C: 애플리케이션 응답 C-->>U: HTTPS 응답 반환 |
Ⅵ. Caddy 설치 방법
ⅰ. Ubuntu / Debian 계열
| sudo apt update 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 -y |
ⅱ. Rocky Linux / AlmaLinux / RHEL 계열
| dnf install 'dnf-command(copr)' -y dnf copr enable @caddy/caddy -y dnf install caddy -y |
ⅲ. Docker Compose 설치 예시
| services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - ./site:/srv - caddy_data:/data - caddy_config:/config networks: - web app: image: your-app:latest container_name: app expose: - "3000" networks: - web networks: web: volumes: caddy_data: caddy_config: |
Docker 환경에서는 /data, /config 볼륨을 반드시 유지해야 합니다.
이 경로에는 인증서와 Caddy 운영 데이터가 저장됩니다.
Ⅶ. Caddy 사용 방법
ⅰ. 단순한 Reverse Proxy
| example.com { reverse_proxy 127.0.0.1:3000 } |
ⅱ. Docker Compose 서비스 프록시
Docker 컨테이너 안에서 localhost는 호스트 서버가 아니라 컨테이너 자기 자신입니다.
따라서 Docker Compose 환경에서는 서비스 이름을 사용해야 합니다.
| 잘못된 예: example.com { reverse_proxy localhost:3000 } 올바른 예: example.com { reverse_proxy app:3000 } |
ⅲ. 여러 서비스 도메인 분리
| grafana.example.com { reverse_proxy grafana:3000 } redmine.example.com { reverse_proxy redmine:3000 } zabbix.example.com { reverse_proxy zabbix-web:8080 } |
ⅳ. 헬스체크 포함 Reverse Proxy
| api.example.com { reverse_proxy app1:3000 app2:3000 { lb_policy least_conn health_uri /health health_interval 10s health_timeout 3s fail_duration 30s } } |
운영 환경에서는 백엔드 애플리케이션에 /health 같은 헬스체크 엔드포인트를 만들어 두는 것이 좋습니다.
ⅴ. 정적 사이트 + API 분리
| example.com { encode gzip zstd handle /api/* { reverse_proxy api:8080 } handle { root * /srv try_files {path} /index.html file_server } log { output file /var/log/caddy/example.log format json } } |
Ⅷ. 자주 쓰는 명령어
| 명령어 | 설명 |
| caddy version Caddy | 버전 확인 |
| caddy validate --config Caddyfile | 설정 문법 검사 |
| caddy adapt --config Caddyfile --pretty | Caddyfile을 JSON으로 변환 |
| caddy fmt --overwrite Caddyfile | Caddyfile 포맷 정리 |
| sudo systemctl reload caddy | 무중단 설정 리로드 |
| systemctl status caddy | 서비스 상태 확인 |
| journalctl -u caddy -f | 실시간 로그 확인 |
| caddy list-modules | 빌드된 모듈 확인 |
| caddy reverse-proxy --from :8080 --to :3000 | 임시 Reverse Proxy 실행 |
| caddy file-server --listen :8080 --root /srv | 임시 정적 파일 서버 실행 |
Ⅸ. 운영 시 주의해야 할 점
ⅰ. DNS가 먼저 정상 여부 확인
Caddy의 자동 HTTPS는 DNS가 정상이라는 전제에서 동작합니다.
| nslookup example.com dig example.com 확인할 내용: A 레코드가 올바른 서버 IP를 바라보는가? CNAME이 잘못 연결되어 있지 않은가? DNS 전파가 완료되었는가? Cloudflare 같은 프록시 서비스가 중간에 있는가? |
ⅱ. 80번과 443번 포트 확인
자동 HTTPS를 사용하려면 외부에서 80번 또는 443번 포트 접근이 가능해야 합니다.
| ss -lntp | grep -E ':80|:443' firewall-cmd --list-all ufw status |
클라우드 환경에서는 다음도 함께 확인해야 합니다.
| Security Group Network ACL Load Balancer Listener NAT 정책 사내 방화벽 정책 |
ⅲ. 인증서 저장소는 데이터처럼 관리 필요
Caddy의 /data 디렉터리는 단순 캐시가 아닙니다.
인증서, 계정 키, 관련 운영 데이터가 저장될 수 있습니다.
Docker에서 다음 볼륨은 반드시 유지해야 합니다.
| volumes: caddy_data: caddy_config: ※ 이 볼륨을 삭제하면 인증서를 다시 발급해야 하며, 반복 재발급으로 인증기관의 rate limit에 걸릴 수 있습니다. |
ⅳ. Admin API는 외부에 노출하지 않는다
Caddy의 Admin API는 강력하지만, 외부 공개용이 아닙니다.
| 운영 원칙 1) 외부 인터넷 노출 금지 2) localhost 바인딩 권장 3) 필요 시 SSH Tunnel 사용 4) Kubernetes에서는 Sidecar 또는 Operator 방식 권장 |
ⅳ. reload 전 validate 습관화
| 운영 환경에서는 다음 순서가 안전합니다. 1) caddy fmt --overwrite /etc/caddy/Caddyfile 2) caddy validate --config /etc/caddy/Caddyfile 3) sudo systemctl reload caddy 4) systemctl status caddy 5) journalctl -u caddy -n 50 |
Ⅹ. 트러블슈팅
ⅰ. HTTPS 인증서가 발급되지 않는 경우
| 1) 가능한 원인 - DNS가 서버 IP를 바라보지 않음 - 80번 포트가 차단됨 - 443번 포트가 차단됨 - Cloudflare SSL 모드 문제 - 내부망 또는 폐쇄망이라 ACME 서버 접근 불가 - 같은 도메인으로 인증서를 너무 자주 재발급함 2) 확인 명령어 - dig example.com - curl -I http://example.com - curl -Iv https://example.com - journalctl -u caddy -f 3) 해결 방향 - DNS부터 확인합니다. - OS 방화벽과 클라우드 보안 그룹을 함께 확인합니다. - CDN 뒤에 있다면 DNS-01 Challenge를 검토합니다. - Docker 볼륨이 사라져 인증서를 반복 발급하고 있지 않은지 확인합니다. |
ⅱ. 502 Bad Gateway가 발생하는 경우
| 1) 가능한 원인 - 백엔드 애플리케이션이 실행 중이 아님 - Caddyfile의 포트가 잘못됨 - Docker 네트워크가 다름 - 백엔드가 127.0.0.1에만 바인딩됨 - 컨테이너 서비스 이름을 잘못 입력함 2) 확인 명령어 - docker ps - docker logs app - docker network inspect web - curl -I http://app:3000 - journalctl -u caddy -f 3) 해결 방향 - Caddy 컨테이너와 백엔드 컨테이너가 같은 네트워크에 있는지 확인합니다. - localhost 대신 Docker Compose 서비스 이름을 사용합니다. - 백엔드가 0.0.0.0으로 listen 하는지 확인합니다. |
ⅲ. WebSocket이 끊기는 경우
| 1) 가능한 원인 Caddy의 Reverse Proxy는 일반적인 WebSocket을 지원합니다. 중간 방화벽, 보안 장비, L7 장비가 장시간 연결을 끊을 수 있습니다. 2) 확인할 내용 - 백엔드 로그에 Upgrade 요청이 도달하는가? - Caddy 라우팅 경로가 WebSocket 경로를 포함하는가? - 중간 장비가 idle timeout을 짧게 잡고 있지 않은가? - 애플리케이션에서 Origin 제한을 걸고 있지 않은가? |
ⅳ. HTTP/3가 동작하지 않는 경우
| 1) 가능한 원인 HTTP/3는 UDP 443을 사용합니다. TCP 443만 열려 있으면 HTTPS는 되지만 HTTP/3는 동작하지 않을 수 있습니다. 2) 확인할 내용 sudo ss -lunp | grep 443 3) 점검 포인트 - UDP 443이 열려 있는가? - 방화벽에서 UDP 443을 허용하는가? - 클라우드 보안 그룹에서 UDP 443을 허용하는가? - 중간 네트워크 장비가 QUIC을 차단하지 않는가? |
ⅴ. access log가 너무 커지는 경우
| 1) 증상 고트래픽 서비스에서 JSON access log를 파일로 남기면 디스크가 빠르게 찰 수 있습니다. 2) 대응 방법 - logrotate 설정 - Fluent Bit, Vector, Promtail 같은 수집기로 전달 - Loki, Elasticsearch, OpenSearch로 중앙화 - 불필요한 debug log 비활성화 |
■ Caddy와 Traefik 차이
Docker에서 Caddy와 자주 비교되는 기술은 Traefik입니다. 둘 다 Reverse Proxy 역할을 하지만 방향성이 다릅니다.
| 구분 | Caddy | Traefik |
| 핵심 철학 | 간단한 설정과 자동 HTTPS | 컨테이너 자동 감지와 동적 라우팅 |
| 설정 방식 | Caddyfile 중심 | Docker label 중심 |
| 초보자 접근성 | 쉬움 | 처음에는 복잡함 |
| Docker 자동 감지 | 기본은 약함 | 강함 |
| 서비스 | 수가 적을 때 적합 | 약간 과할 수 있음 |
| 서비스 수가 많고 자주 변할 때 | 관리 부담 증가 | 적합 |
| Kubernetes 친화성 | 가능 | 강함 |
| 운영 가독성 | Caddyfile 하나로 명확 | label이 많아지면 복잡 |
※ 추천 기준
ⅰ. 도메인 몇 개, 내부 도구, 개인 서버, Docker Compose 중심 → Caddy 추천
ⅱ. 컨테이너가 많고 서비스가 자주 생기고 사라짐 → Traefik 추천
'어플리케이션' 카테고리의 다른 글
| 웹 자동화의 표준, Selenium에 대해 알아보겠습니다. (0) | 2026.04.29 |
|---|---|
| 초고속 분산 처리 엔진 Apache Spark에 대해 알아보겠습니다. (0) | 2026.04.28 |
| Apache Iceberg 성능 최적화와 데이터 관리 전략 (0) | 2026.04.27 |
| 실시간 레이크하우스의 핵심 Apache Paimon 에 대해 알아보겠습니다. (0) | 2026.04.24 |
| 브루트 포스(Brute Force, 무차별 대입) 공격 완전 정복 (0) | 2026.03.05 |