이 글은 Cloudflare 프록시와 인증서 시리즈의 1번째 편이다.
주황 구름 하나가 DNS 응답, 접속 경로, TLS 종료 지점을 어떻게 바꾸는지 다룬다.
들어가며
주황 구름을 껐더니 인증서 경고가 떴다. 같은 도메인, 같은 서버인데 결과가 달랐다.
Cloudflare DNS 레코드 옆의 구름 아이콘은 오래 "CDN 켜기/끄기" 정도로만 알고 있었다. 틀린 설명은 아니다. 그런데 이 이해로는 위 상황을 설명할 수 없었다. 반대로 주황 구름에서는 525가 뜨는데 회색 구름으로 바꾸면 멀쩡히 접속되는 경우도 있었다.
결론부터 말하면 이 차이는 Cloudflare가 주소만 알려주는지, 요청까지 대신 받는지에서 나온다. DNS 기초(권위 서버, Anycast, CNAME Flattening)는 DNS 동작 원리 글에서 다뤘으니, 이번 편은 그 다음 단계인 HTTP 요청 경로와 TLS 연결에 집중한다.
DNS Only: Cloudflare는 주소만 알려준다
회색 구름(DNS Only)에서 Cloudflare는 권위 DNS 서버 역할만 한다. 클라이언트는 받은 IP로 오리진 서버에 직접 접속한다.
sequenceDiagram
participant C as Client
participant D as Cloudflare DNS
participant O as Origin Server
C->>D: example.com A?
D-->>C: 203.0.113.10 (오리진 IP)
C->>O: TLS 핸드셰이크 + HTTPS 요청
O-->>C: 오리진 인증서 + 응답
DNS 조회가 끝난 순간 Cloudflare는 대화에서 빠진다. 그래서 다음 두 가지가 따라온다.
- 브라우저가 검증하는 인증서는 오리진 서버의 인증서다. 브라우저가 신뢰하는 공인 CA 인증서여야 경고가 안 뜬다.
- 대시보드에서 SSL/TLS 모드를 Full (strict)로 해 두었어도 이 레코드에는 아무 영향이 없다. 모드는 Cloudflare 엣지와 오리진 사이 구간의 설정인데, 그 구간 자체가 없기 때문이다.
Proxied: Cloudflare가 요청을 대신 받는다
주황 구름(Proxied)에서 Cloudflare는 리버스 프록시가 된다. DNS는 오리진 IP 대신 Cloudflare Anycast IP를 응답하고, 클라이언트는 가장 가까운 Cloudflare 엣지에 접속한다.
sequenceDiagram
participant C as Client
participant D as Cloudflare DNS
participant E as Cloudflare Edge
participant O as Origin Server
C->>D: example.com A?
D-->>C: Cloudflare Anycast IP
C->>E: TLS 연결 ① (엣지 인증서)
E->>E: TLS 종료, WAF·캐시·규칙 처리
E->>O: 연결 ② (SSL/TLS 모드에 따라 HTTP 또는 HTTPS)
O-->>E: 응답
E-->>C: 응답
여기서 연결이 두 개로 쪼개진다. 브라우저와 엣지 사이의 연결 ①, 엣지와 오리진 사이의 연결 ②는 서로 별개의 TCP·TLS 세션이다. 엣지가 연결 ①의 TLS를 풀어야 캐시나 WAF가 요청 내용을 볼 수 있기 때문이다.
그래서 브라우저 주소창의 자물쇠는 연결 ①만 보증한다. 연결 ②가 암호화되는지, 오리진 인증서를 검증하는지는 별도 설정인 SSL/TLS 모드가 정한다. 이게 EP.02의 주제다.
dig로 확인한 DNS 응답 차이
Proxied 레코드는 DNS 단계에서부터 오리진 IP를 숨긴다. 내 도메인 hyochan.site는 Proxied 상태라 실제로 조회하면 이렇게 나온다.
dig +short hyochan.site A
dig +short hyochan.site AAAA
172.67.161.243
104.21.15.78
2606:4700:3033::ac43:a1f3
2606:4700:3033::6815:f4e
104.21.0.0/16, 172.67.0.0/16, 2606:4700::/32는 모두 Cloudflare가 공개한 IP 대역이다. 오리진 주소는 어디에도 없다.
회색 구름 레코드라면 대시보드에 입력한 값이 그대로 나온다. 정리하면 다음과 같다.
| 레코드 상태 | A/AAAA 응답 | CNAME 응답 |
|---|---|---|
| DNS Only | 입력한 오리진 IP | 입력한 대상 호스트명 |
| Proxied | Cloudflare Anycast IP | 대상 대신 Cloudflare IP(A/AAAA) |
CNAME도 Proxied 상태면 대상 호스트명이 노출되지 않는다는 점이 의외로 헷갈린다. 확인하려면 dig example.com CNAME을 따로 날려보면 된다.
curl과 openssl로 본 엣지 인증서
Proxied 상태에서 브라우저가 받는 인증서는 오리진에 설치한 인증서가 아니라 Cloudflare 엣지 인증서다. 같은 도메인으로 직접 확인했다.
echo | openssl s_client -connect hyochan.site:443 \
-servername hyochan.site 2>/dev/null \
| openssl x509 -noout -issuer -subject -dates -ext subjectAltName
issuer=C=US, O=Google Trust Services, CN=WE1
subject=CN=hyochan.site
notBefore=Oct 9 16:58:01 2026 GMT
notAfter=Jan 7 17:57:50 2027 GMT
X509v3 Subject Alternative Name:
DNS:hyochan.site, DNS:idp.hyochan.site, DNS:*.idp.hyochan.site
발급자는 Google Trust Services다. 내가 발급받은 적 없는 인증서다. Cloudflare가 Universal SSL로 자동 발급해 엣지에 걸어 둔 것이다. 유효기간은 90일이고 갱신도 Cloudflare가 알아서 한다.
응답 헤더와 엣지 정보도 확인할 수 있다.
curl -sI https://hyochan.site | grep -iE '^(server|cf-ray)'
curl -s https://hyochan.site/cdn-cgi/trace | grep -E 'colo|tls|http='
server: cloudflare
cf-ray: a4a7aaf489b0d1ee-ICN
colo=ICN
http=http/2
tls=TLSv1.3
cf-ray 끝의 ICN은 인천 PoP(Point of Presence)에서 응답했다는 뜻이다. /cdn-cgi/trace는 Proxied 호스트에서만 응답하니, 프록시가 켜져 있는지 볼 때 이것부터 쳐보면 된다.
단, 여기서 보이는 tls=TLSv1.3은 연결 ①의 정보다. 엣지와 오리진 사이 연결 ②의 상태는 클라이언트 쪽 도구로는 보이지 않는다.
프록시를 켜면 달라지는 것 한눈에 보기
주황 구름 하나로 DNS 응답부터 보안 기능 적용 범위까지 줄줄이 바뀐다.
| 항목 | DNS Only (회색) | Proxied (주황) |
|---|---|---|
| DNS 응답 | 오리진 IP | Cloudflare Anycast IP |
| HTTP(S) 접속 대상 | 오리진 서버 | Cloudflare 엣지 |
| 브라우저가 검증하는 인증서 | 오리진 인증서 | Cloudflare 엣지 인증서 |
| SSL/TLS 모드 적용 | 안 됨 | 엣지 ↔ 오리진 구간에 적용 |
| CDN 캐시 | 안 됨 | 캐시 규칙에 따라 적용 |
| WAF·DDoS 방어·Rules | 안 됨 | 요금제·설정에 따라 적용 |
| 오리진이 보는 클라이언트 IP | 실제 방문자 IP | Cloudflare IP (CF-Connecting-IP 헤더로 원래 IP 전달) |
| 지원 프로토콜 | 제한 없음 | HTTP(S), 지정 포트만 |
마지막 줄은 놓치기 쉽다. 일반 Proxied 레코드가 중계하는 포트는 정해져 있다.
| 구분 | 포트 |
|---|---|
| HTTP | 80, 8080, 8880, 2052, 2082, 2086, 2095 |
| HTTPS | 443, 2053, 2083, 2087, 2096, 8443 |
SSH(22)나 SMTP(25)를 Proxied 레코드로 걸어두면 접속이 안 된다. 이런 서비스는 DNS Only 레코드를 따로 두거나 Spectrum, Tunnel 같은 별도 제품을 써야 한다. 내부망 접근은 Cloudflare 딥다이브 EP.02에서 다룬 Tunnel·WARP 구성이 더 맞다.
오리진 애플리케이션 로그에 Cloudflare IP만 찍히는 것도 같은 이유다. 실제 방문자 IP가 필요하면 CF-Connecting-IP 헤더를 읽도록 웹 서버를 설정해야 한다.
삽질 기록
주황 구름 관련 문제는 대부분 "연결이 두 개"라는 사실을 놓쳐서 생긴다. 실제로 헷갈렸던 세 가지를 정리했다.
1. 주황 구름을 끄니 인증서 경고가 떴다
오리진에 Cloudflare Origin CA 인증서를 설치하고 Full (strict)로 잘 쓰던 서브도메인이 있었다. 디버깅하려고 잠깐 회색 구름으로 바꿨더니 NET::ERR_CERT_AUTHORITY_INVALID가 떴다.
인증서가 깨진 게 아니었다. 브라우저가 검증하는 대상이 엣지 인증서에서 오리진 인증서로 바뀌었을 뿐이다. Origin CA 인증서는 Cloudflare만 신뢰하고 브라우저는 신뢰하지 않는다. Cloudflare 공식 문서도 프록시를 끄거나 Cloudflare를 일시 정지하면 이 경고가 뜬다고 명시한다. 왜 그런지는 EP.02의 신뢰 체계 부분에서 풀어본다.
2. 주황 구름인데 서버에 직접 접속이 됐다
주황 구름이 서버를 숨겨준다고 생각했다. 그런데 오리진 IP로 curl --resolve를 날리니 그대로 응답이 왔다.
주황 구름은 DNS 응답에서 IP를 빼줄 뿐, 서버의 방화벽이 아니다. IP는 다른 경로로도 새어 나간다.
- 같은 서버를 가리키는 DNS Only 서브도메인(
mail,ftp,dev등) - 프록시를 켜기 전의 과거 DNS 기록(DNS 히스토리 서비스에 남는다)
- 오리진이 직접 보내는 메일의 헤더
- Censys 같은 인터넷 스캐너가 IP 대역을 훑으며 수집한 오리진 인증서(인증서의 도메인으로 IP를 역추적할 수 있다)
오리진을 정말 숨기려면 방화벽에서 Cloudflare IP 대역만 허용하거나, Authenticated Origin Pulls 또는 Tunnel을 쓴다. 구체적인 구성은 EP.02 운영 환경 섹션에 정리했다.
3. dig로 확인했는데 바뀐 게 없었다
구름 아이콘을 바꾼 직후 dig를 했는데 이전 IP가 나왔다. 로컬 리졸버 캐시 때문이다. Proxied 레코드의 TTL은 Auto(기본 5분)로 고정되지만, 이전에 DNS Only로 긴 TTL을 걸어뒀다면 그만큼 남는다. 권위 서버에 직접 물으면 바로 확인된다.
dig +short example.com A @$(dig +short NS example.com | head -1)
정리
주황 구름은 "CDN 켜기"가 아니라 누가 HTTP 요청을 먼저 받는지를 바꾸는 스위치다.
| 질문 | DNS Only | Proxied |
|---|---|---|
| Cloudflare의 역할 | 권위 DNS | 권위 DNS + 리버스 프록시 |
| TLS 연결 수 | 1개 (브라우저 ↔ 오리진) | 2개 (브라우저 ↔ 엣지, 엣지 ↔ 오리진) |
| 브라우저가 신뢰해야 할 인증서 | 오리진 인증서 | 엣지 인증서 (Cloudflare가 관리) |
| 오리진 인증서를 검증하는 주체 | 브라우저 | Cloudflare (SSL/TLS 모드에 따라) |
마지막 줄이 다음 편으로 이어진다. Proxied에서는 오리진 인증서를 검증하는 쪽이 브라우저가 아니라 Cloudflare다. 그렇다면 오리진에 인증서가 없거나, 자체 서명 인증서거나, Origin CA 인증서일 때 Cloudflare는 각각 어떻게 반응할까?
EP.02에서는 Off, Flexible, Full, Full (strict) 모드와 인증서 종류를 교차해서 525·526 오류가 나는 조건, 그리고 인증서 신뢰도가 실제로 무엇을 뜻하는지 다룬다.