Caddy 자동 HTTPS: Caddy 자동 TLS의 작동 원리와 문제 해결
Caddy는 설정에 있는 모든 도메인에 대해 ACME로 TLS 인증서를 발급·갱신합니다. HTTP-01, TLS-ALPN-01, DNS-01 와일드카드, 온디맨드 TLS, 자주 발생하는 오류를 알아봅니다.
짧은 답변: Caddy는 공개 도메인 이름인 모든 사이트 주소에 대해 자동으로 HTTPS를 활성화합니다. ACME(기본값은 Let's Encrypt 또는 ZeroSSL)로 인증서를 요청하고, 80번과 443번 포트에서 HTTP-01 또는 TLS-ALPN-01 챌린지로 도메인 소유를 증명하며, HTTP를 HTTPS로 리디렉션하고, 만료 전에 인증서를 갱신합니다. 추가 설정은 필요 없습니다.
Caddy 리버스 프록시란 Caddy가 그 HTTPS 연결을 종료하고 `reverse_proxy` 지시어로 다른 포트, 컨테이너, 호스트의 앱에 요청을 전달하는 것입니다. 몇 줄로 TLS와 프록시를 함께 해결한다는 점이 사람들이 Caddy를 선택하는 주된 이유입니다.
세 줄로 만드는 SSL 리버스 프록시
DNS가 서버를 가리키고 80번과 443번 포트에 접근할 수 있다면, 이 Caddyfile은 두 호스트 이름을 위한 완전한 HTTPS 리버스 프록시입니다. Caddy는 시작 시 두 이름의 인증서를 받아 HTTP/1.1, HTTP/2, HTTP/3로 서비스하고, 백엔드에 `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`를 전달합니다. `email` 옵션은 선택 사항이지만, CA가 만료되거나 폐기된 인증서에 대해 연락할 수 있게 해 줍니다.
공식 apt 저장소를 통한 설치와 systemd 기본 사항은 Caddy 리버스 프록시 가이드에서, 컨테이너는 Docker 리버스 프록시로서의 Caddy에서 다룹니다. 변경 사항은 `sudo systemctl reload caddy`로 적용하세요.
# /etc/caddy/Caddyfile
{
email [email protected]
}
example.com, www.example.com {
reverse_proxy 127.0.0.1:8080
}Caddy 자동 TLS의 내부 동작
Caddy는 설정을 불러올 때 사이트 주소에서 모든 호스트 이름을 모으고 각 인증서를 누가 발급할지 결정합니다. 공개 도메인은 공개 ACME 발급 기관으로 가고, `localhost`, IP 주소, 내부 전용 이름은 Caddy 자체 로컬 CA에서 인증서를 받습니다(`tls internal`을 쓴 것과 같습니다). 인증서, 키, ACME 계정은 Caddy 데이터 디렉터리에 저장됩니다. apt 패키지는 `/var/lib/caddy/.local/share/caddy`, 공식 Docker 이미지는 `/data`입니다.
갱신은 만료 훨씬 전에 백그라운드에서 이루어지며, CA가 지원하는 경우 Caddy는 OCSP 응답을 스테이플링합니다. 발급이 실패하면 Caddy는 지수 백오프로 재시도하며, 프로덕션 속도 제한을 소모하지 않도록 재시도에는 CA의 스테이징 엔드포인트를 사용합니다. 같은 스토리지를 공유하는 여러 Caddy 인스턴스는 서로 조율해 하나만 특정 인증서를 요청합니다.
| 챌린지 | CA가 확인하는 것 | 필요 조건 | 와일드카드? |
|---|---|---|---|
| HTTP-01 | `http://domain/.well-known/acme-challenge/`에서 제공되는 토큰 | 인터넷에서 80번 포트 접근 가능 | 불가 |
| TLS-ALPN-01 | TLS 핸드셰이크에서 제시되는 특수 인증서 | 인터넷에서 443번 포트 접근 가능 | 불가 |
| DNS-01 | `_acme-challenge.domain`의 TXT 레코드 | DNS 제공업체 API 토큰과 해당 제공업체 모듈이 포함된 Caddy 빌드 | 가능 |
DNS-01로 와일드카드 인증서 받기
`*.example.com` 같은 와일드카드 인증서는 DNS-01 챌린지로만 발급받을 수 있습니다. 표준 Caddy 바이너리에는 DNS 제공업체 플러그인이 없으므로 xcaddy로 빌드하거나(또는 공식 다운로드 페이지에서 제공업체를 선택) 해당 존의 DNS 편집 권한만 가진 API 토큰을 Caddy에 제공합니다. DNS-01은 내부 전용 서버처럼 80번과 443번 포트가 인터넷에서 접근 불가능할 때도 작동합니다.
토큰은 Caddyfile에 넣지 마세요. systemd 유닛이 사용하는 환경 파일에 넣고(`sudo systemctl edit caddy` 후 `[Service]` 아래에 `EnvironmentFile=/etc/caddy/caddy.env`) `chmod 600`으로 파일 권한을 제한하세요.
# Build Caddy with a DNS provider module (example: Cloudflare)
xcaddy build --with github.com/caddy-dns/cloudflare
# Caddyfile: one wildcard certificate for all subdomains
*.example.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
@app host app.example.com
handle @app {
reverse_proxy 127.0.0.1:3000
}
handle {
respond "Unknown host" 404
}
}온디맨드 TLS와 스테이징
온디맨드 TLS는 Caddy가 처음 보는 호스트 이름에 대해 첫 TLS 핸드셰이크 중에 인증서를 받게 합니다. 고객이 자신의 도메인을 당신의 서버로 연결하는 플랫폼을 위한 기능입니다. 반드시 `ask` 엔드포인트를 설정하세요. Caddy는 `?domain=name`으로 이를 호출하고 200 응답일 때만 진행합니다. 이것이 없으면 누구나 임의의 이름을 당신의 IP로 연결해 속도 제한에 걸리게 만들 수 있습니다.
실험하는 동안에는 전역 옵션 `acme_ca`로 Let's Encrypt 스테이징 CA로 전환하세요. 스테이징 인증서는 브라우저가 신뢰하지 않지만 제한이 훨씬 높아서 마음껏 망가뜨리고 고칠 수 있습니다. 작업이 끝나면 옵션을 제거하고, 원한다면 데이터 디렉터리에서 스테이징 인증서를 삭제하세요.
{
on_demand_tls {
ask http://127.0.0.1:5555/allowed
}
}
https:// {
tls {
on_demand
}
reverse_proxy 127.0.0.1:8080
}
# Global options block for testing
{
email [email protected]
# Test against Let's Encrypt staging; remove when everything works
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}자주 발생하는 Caddy TLS 오류와 해결 방법
- 01.DNS 미연결: A 또는 AAAA 레코드가 없거나, 여전히 이전 서버를 가리키거나, 캐시되어 있습니다. 오래된 AAAA 레코드는 대표적인 함정입니다. CA는 IPv6가 있으면 그것을 우선하기 때문입니다. `dig`로 둘 다 확인하세요.
- 02.80번 또는 443번 포트 차단: 호스트 방화벽, 클라우드 방화벽, 라우터가 인바운드 트래픽을 막아 HTTP-01도 TLS-ALPN-01도 완료할 수 없습니다. 80/tcp와 443/tcp(HTTP/3용으로 443/udp도)를 여세요.
- 03.포트 사용 중: Apache, nginx 또는 다른 컨테이너가 80번이나 443번에 바인딩되어 있어 Caddy가 리슨할 수 없습니다. 다른 서비스를 중지하거나 Caddy 뒤로 옮기세요.
- 04.앞단의 CDN 프록시: 오렌지 클라우드 방식의 프록시가 Caddy보다 먼저 TLS를 종료해 TLS-ALPN-01이 실패할 수 있습니다. DNS-01을 사용하거나 발급 중에는 레코드를 DNS 전용으로 설정하세요.
- 05.속도 제한: 반복된 실패나 데이터 디렉터리 삭제로 CA 제한에 걸립니다(Let's Encrypt는 호스트 이름당 시간당 검증 실패 횟수와 주당 중복 인증서 수를 제한합니다). 근본 원인을 고치고, 스테이징을 사용하고, 데이터 디렉터리를 영구적으로 유지하세요.
- 06.CAA 레코드: 다른 CA만 허용하는 CAA 레코드는 발급을 막습니다. `letsencrypt.org`와 `sectigo.com`(ZeroSSL이 사용)을 허용하거나 제한을 제거하세요.
# Does the name resolve to this server (both IPv4 and IPv6)?
dig +short A example.com
dig +short AAAA example.com
curl -4 -s https://ifconfig.me ; echo
# Are ports 80 and 443 open in the host firewall?
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
# Is something else already bound to 80/443?
sudo ss -ltnp '( sport = :80 or sport = :443 )'
# What does Caddy say?
sudo journalctl -u caddy --no-pager -n 100백엔드까지 HTTPS로
같은 호스트의 백엔드는 대부분 localhost에서 일반 HTTP를 사용하며, 이는 문제없습니다. 업스트림이 다른 머신에 있거나 TLS를 요구한다면 `https://` 업스트림을 사용하고, 어떤 이름을 검증하고 어떤 CA를 신뢰할지 Caddy에 알려 주세요. 간단한 테스트 외에는 `tls_insecure_skip_verify`를 피하세요. 프록시와 백엔드 사이의 가로채기에 대한 보호가 사라지기 때문입니다. 서버 간 트래픽에는 백엔드 인증서를 관리하는 것보다 사설 WireGuard 터널이 더 간단한 경우가 많습니다.
전체 root 권한과 전용 IPv4 주소가 있는 NoctisNode VPS에서 일반적인 설정은 다음과 같습니다. A 레코드를 서버로 지정하고(서버에 IPv6가 없다면 AAAA는 생략), 80번과 443번을 열고, 나머지는 Caddy에 맡기세요. HTTP가 아닌 트래픽을 전달해야 한다면 Caddy TCP 및 SSH 프록시를 참고하세요.
example.com {
reverse_proxy https://10.0.0.5:8443 {
transport http {
tls_server_name backend.internal
tls_trust_pool file /etc/caddy/backend-ca.pem
}
}
}FAQ
QCaddy 리버스 프록시란 무엇인가요?
ACaddy 웹 서버가 클라이언트 연결을 받아 보통 HTTPS를 종료하고, `reverse_proxy` 지시어로 하나 이상의 백엔드 서비스에 요청을 전달하는 것입니다. 이를 위한 TLS 인증서는 Caddy가 자동으로 발급하고 갱신합니다.
QCaddy는 인증서를 자동으로 갱신하나요?
A네. 도메인이 계속 서버로 해석되고 챌린지 포트에 접근할 수 있는 한, Caddy는 만료 훨씬 전에 백그라운드에서 인증서를 갱신하고 재시작 없이 다시 불러옵니다.
QCaddy가 인증서를 받지 못하는 이유는 무엇인가요?
A가장 흔한 원인은 DNS 레코드가 다른 곳을 가리키는 경우(오래된 AAAA 레코드 포함), 80번과 443번 포트 차단, 다른 프로그램의 포트 사용, 반복 실패 후의 속도 제한입니다. `journalctl -u caddy`로 정확한 ACME 오류를 확인할 수 있습니다.
QCaddy로 와일드카드 인증서를 발급받을 수 있나요?
A네, 하지만 DNS-01 챌린지로만 가능합니다. 이를 위해서는 DNS 제공업체 모듈이 포함된 Caddy 빌드와 해당 존의 API 토큰이 필요합니다.
QCaddy에서 자동 HTTPS를 비활성화하려면 어떻게 하나요?
A단일 사이트라면 `http://` 사이트 주소를 사용하거나, 전역 옵션 `auto_https off`를 설정하세요. `auto_https disable_redirects`를 사용하면 인증서는 유지하면서 HTTP에서 HTTPS로의 리디렉션만 중지할 수 있습니다.