자동 HTTPS 리버스 프록시의 이해부터, 내 시스템에 적용하는 단계별 로드맵까지.
Docker 컨테이너: 자동화 에이전트 · 스케줄러 · 내부 API
접근: Tailscale(사설 VPN)로만
Docker 컨테이너: 텔레그램 봇 6개 · 사용량 대시보드 · 세션 저장 파이프라인
접근: 같은 Tailscale 망
내 기기들끼리만 통하는 사설망. 외부 인터넷에서 이 시스템으로 들어오는 문은 0개다.
봇 6개가 각자 0.5초마다 텔레그램 서버에 "새 메시지 있어?"
메시지가 없어도 요청은 계속 발생
응답 지연 최대 0.5~1초 · CPU와 네트워크 낭비
새 메시지가 오면 텔레그램이 내 서버로 즉시 POST
이벤트 기반 · 즉시 반응 · 낭비 없음
조건: 외부에서 부를 수 있는 공개 HTTPS 주소
API 비용 모니터링 — 그 컴퓨터 앞에 앉아야만 볼 수 있다
수백 개의 작업 기록 — 특정 앱(Obsidian)을 열어야만 검색된다
웹 프론트엔드 · iOS 단축어 · 자동화 도구가 내 API를 호출할 방법 자체가 없다
포트포워딩은 DNS에 우리 집 공인 IP를 공개 등록해야 작동한다 — 회선 전체가 표적이 된다
서비스마다 :3000, :8080, :5173… 접속자가 번호를 알아야 하고, 포워딩 규칙도 포트 수만큼
HTTPS는 필수(웹훅 조건)인데, 인증서 발급·갱신을 서비스 수만큼 반복해야 한다
경로만 열리고 나머지는 계속 숨는다 · 인증서는 관문이 한 번에 처리
여러분의 localhost에도 세상에 내보내고 싶은 것이 하나쯤 있습니다
이것이 설정의 전부 — 인증서 발급 · 자동 갱신 · HTTP→HTTPS 리다이렉트 포함.
Go로 작성된 단일 실행 파일, 오픈소스(2015~), 설정 파일 이름은 Caddyfile.
인증서 발급·갱신 자동(ACME)
뒤의 서비스로 전달·분배
파일 서버 기능 내장
최신 프로토콜 기본 지원
간결한 설정 문법
실행 중 설정 변경 가능
의존성 없는 실행 파일 하나
합리적인 기본값 — 설정하지 않아도 안전한 쪽으로 동작
| 항목 | Nginx | Caddy |
|---|---|---|
| HTTPS 설정 | certbot 별도 설치 + 갱신 cron | 자동 — 설정 자체가 없음 |
| 설정 문법 | 강력하지만 장황 | 간결, 대부분 몇 줄 |
| 성능 | 최고 수준(C) | 높음(Go) — 개인·중소 규모엔 차이 체감 불가 |
| 생태계·레퍼런스 | 매우 넓음 | 성장 중 |
| 설정 반영 | reload 필요 | 무중단 reload + 실행 중 API 변경 |
| 학습 곡선 | 높음 | 낮음 |
리버스 프록시 — 가장 흔한 패턴
정적 사이트 / SPA
/webhook/* → 텔레그램 봇들 — 증상 1 해결
/usage/* → 대시보드 API — 증상 2 해결
/sessions/* → 세션 검색 — 증상 2 해결
문은 여전히 443 하나 — 증상 3 해결
로드밸런싱 + 헬스체크
Basic Auth
CORS 헤더
포트는 프로그램이 정하는 값 — 80/443은 강제가 아니라 관례. 충돌은 같은 네트워크 공간 안에서만.
컨테이너는 각자 내부 IP를 받는다 — 둘 다 :3000을 써도 무충돌. 구분 기준은 포트가 아니라 IP:포트 쌍.
compose 네트워크 안에서는 이름이 주소가 된다(이름→IP 자동 변환) — 그래서 reverse_proxy app:3000이 가능하고, IP가 바뀌어도 깨지지 않는다.
앱 컨테이너는 인터넷에서 구조적으로 도달 불가 — Caddy가 라우팅한 경로만 열린다
기존 사이트 배포(Vercel 등)는 그대로 두고, api.mydomain.com A 레코드 하나만 서버로 추가
localhost는 Caddy의 내부 CA(사설 발급 기관) 인증서 — 브라우저가 아직 모르는 발급자일 뿐. caddy trust 또는 "고급 → 이동"으로 통과
localhost는 공인 인증서가 불가능하므로 Caddy가 스스로 발급한다 — 실전 도메인에서는 이 자리가 자동으로 공인 인증서가 된다
/a와 /b가 서로 다른 앱으로 — 두 앱 모두 내부 포트는 3000, 구분은 이름(IP)
서비스 추가 = handle 블록 추가 — 문은 계속 하나, 포워딩 규칙도 인증서도 늘지 않는다
증상 1 — polling 6개 루프 → 웹훅 즉시 수신
증상 2 — 갇힌 API → 도메인 하나로 호출 가능
증상 3 — 문 여러 개 부담 → 관리된 문 하나
SSH·관리자 대시보드는 Caddy 라우팅에 아예 넣지 않는다 — VPN(Tailscale) 전용 유지
시크릿 토큰 검증(X-Telegram-Bot-Api-Secret-Token) + 추측 불가능한 랜덤 경로
조회형 API는 API 키·Basic Auth를 관문에서 강제 — 실행형 API는 공개 여부 자체를 신중히
공개할 서비스만 Caddy의 Docker 네트워크에 연결 — 나머지는 구조적으로 도달 불가
DNS: api.mydomain.com → 집 공인 IP
외부 → 공유기(포트 개방) → 서버
DNS 기록은 공개 정보 — nslookup 한 줄로 누구나 조회
공인 IP는 회선 전체의 주소 — 공격 시 집 인터넷이 함께
공개된 IP는 통상 수 분 내 자동 포트 스캔 대상
DNS: api.mydomain.com → 중계 서버 IP
서버 →(먼저 나감)→ 중계 ←(타고 들어옴)← 외부
집 IP는 어디에도 기록되지 않는다
공유기에 여는 포트 0개 — 나가는 연결은 원래 허용
중계가 앞에서 공격 트래픽을 받아준다
| 방식 | 얻는 것 | 감수할 것 |
|---|---|---|
| 포트포워딩 직결 | 의존성 0 · 기존 네임서버에 A 레코드만 | 집 IP 노출 · 포트 개방 · 유동 IP면 DDNS 필요 |
| Cloudflare Tunnel | IP 은닉 · 무포트 · WAF · 무료 | 네임서버를 Cloudflare로 이전 필수 · TLS 종단이 CF |
| Tailscale Funnel | 이미 쓰고 있다면 가장 간단 · 무료 | 커스텀 도메인 불가(*.ts.net 고정) · 대역폭 제한 |
| VPS 중계 | 완전한 통제 · A 레코드만 · IP 은닉 | 월 비용 · 관리할 서버 +1 |
터널 장애 = 공개 문만 닫힘. 관리망(Tailscale SSH)은 독립 경로라 서버 접속·운영은 계속된다
터널이든 포트포워딩이든 VPS든 Caddy 뒤 구성은 동일 — 종속은 입구 한 조각에 국한, 갈아끼우면 된다
웹훅 장애 시 봇은 polling으로 즉시 복귀 가능 — 지금 쓰는 방식이 곧 백업이다
네임서버 이전도 가역적 — "갇히는 종속"이 아니라 "교체 비용이 있는 선택"으로 판단하라
1단계 — 검증
도메인 · 네임서버 이전 · 비용 전부 불필요.
웹훅이 실제로 들어오는지 확인이 목표
2단계 — 확장
효용 확인 후 앞 장의 표에서 입구 선택.
갈림길: 네임서버 이전을 감수하는가
3단계 — 정착
/webhook/* · /api/* · /sessions —
추가는 handle 블록 하나씩
내 컴퓨터·서버에서 도는 것 전부 — localhost에서만 도는 것 포함
누가 부르나 · 조회형인가 실행형인가 · 유출 시 피해는 — 세 질문 통과한 것만
커스텀 도메인 필요 여부부터 결정 트리를 따라간다
검증 → 확장 → 정착 — 1단계는 이번 주에 할 수 있는 크기로
1 내 시스템 한 줄 소개
2 밖으로 내보내기로 한 것 (그리고 안 내보내기로 한 것)
3 선택한 입구와 그 이유
4 1단계 — 이번 주에 할 일
같은 도구, 다른 시스템, 다른 선택 — 선택의 이유가 오늘 배운 것의 전부입니다.
서비스 몇 개~십여 개의 개인·소규모 인프라
HTTPS·인증서 관리를 자동화하고 싶다
정적 페이지와 API가 섞여 있다
설정을 한 파일에서 한눈에 보고 싶다
이미 잘 돌아가는 Nginx 체계가 있다
컨테이너가 수십 개씩 수시로 뜨고 진다(→ Traefik)
극한 트래픽·초저지연이 목표다
조직 표준이 이미 다른 도구다
A1 Caddy vs Traefik · A2 유동 IP와 DDNS · A3 Cloudflare와 TLS 종단 · A4 홈서버 vs VPS vs PaaS
| 항목 | Traefik | Caddy |
|---|---|---|
| 설정 방식 | 컨테이너 라벨 자동 감지 — 뜨고 지는 컨테이너를 스스로 라우팅 | Caddyfile 한 파일에 명시 |
| 유리한 환경 | 컨테이너 수십 개, 수시 변동(오케스트레이터) | 서비스 몇 개~십여 개, 구성 안정적 |
| 학습 곡선 | 라우터·서비스·미들웨어 개념 체계 필요 | 낮음 |
| 정적 파일 서빙 | 불가(순수 프록시) — 파일 서버를 별도로 | 내장 — 혼자 다 한다 |
DDNS 없이 포트포워딩 = IP가 바뀌는 순간 서비스 두절 — 원인도 한참 뒤에 알게 된다
터널 방식은 서버가 먼저 나가서 연결하므로 유동 IP여도 무관 — 이 고민 자체가 없다
Cloudflare는 구조적으로 내용을 읽을 수 있는 위치다 — WAF·캐시가 가능한 이유이자 대가
민감 데이터라면 종단간 통제가 되는 방식으로 — VPS 중계(WireGuard) 또는 포트포워딩 직결
| 홈서버 | VPS | PaaS (Vercel 등) | |
|---|---|---|---|
| 비용 | 이미 가진 하드웨어 + 전기 | 월 5천 원~ (사양 비례) | 무료~ (사용량 비례) |
| 자원 | 램·스토리지 넉넉, 로컬 데이터 직접 접근 | 저사양부터, 데이터는 올려야 | 코드만 — 파일·하드웨어 접근 불가 |
| 상시 프로세스 | 무제한 (봇 6개든 뭐든) | 가능 (사양 안에서) | 대부분 불가 — 요청 시에만 실행 |
| 노출 고민 | 오늘 배운 설계가 필요 | 없음 (고정 IP 기본) | 없음 (HTTPS·도메인 자동) |