연결 두 개, 인증서 두 개
호스트명을 Cloudflare 프록시(주황색 구름)로 돌리면 방문자는 내 서버와 직접 통신하지 않습니다. 연결이 두 개로 나뉩니다.
- 브라우저에서 Cloudflare 엣지까지. Cloudflare의 엣지 인증서를 씁니다. 보통 Cloudflare가 무료로 발급해 주는 Universal SSL 인증서입니다.
- Cloudflare 엣지에서 내 서버(원 서버, origin)까지. 내 서버에 설정된 인증서를 쓰거나, 아예 쓰지 않습니다.
대시보드의 SSL/TLS 암호화 모드(SSL/TLS → Overview)는 두 번째 연결만 정합니다. Cloudflare가 원 서버와 HTTP로 통신할지 HTTPS로 통신할지, 돌려받은 인증서를 검사할지를 정하는 설정입니다. 방문자 브라우저의 자물쇠는 첫 번째 연결에서 오므로 Off를 빼면 어느 모드에서나 똑같이 보입니다. 그래서 여기서 잘못 고른 설정이 몇 년씩 눈에 띄지 않습니다.
모드별 동작
| 모드 | Cloudflare → 원 서버 | 원 서버 인증서 검사 |
|---|---|---|
| Off | HTTP. 사이트로 오는 HTTPS 요청은 HTTP로 리다이렉트 | 없음 |
| Flexible | HTTP, 80 포트 | 없음. 원 서버에 인증서가 필요 없음 |
| Full | 방문자가 HTTPS로 왔으면 HTTPS | 어떤 인증서든 받아 줌 |
| Full (strict) | 방문자가 HTTPS로 왔으면 HTTPS | 만료되지 않고, 신뢰할 수 있고, 이름이 맞아야 함 |
놓치기 쉬운 점이 두 가지 있습니다.
- Full과 Full (strict)에서 Cloudflare는 방문자의 scheme을 따라갑니다. 방문자가
http://로 들어오면 원 서버에도 80 포트 HTTP로 요청하고, HTTPS로 올려 주지 않습니다. “Always Use HTTPS”(SSL/TLS → Edge Certificates)를 켜면 HTTP 방문자를 엣지에서 리다이렉트하므로 원 서버의 80 포트까지 오지 않습니다. - Cloudflare 문서에는 Strict (SSL-Only Origin Pull)라는 모드도 있습니다. 방문자가 HTTP로 와도 원 서버에는 항상 HTTPS로, 검증까지 해서 연결합니다. 글을 쓰는 시점에는 Enterprise 요금제 전용이라 대부분의 사이트에서는 위 네 가지만 보입니다.
새로 만든 존에서는 고정된 모드 대신 Automatic SSL/TLS가 보일 수 있습니다. 이 설정에서는 Cloudflare가 원 서버를 점검해서 동작할 것으로 보이는 가장 엄격한 모드를 고릅니다. 결과를 예측 가능하게 두고 싶다면 Custom을 고르고 모드를 직접 정하세요.
Flexible과 Full의 차이
Flexible은 HTTPS가 아예 없는 원 서버를 위한 모드입니다. Cloudflare가 방문자 쪽 TLS를 처리하고, 원 서버에서는 평문 HTTP로 페이지를 가져옵니다. 방문자에게는 자물쇠가 보이지만 나머지 절반은 암호화되지 않습니다. 호스팅사 네트워크, 뚫린 라우터, 공유 VLAN 등 Cloudflare와 내 서버 사이 경로에 있는 누구든 폼 전송 내용, 세션 쿠키, 비밀번호를 읽을 수 있고 페이지를 바꿀 수도 있습니다.
Flexible은 기능도 망가뜨립니다. 애플리케이션 입장에서는 모든 요청이 HTTP이기 때문입니다.
- 원 서버가 HTTP를 HTTPS로 리다이렉트하면(대부분 그렇고, WordPress는 사이트 주소가
https://로 시작하면 그렇게 합니다) 리다이렉트 루프가 생깁니다. 아래에서 다룹니다. - 애플리케이션이 요청 scheme으로 절대 URL을 만들기 때문에 페이지에
http://링크와 리소스가 섞여 나옵니다. 혼합 콘텐츠 문제입니다. - 요청 scheme을 보고
Secure플래그를 붙이는 쿠키가 플래그 없이 설정될 수 있습니다.
Full은 원 서버와 HTTPS로 통신해서 이 문제를 모두 없앱니다. 대신 원 서버의 443 포트에서 실제로 TLS가 돌아야 합니다. 그렇지 않으면 525 에러(포트는 응답하지만 TLS가 아님)나 연결 거부 에러(443에서 듣는 프로그램이 없음)가 납니다.
Full과 Full (strict)의 차이
Full 모드에서 Cloudflare는 원 서버 인증서를 전혀 검증하지 않습니다. 자체 서명, 만료, 다른 호스트명용 모두 통과합니다. 연결은 암호화되지만, Cloudflare는 상대가 내 서버인지, 중간에서 연결을 가로채 자기 인증서를 내민 누군가인지 알 수 없습니다.
Full (strict)는 당연히 해야 할 검사를 합니다. 원 서버 인증서가 다음을 만족해야 합니다.
- 만료되지 않았을 것
- 공인 CA나 Cloudflare Origin CA가 발급했을 것
- 호스트명에 맞을 것. 방문자가 요청한 호스트명이나, 프록시된 CNAME이라면 그 대상 호스트명입니다. 브라우저처럼 SAN 목록을 봅니다.
하나라도 어긋나면 방문자는 페이지 대신 526 에러를 봅니다. strict 모드의 비용은 이것뿐입니다. 유효하게 유지해야 할 것이 하나 늘어난다는 점입니다. Origin CA 인증서는 최대 15년까지 유효하게 만들 수 있고 Let’s Encrypt 인증서는 자동으로 갱신되니, 그 비용은 작습니다.
어떤 모드를 써야 하나
프록시를 거치는 모든 호스트명에 Full (strict)입니다. 나머지 모드는 거쳐 가는 단계일 뿐입니다.
- Flexible은 원 서버가 아직 HTTPS를 못 할 때 씁니다. 원 서버를 고치고 벗어나세요.
- Full은 원 서버 인증서를 당장 유효하게 만들 수 없을 때 씁니다. 예를 들어 갱신이 깨진 동안입니다. 적어도 암호화는 되니 Flexible보다는 낫지만 임시로만 두세요.
- Off는 공개 사이트라면 거의 쓸 일이 없습니다.
의견을 하나 덧붙이면, “그냥 되니까 Flexible을 쓴다”는 말은 대개 리다이렉트 루프나 혼합 콘텐츠 문제가 이미 대기 중이라는 뜻입니다. 원 서버에 인증서를 넣는 데는 10분이면 됩니다.
모드와 원 서버가 안 맞을 때 나타나는 증상
모드와 원 서버 설정이 어긋나면 오류가 어느 쪽인지 알려 줍니다.
| 설정 | 방문자가 보는 것 |
|---|---|
| Flexible인데 원 서버가 HTTP를 HTTPS로 리다이렉트 | ERR_TOO_MANY_REDIRECTS |
| Flexible인데 원 서버가 443만 열어 둠 | 521 에러(80 포트 연결 거부) |
| Full 또는 strict인데 443에서 듣는 프로그램이 없음 | 521 에러 |
| Full 또는 strict인데 443은 열렸지만 TLS가 없거나 쓸 수 있는 인증서가 없음 | 525 에러 |
| Full (strict)인데 인증서가 만료·자체 서명·다른 이름용 | 526 에러 |
Flexible인데 앱이 http:// URL을 만듦 | 혼합 콘텐츠 경고, 스타일 깨짐 |
방문자로서 다른 사람의 사이트에서 이런 오류를 봤다면 내 쪽에서 고칠 것은 없습니다. Cloudflare와 서버 사이에서 도는 루프는 쿠키를 지워도 풀리지 않습니다. 사이트 운영자만 바꿀 수 있습니다.
Flexible 리다이렉트 루프 고치기
먼저 루프인지, 어디로 돌고 있는지 확인합니다.
curl -sIL --max-redirs 5 https://www.example.com/ | grep -iE '^(HTTP|location)'
HTTP/2 301
location: https://www.example.com/
HTTP/2 301
location: https://www.example.com/
...
curl: (47) Maximum (5) redirects followed
똑같은 HTTPS 주소로 계속 리다이렉트된다면 Flexible의 전형적인 모습입니다. 원 서버는 HTTP 요청을 받았으니 HTTPS로 보내고, 브라우저는 그 주소를 다시 Cloudflare에 요청합니다. HTTP 점검으로 바깥에서 같은 리다이렉트 체인을 볼 수 있습니다.
제대로 된 해결은 원 서버에 인증서를 넣고 Full (strict)로 바꾸는 것입니다. 당분간 Flexible을 유지해야 한다면, 애플리케이션이 자기 연결 대신 Cloudflare가 알려 주는 scheme을 믿게 하세요. Cloudflare는 방문자의 scheme을 CF-Visitor 헤더로 보냅니다. 예를 들면 {"scheme":"https"}입니다. WordPress라면 wp-config.php에서 wp-settings.php를 불러오는 줄 위에 이렇게 넣습니다.
if (isset($_SERVER['HTTP_CF_VISITOR']) && strpos($_SERVER['HTTP_CF_VISITOR'], '"scheme":"https"') !== false) {
$_SERVER['HTTPS'] = 'on';
}
원 서버가 Cloudflare IP 대역에서 오는 트래픽만 받는 경우에만 이렇게 하세요. 그렇지 않으면 누구나 내 서버에 직접 이 헤더를 보낼 수 있습니다.
리다이렉트를 하는 층이 두 개 겹쳐 있지 않은지도 보세요. “HTTPS 강제” 플러그인, .htaccess 규칙, Cloudflare 리다이렉트 규칙은 각각은 맞아도 함께 있으면 루프를 만들 수 있습니다. 일반적인 경우는 리다이렉트 루프 진단에 정리돼 있습니다.
장애 없이 Full (strict)로 옮기기
스위치를 바꾸기 전에 Cloudflare가 연결하는 방식 그대로 원 서버를 테스트하세요. 원 서버의 실제 IP가 필요합니다. Cloudflare DNS 대시보드의 A 또는 AAAA 레코드 값입니다(프록시를 켠 레코드는 공개 DNS에서 Cloudflare 주소만 나옵니다).
curl -v --resolve www.example.com:443:203.0.113.10 https://www.example.com/ -o /dev/null
공인 인증서(Let’s Encrypt나 상용 CA)라면 -k 없이 성공해야 합니다. 만료나 이름 오류로 실패한다면 strict 모드도 똑같이 실패합니다. Origin CA 인증서는 내 컴퓨터가 기본으로 신뢰하지 않습니다. Cloudflare 문서에서 Origin CA 루트 인증서(내 인증서에 맞춰 RSA 또는 ECC)를 내려받아 --cacert로 넘기면, strict 모드가 검사하는 것과 같은 검사를 할 수 있습니다.
그다음 순서는 이렇습니다.
- 원 서버에 쓸 만한 인증서가 없다면 만듭니다. SSL/TLS → Origin Server → Create Certificate에서
example.com과*.example.com을 넣습니다. 프록시를 거치는 모든 호스트명의 HTTPS server 블록에 인증서와 키를 넣고 리로드합니다(nginx -t && systemctl reload nginx). - 모드를 Full (strict)로 바꿉니다.
- 호스트명별 예외 설정을 찾습니다. Configuration Rule이나 예전 Page Rule로 호스트명이나 경로 하나에만 다른 SSL 모드를 걸 수 있고, 존 전체 설정에는 드러나지 않습니다.
- 메인 호스트명만이 아니라 프록시를 거치는 모든 호스트명을 열어 봅니다. 다른 서버를 가리키는 서브도메인에서 일주일 뒤에 526이 터지곤 합니다.
존 설정은 API로도 확인할 수 있습니다. 값은 off, flexible, full, strict 중 하나입니다.
curl -s https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/ssl \
-H "Authorization: Bearer $CF_API_TOKEN" | jq '.result.value'
Origin CA 인증서와 그 함정
Origin CA 인증서는 무료이고, 대시보드에서 몇 초 만에 발급되며, Full (strict)에서 통과합니다. 유효 기간은 며칠부터 최대 15년까지 고를 수 있고, 내 존의 호스트명과 한 단계 와일드카드를 넣을 수 있습니다.
함정은 Cloudflare만 이 인증서를 신뢰한다는 점입니다. 브라우저, curl, 가동 모니터링 서비스, 다른 CDN은 거부합니다. 호스트명이 프록시를 거치는 동안은 문제없습니다. 누군가 “잠깐 테스트하려고” 레코드를 DNS 전용으로 바꾸는 순간 방문자가 인증서 오류를 보게 되고, 외부 모니터링이 원 서버를 직접 검사해도 오류가 납니다. 둘 중 하나라도 일어날 법하다면 원 서버에 Let’s Encrypt 인증서를 쓰세요. Cloudflare 뒤 Full (strict)에서도 똑같이 잘 동작합니다.
SSL 모드가 정하지 않는 것
- 방문자가 받는 인증서. Universal SSL은
example.com과*.example.com, 즉 한 단계까지만 커버합니다. 프록시된a.b.example.com은 어떤 모드를 고르든 브라우저에서 이름 오류가 납니다. ERR_CERT_COMMON_NAME_INVALID를 보세요. - 방문자의 HTTP→HTTPS 리다이렉트. Always Use HTTPS나 리다이렉트 규칙이 담당합니다.
- HSTS. Cloudflare나 원 서버에서 설정하되, 모든 호스트명이 HTTPS로 잘 동작한 다음에 켜세요. HSTS preload 체크리스트를 참고하세요.
SSL 점검은 어떤 호스트명에서 방문자가 받는 인증서를 보여 줍니다. 프록시된 호스트명이라면 원 서버 인증서가 아니라 Cloudflare 엣지 인증서가 나옵니다.
함께 보면 좋은 글
- Cloudflare 525 에러: Full 또는 strict 모드에서 원 서버와의 TLS 핸드셰이크가 실패합니다.
- Cloudflare 526 에러: Full (strict)가 원 서버 인증서를 거부합니다.
- Cloudflare 522 에러: Cloudflare가 원 서버에 아예 닿지 못합니다.
- 리다이렉트 루프 진단: SSL 모드 때문이 아닌 루프를 찾는 법.
내 도메인에서 바로 확인
무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.