# Caddy + Headscale 리버스 프록시 트러블슈팅 > 날짜: 2026-04-06 > 환경: Caddy v2.11.2, Headscale 0.26.1 (Docker), Tailscale 1.96.4 ## 증상 외부 노드에서 `tailscale up --login-server=http://head.pharmq.kr` 실행 시 무한 대기 또는 실패. - PBS-LXC, 약국 PVE 등 외부 노드에서 Headscale 등록 불가 - Headscale Docker 재시작 후 기존 노드 재연결 실패 - Caddy 로그에 502 Bad Gateway 다수 발생 ## 원인: 2가지 문제 ### 문제 1: HTTP → HTTPS 강제 리다이렉트 (308) **Caddy 동작**: TLS 설정이 있는 도메인은 자동으로 HTTP(80) → HTTPS(443) 308 리다이렉트 생성 ``` # Tailscale 클라이언트 요청 POST http://head.pharmq.kr/ts2021 # Caddy 응답 HTTP/1.1 308 Permanent Redirect Location: https://head.pharmq.kr/ Server: Caddy ``` **왜 문제인가**: Tailscale 클라이언트는 `--login-server=http://...`로 설정되면 HTTP를 기대함. HTTPS 리다이렉트를 자동으로 따라가지 못함. **해결**: `http://` 전용 블록을 별도로 추가 ```caddy http://head.pharmq.kr { reverse_proxy 192.168.0.100:8070 } ``` ### 문제 2: HTTP/2에서 Upgrade 헤더 호환 불가 (502) **Caddy 동작**: HTTPS 연결에서 백엔드로 프록시할 때 HTTP/2를 사용 ``` # Tailscale TS2021 프로토콜 요청 POST /ts2021 HTTP/1.1 Connection: upgrade Upgrade: tailscale-control-protocol # Caddy가 h2로 변환하여 백엔드에 전달 → 에러 http2: invalid Upgrade request header: ["tailscale-control-protocol"] → 502 Bad Gateway ``` **왜 문제인가**: HTTP/2 스펙(RFC 7540)에서 `Connection`과 `Upgrade` 헤더는 hop-by-hop이라 금지됨. Tailscale TS2021은 HTTP/1.1 Upgrade 메커니즘에 의존. **Caddy 로그 (실제)**: ```json { "level": "error", "msg": "http2: invalid Upgrade request header: [\"tailscale-control-protocol\"]", "request": { "method": "POST", "uri": "/ts2021", "headers": { "Connection": ["upgrade"], "Upgrade": ["tailscale-control-protocol"] } }, "status": 502 } ``` **해결**: 백엔드 연결을 HTTP/1.1로 강제 + 스트리밍 즉시 전달 ```caddy head.pharmq.kr { reverse_proxy 192.168.0.100:8070 { flush_interval -1 transport http { versions 1.1 } } tls { dns cloudflare {env.CF_API_TOKEN} } } ``` ## 최종 Caddyfile 설정 ```caddy # Headscale — HTTP (리다이렉트 없이 직접 프록시) http://head.pharmq.kr { reverse_proxy 192.168.0.100:8070 } # Headscale — HTTPS (HTTP/1.1 강제, TS2021 Upgrade 호환) head.pharmq.kr { reverse_proxy 192.168.0.100:8070 { flush_interval -1 transport http { versions 1.1 } } tls { dns cloudflare {env.CF_API_TOKEN} } } ``` ## 검증 방법 ```bash # HTTP 프록시 확인 (리다이렉트 없어야 함) curl -v http://head.pharmq.kr 2>&1 | grep -E "HTTP|Location|Server" # 기대: HTTP/1.1 404 Not Found, Server 없거나 Via: 1.1 Caddy # HTTPS 프록시 확인 curl -sk https://head.pharmq.kr/ts2021 2>&1 | head -5 # 502가 아니어야 함 # Tailscale 등록 테스트 tailscale up --login-server=http://head.pharmq.kr \ --authkey= --accept-routes --accept-dns=false ``` ## 참고 - Docker 포트 매핑: 호스트 8070 → 컨테이너 8080 - Headscale config: `listen_addr: 0.0.0.0:8080` - 같은 LAN에서는 NAT Hairpinning 문제로 `http://192.168.0.100:8070` 직접 사용 필요 ([nat-hairpinning.md](nat-hairpinning.md) 참조)