Cloudflare + Nginx Proxy Manager로 SSL 설정하기

낭가인·2025년 12월 4일

SKALA최종프로젝트

목록 보기
6/9

들어가며

백엔드 API를 배포하고 Swagger UI를 외부에서 접근 가능하게 하려다가 SSL 설정으로 한참을 헤맸다. Cloudflare와 Nginx Proxy Manager를 함께 쓰면서 발생한 여러 오류들과 해결 과정을 기록한다.

초기 구성

  • Domain: example.site (가비아에서 구매, Cloudflare DNS 관리)
  • 인프라: GCP VM + Docker
  • 리버스 프록시: Nginx Proxy Manager
  • 백엔드: Spring Boot (8080 포트)
  • 목표: https://example.site/swagger-ui/index.html 접근 가능하게 만들기

시행착오 1: SSL handshake failed (Error 525)

초기 설정

- Cloudflare: 주황 구름 (프록시 활성화) + Full SSL 모드
- Nginx Proxy Manager: SSL Certificate None

결과

SSL handshake failed
Error code 525

원인 분석

  • Cloudflare Full 모드는 Cloudflare ↔ 서버 간에도 HTTPS 통신을 요구한다
  • 하지만 Nginx는 SSL 인증서가 없어서 HTTP만 제공
  • 결과: Cloudflare가 HTTPS로 연결 시도 → Nginx가 응답 못함 → SSL handshake 실패

SSL 모드별 차이점

SSL 모드사용자 → CloudflareCloudflare → 서버서버 요구사항
OffHTTPHTTP없음
FlexibleHTTPSHTTP없음 (HTTP만)
FullHTTPSHTTPSSSL 인증서 (자체 서명 가능)
Full (strict)HTTPSHTTPS유효한 SSL 인증서 필수

시행착오 2: Let's Encrypt HTTP Challenge 실패 (403)

처음에는 "Cloudflare 프록시(주황 구름)가 켜져 있으면 Let's Encrypt HTTP Challenge를 막을 거야"라고 생각했다.

예상한 문제

  • Let's Encrypt는 /.well-known/acme-challenge/ 경로로 검증
  • Cloudflare 프록시가 중간에서 이 요청을 차단할 것이다
  • 따라서 회색 구름(DNS only)으로 바꿔야 한다

실제로는?

그냥 됐다. 😳

해결책: Cloudflare Full + Nginx Let's Encrypt

최종 작동 순서

graph TD
    A[Cloudflare Full + Nginx None] -->|525 에러| B[Nginx에서 Let's Encrypt 인증서 발급]
    B -->|HTTP Challenge 성공| C[Nginx에 인증서 적용]
    C -->|HTTPS 지원| D[접속 성공!]

구체적인 단계

1. 초기 상태 (에러 발생)

Cloudflare: Full 모드 + 주황 구름 (프록시)
Nginx: SSL None
결과: 525 에러

2. Let's Encrypt 인증서 발급

Nginx Proxy Manager 설정:
1. SSL Certificates → Add SSL Certificate
2. Let's Encrypt 선택
3. Domain Names: example.site
4. Email: 본인 이메일
5. Use a DNS Challenge: 체크 안 함 (HTTP Challenge)
6. I Agree to the Let's Encrypt Terms of Service 체크
7. Save 클릭

결과: 성공! 🎉

3. Proxy Host에 인증서 적용

  1. Proxy Hosts에서 example.site 편집
  2. SSL 탭:
    • SSL Certificate: 방금 생성한 Let's Encrypt 인증서 선택
    • Force SSL: 활성화
    • HTTP/2 Support: 활성화
  3. Save

4. 접속 성공

https://example.site/swagger-ui/index.html → 정상 작동!

왜 이게 가능했을까?

Cloudflare의 특별한 ACME Challenge 처리

Cloudflare는 Full 모드에서도 Let's Encrypt ACME Challenge를 자동으로 감지하고 특별 처리한다.

일반 요청:
사용자 → Cloudflare (HTTPS) → Nginx (HTTPS 시도)
→ Nginx가 SSL 인증서 없으면 525 에러

ACME Challenge 요청:
Let's Encrypt → Cloudflare → Nginx (HTTP로 폴백)
→ Nginx가 HTTP로 토큰 응답
→ 인증서 발급 성공!

동작 원리

  1. Let's Encrypt가 http://example.site/.well-known/acme-challenge/토큰 요청
  2. Cloudflare가 이 경로를 감지
  3. 백엔드에 HTTPS 연결 실패 → 자동으로 HTTP로 재시도
  4. Nginx가 HTTP로 토큰 파일 응답
  5. 인증서 발급 성공!

인증서 발급 후:

  • Nginx가 HTTPS를 지원하게 됨
  • Cloudflare Full 모드 정상 작동

핵심 요약

✅ 작동하는 방법

1. Cloudflare: Full 모드 + 주황 구름 유지
2. Nginx: SSL None 상태로 Proxy Host 생성
3. Nginx에서 Let's Encrypt HTTP Challenge로 인증서 발급
4. Proxy Host에 인증서 적용
5. 완료!

❌ 필요 없는 것들

  • Cloudflare를 Flexible로 바꿀 필요 없음
  • 회색 구름(DNS only)으로 바꿀 필요 없음
  • DNS Challenge 사용할 필요 없음 (API 토큰 불필요)
  • Cloudflare Origin Certificate 발급할 필요 없음

추가 팁

Nginx Proxy Manager 설정 확인사항

Proxy Host 설정:
  Domain Names: example.site
  Scheme: http
  Forward Hostname/IP: example-backend  # 컨테이너 이름
  Forward Port: 8080

  SSL 탭:
    SSL Certificate: Let's Encrypt 인증서 선택
    Force SSL: ON
    HTTP/2 Support: ON
    HSTS Enabled: ON (선택)

Docker 네트워크 확인

Nginx Proxy Manager와 백엔드 컨테이너가 같은 Docker 네트워크에 있어야 한다:

# 네트워크 생성
docker network create web

# 컨테이너 실행 시 네트워크 연결
docker run -d \
  --name example-backend \
  --network web \
  -p 8080:8080 \
  your-image

결론

Cloudflare는 개발자 친화적으로 설계되어 있어서, 프록시를 켠 상태로도 Let's Encrypt 인증서 발급이 가능하다.

처음에는 복잡하게 생각해서 여러 방법을 시도했지만, 결국 가장 간단한 방법이 정답이었다:

Cloudflare Full 모드 유지 → Nginx에서 Let's Encrypt HTTP Challenge → 인증서 적용

이것만 기억하면 된다! 🚀

참고 자료

profile
안녕하세요

0개의 댓글