522 에러가 뜻하는 것

Cloudflare 프록시를 켜면 요청은 두 구간으로 나뉩니다. 방문자에서 Cloudflare까지, 그리고 Cloudflare에서 내 서버(원 서버, origin)까지입니다. 522는 두 번째 구간의 맨 첫 단계에서 난 실패입니다. Cloudflare가 원 서버 IP와 포트로 TCP SYN을 보냈는데 SYN-ACK이 돌아오지 않았습니다. 기다리다 포기하고 방문자에게 522 화면을 보여 준 것입니다. 브라우저 정상, Cloudflare 정상, 호스트 오류.

핵심은 시간 초과라는 말입니다. 연결을 거부한 것은 없습니다. 패킷은 어딘가로 갔고 돌아온 것은 침묵이었습니다. Cloudflare 문서는 이 대기 시간을 약 19초로 적고 있고, 방문자가 겪는 것도 그대로입니다. 한참 로딩하다가 오류가 뜹니다.

Cloudflare는 드문 경우 하나에도 522를 씁니다. 연결은 열렸는데 Cloudflare가 보낸 요청을 원 서버가 끝내 확인(ACK)하지 않은 경우입니다(Cloudflare 문서상 약 90초). 아래 진단은 거의 언제나 첫 번째 경우에 관한 것입니다.

방문자라면

내 쪽에서 고칠 것은 없습니다. 실패한 연결은 Cloudflare 서버와 그 사이트 서버 사이입니다. 캐시를 지우거나, 브라우저를 바꾸거나, VPN을 꺼도 달라지지 않습니다. 잠시 후 다시 시도하거나 사이트 운영자에게 알리세요. 오류가 난 시각과 오류 화면 아래쪽의 Ray ID를 함께 전하면 실패한 요청을 정확히 찾을 수 있습니다.

521, 522, 523, 524: 연결이 어디까지 갔나

Cloudflare의 52x 코드는 원 서버 연결을 단계별로 알려 줍니다. 맞는 코드를 고르면 엉뚱한 층을 디버깅하는 시간을 아낄 수 있습니다.

코드일어난 일
523Cloudflare가 원 서버 IP로 가는 경로 자체를 찾지 못함
521원 서버가 TCP 연결을 거부함(reset)
522TCP 연결에 응답이 없어 시간 초과
525TCP는 연결됐지만 TLS 핸드셰이크가 실패함
524연결하고 요청도 보냈지만 Cloudflare 한도(기본 100초) 안에 HTTP 응답이 없음

그러니 522는 문제가 HTTP보다도, TLS보다도 아래에 있다는 뜻입니다. nginx 설정, 애플리케이션, 인증서는 실행될 기회조차 없었습니다. IP 주소, 포트, 방화벽, 그리고 커널이 연결을 받아들일 수 있는 상태인지를 보세요.

1단계: Cloudflare가 실제로 연결하는 IP 찾기

공개 DNS로는 알 수 없습니다. 프록시를 켠 레코드에 dig을 하면 Cloudflare 주소가 나옵니다.

dig +short www.example.com
# 104.21.x.x
# 172.67.x.x

실제 원 서버 주소는 Cloudflare 대시보드 DNS → Records의 A(와 AAAA) 레코드 값입니다. 이 값을 서버의 실제 공인 주소와 비교하세요. 서버에서:

curl -4 https://ifconfig.me ; echo
ip -brief addr

둘이 다르면 원인을 찾았을 가능성이 큽니다(아래 ‘잘못된 IP’ 참고). AAAA 레코드가 있는지도 보세요. 있다면 Cloudflare가 원 서버에 IPv6로 연결할 수 있고, 그 IPv6 주소에 닿지 않거나 듣고 있는 프로그램이 없으면 IPv4가 멀쩡해도 522가 납니다.

2단계: 바깥에서 원 서버 테스트하기

Cloudflare처럼, 올바른 호스트명을 보내면서 원 서버 IP에 직접 연결해 봅니다.

curl -v --connect-timeout 20 -k \
  --resolve www.example.com:443:203.0.113.10 https://www.example.com/ -o /dev/null

결과는 셋 중 하나입니다.

  • Connected to www.example.com (203.0.113.10) port 443: 원 서버는 내 연결을 받습니다. 그래도 Cloudflare가 522를 받는다면 무언가가 Cloudflare 주소만 다르게 다루고 있습니다. 3단계로 가세요.
  • 몇 ms 만에 Failed to connect ... Connection refused: 그 포트에서 듣는 프로그램이 없거나 방화벽이 reject합니다. 521 계열이고, 거부가 내 IP에만 해당하는 게 아니라면 Cloudflare 쪽에서도 522가 아니라 521로 보여야 합니다.
  • Connection timed out after 20001 milliseconds: 원 서버가 모두를 drop합니다. IP, 클라우드 방화벽, 서버 방화벽 순서로 보세요.

nc -vz -w 10 203.0.113.10 443도 HTTP 부분 없이 같은 답을 줍니다. SSL 모드가 Flexible이면 80 포트도 테스트하세요(원인 3).

포트 점검은 같은 일을 내 네트워크 바깥에서 합니다. 원 서버 IP를 넣거나, 한 포트만 보려면 203.0.113.10:443처럼 넣으세요. 3초를 기다리고, 거부와 시간 초과를 둘 다 ‘닫힘’으로 보여 주므로 둘을 구분해야 할 때는 curl을 쓰세요. 프록시를 켠 호스트명을 넣지는 마세요. Cloudflare 주소로 풀리는데, 이 도구는 Cloudflare 자체 주소 대역에 TCP 연결을 열 수 없어서 ‘점검 불가’로 표시합니다.

주의할 점이 하나 있습니다. 원 서버가 일부러 Cloudflare 대역만 받도록 해 두었다면 바깥 테스트는 모두 시간 초과가 납니다. 우리 도구도 마찬가지입니다. 이것은 정상 동작이지 버그가 아닙니다. 이 경우 바로 3단계로 가세요.

3단계: 원 서버에서 지켜보기

패킷이 어디서 멈추는지 보여 주므로 가장 쓸모 있는 테스트입니다. 원 서버에서 해당 포트의 SYN 패킷을 잡으면서 Cloudflare를 거쳐 사이트를 새로고침하세요.

sudo tcpdump -ni any 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'

리눅스에서 tcpdump는 들어오는 패킷을 방화벽보다 먼저 봅니다. 그래서 iptables가 drop하는 패킷도 여기서는 보입니다.

IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0
IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0
IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0

같은 SYN이 반복되고 반대 방향의 Flags [S.] 응답이 없다면, 패킷은 서버에 도착했는데 서버가 침묵하는 것입니다. 서버 방화벽이거나, 커널이 연결을 받을 수 없는 상태입니다(원인 1, 4).

패킷이 아예 안 보이면 서버까지 오지 못하는 것입니다. DNS 레코드가 다른 곳을 가리키거나(원인 2), 서버 앞의 방화벽이 버리고 있습니다. 클라우드 보안 그룹, 호스팅사 DDoS 필터, 공유기 같은 것들입니다(원인 1, 6).

SYN이 들어오고 SYN-ACK도 나가는데 522라면, 응답이 돌아가지 못하는 것입니다. 네트워크 인터페이스가 둘이거나 VPN이 돌아가는 경로를 바꾸고 있는지, 또는 한 방향만 막는 상위 필터가 있는지 보세요.

흔한 원인

1. 방화벽이 Cloudflare 주소를 drop함

가장 흔한 원인입니다. 동작이 DROP인 규칙은 아무것도 돌려보내지 않으므로 Cloudflare는 기다리다 시간 초과가 납니다. 흔한 출처는 이렇습니다.

  • fail2ban, CrowdSec 같은 도구가 ‘공격적인’ IP를 차단함. Cloudflare 뒤에서는 모든 방문자 요청이 Cloudflare 주소에서 들어오므로, 요청을 너무 많이 보내는 IP를 막는 규칙이 결국 Cloudflare 엣지 서버를 막게 됩니다. 그 엣지를 거치는 방문자만 522를 보고 나머지는 멀쩡합니다. 그래서 이 경우는 간헐적이고 지역에 따라 다르게 나타나는 일이 많습니다.
  • 기본 거부(default deny) 서버 방화벽에 사무실 IP만 허용했거나, Cloudflare IPv4 대역만 허용하고 IPv6는 빠뜨림.
  • 호스팅사 자체의 DDoS 방어나 rate limit. 모든 트래픽이 Cloudflare 주소 수천 개에서만 오므로 공격처럼 보일 수 있습니다.

서버의 방화벽 카운터와 차단 목록을 확인하세요.

sudo iptables -L INPUT -n -v --line-numbers | head -30
sudo nft list ruleset | less
sudo ufw status verbose
sudo fail2ban-client status nginx-limit-req   # 실제 jail 이름으로

해결: Cloudflare가 공개한 대역(https://www.cloudflare.com/ips-v4, https://www.cloudflare.com/ips-v6)을 80·443에 대해 모든 계층에서 허용하고 자동 차단 대상에서 빼세요. 그리고 웹 서버가 CF-Connecting-IP 헤더로 실제 방문자 IP를 기록하게 하세요(nginx에서는 real_ip_header CF-Connecting-IP와 Cloudflare 대역마다 set_real_ip_from). 그러면 fail2ban이 Cloudflare가 아니라 방문자를 차단합니다.

방화벽이 호스팅사 소관이라 직접 바꿀 수 없다면, 두 대역 목록을 보내고 80·443을 허용해 달라고 요청하세요.

2. DNS 레코드가 잘못된 IP를 가리킴

원 서버는 옮겼는데 레코드는 그대로인 경우입니다. 새 서버로 이전한 뒤, 호스팅사가 IP를 바꾼 뒤, 또는 IP가 바뀌는 가정용 회선에서 흔합니다. Cloudflare는 아무도 응답하지 않는 주소나 나와 상관없는 기계로 SYN을 보내게 됩니다.

tcpdump 테스트에서 아무것도 들어오지 않고, 1단계에서 주소 불일치가 보입니다. Cloudflare 대시보드에서 A/AAAA 레코드를 고치세요. IP가 주기적으로 바뀐다면 Cloudflare용 동적 DNS 갱신 도구를 쓰거나 터널(아래)로 바꾸세요. 유지할 수 없는 AAAA 레코드는 지우세요.

3. Cloudflare가 내가 닫은 포트로 연결함

Cloudflare가 원 서버에 연결하는 포트는 방문자가 쓴 포트가 아니라 SSL/TLS 모드에 따라 정해집니다. Flexible 모드에서는 방문자가 HTTPS로 와도 Cloudflare는 원 서버의 80 포트로 연결합니다. “HTTPS 전용 사이트니까” 하고 80을 닫았다면 Flexible에서는 522(또는 521)가 납니다.

Origin Rule이 목적지 포트를 바꾸거나, 레코드가 표준이 아닌 포트의 서비스를 위한 것일 때도 마찬가지입니다. 그 호스트명에 실제로 적용되는 SSL 모드를 확인하세요. 존 설정을 덮어쓰는 Configuration Rule이나 예전 Page Rule도 포함입니다. 더 나은 해결은 원 서버에서 HTTPS를 서비스하고 Full (strict)를 쓰는 것입니다. 모드 비교는 Cloudflare SSL 모드에 있습니다.

4. 원 서버가 새 연결을 제때 받지 못함

서버는 떠 있지만 커널이 SYN에 답하지 않는 경우입니다. tcpdump에는 SYN이 들어오는데 응답이 없고, 그것을 설명하는 방화벽 규칙도 없습니다. 이런 것을 보세요.

nstat -az TcpExtListenOverflows TcpExtListenDrops
ss -ltn 'sport = :443'          # Recv-Q가 Send-Q에 가까우면 accept 대기열이 가득 찬 것
sudo journalctl -k | grep -i conntrack
  • listen 대기열 넘침. 애플리케이션이 연결을 들어오는 속도보다 느리게 받는 경우로, 대개 워커가 모두 바쁜 탓입니다. 위 카운터가 계속 올라갑니다. 먼저 워커 용량(nginx worker_connections, PHP-FPM pm.max_children, 앱 서버의 스레드 수)을 늘리고, 그다음 backlog(net.core.somaxconn, nginx의 listen 443 ssl backlog=...)를 늘리세요. 예전 커널은 somaxconn 기본값이 128이라 금방 찹니다.
  • 연결 추적(conntrack) 테이블이 가득 참. 커널 로그에 nf_conntrack: table full, dropping packet이 남습니다. sysctl net.netfilter.nf_conntrack_count와 net.netfilter.nf_conntrack_max를 비교하고 최대값을 올리거나, 추적할 필요 없는 트래픽은 추적하지 않게 하세요.
  • 원 서버 IP를 직접 노린 SYN flood. Cloudflare를 아예 우회합니다. 원 서버 IP가 알려져 있다면 Cloudflare 대역만 받도록 막아서 flood가 웹 서버까지 오지 못하게 하세요.

이 경우는 부하에 따라 났다 안 났다 합니다. 그것이 가장 좋은 구별법입니다.

5. 원 서버에서 KeepAlive를 끔

Cloudflare의 문제 해결 문서에는 원 서버의 KeepAlive 비활성화도 원인으로 올라 있습니다. Cloudflare는 원 서버와의 연결을 재사용하는데, keep-alive가 꺼져 있으면 요청마다 새 TCP 핸드셰이크가 필요해 바쁜 서버의 연결 부담이 몇 배가 되고 원인 4가 훨씬 쉽게 일어납니다. nginx에서는 keepalive_timeout 0;, Apache에서는 KeepAlive Off가 끄는 설정입니다. 다시 켜세요.

6. 집에서 돌리는 서버, 또는 중간의 ISP

원 서버를 집에서 돌리면 Cloudflare가 넘어야 할 층이 늘어납니다. 공유기에 서버로 가는 포트 포워딩이 있어야 하고, 일부 가정용 ISP는 들어오는 80·443을 막으며, CGNAT 뒤라면 들어올 수 있는 공인 주소 자체가 없습니다. 공유기 관리 화면의 WAN 주소가 내 공인 IP와 다르면 CGNAT 뒤에 있는 것이고, 포트 포워딩으로는 해결되지 않습니다.

집 서버라면 Cloudflare Tunnel(cloudflared)이 더 나은 설계입니다. 터널 데몬이 Cloudflare로 나가는 연결을 맺으므로, 들어오는 포트도, 포트 포워딩도, 고정 IP도 필요 없고 522가 날 수 없게 됩니다. (터널 장애는 다른 오류 코드로 나타납니다.)

522를 만들지 않고 원 서버를 Cloudflare 전용으로 잠그기

원 서버에 Cloudflare만 접근하게 하는 것은 좋은 생각입니다. Cloudflare를 우회해 서버를 직접 치는 것을 막아 줍니다. 동시에 나중에 522를 만드는 가장 흔한 경로이기도 합니다. 몇 가지 원칙입니다.

  • IPv4와 IPv6 목록을 모두, 모든 계층에서 허용하세요. 서버 방화벽, 클라우드 보안 그룹, 호스팅 관리 화면.
  • Cloudflare 대역은 드물지만 바뀝니다. 한 번 붙여 넣고 끝내지 말고 공개 목록에서 주기적으로 갱신하세요.
  • Cloudflare가 아닌 트래픽은 DROP보다 REJECT로 막는 편이 낫습니다. 보안에는 거의 차이가 없고, 무언가 잘못되면 느린 522 대신 빠른 521을 받게 됩니다.
  • IP 목록에 기대지 않는 잠금을 원한다면 Authenticated Origin Pulls(원 서버가 Cloudflare의 클라이언트 인증서를 요구)나 터널을 쓰세요.

ufw 예시입니다(목록이 바뀌면 빠진 대역의 규칙도 지우세요).

for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
  sudo ufw allow proto tcp from "$ip" to any port 80,443
done

고친 다음에

  • 페이지를 새로고침하고 Ray ID가 바뀌었는지 보세요. 새 Ray ID로 522가 나온다면 원 서버가 아직 실패하고 있는 것입니다.
  • tcpdump 명령을 한 번 더 돌려 SYN마다 Flags [S.]가 돌아가는지 확인하세요.
  • 원 서버가 여러 대라면(A 레코드 여러 개, 로드밸런서 풀) IP를 하나씩 테스트하세요. 죽은 원 서버 하나가 요청 일부에서만 522를 만듭니다.
  • 공개 호스트명에 HTTP 점검을 돌려 전체 경로가 정상 응답을 돌려주는지 확인하세요.

비슷해 보이는 다른 오류

  • Cloudflare 525 에러: TCP는 연결됐지만 원 서버와의 TLS 핸드셰이크가 실패했습니다.
  • Cloudflare 526 에러: 핸드셰이크는 됐지만 Full (strict)가 원 서버 인증서를 거부했습니다.
  • 443 포트 닫힘: Cloudflare가 앞에 없을 때 같은 ‘응답 없음’ 문제입니다.
  • ERR_CONNECTION_TIMED_OUT: 응답을 받지 못한 연결을 브라우저가 보여 주는 방식입니다.

내 도메인에서 바로 확인

무료, 가입 불필요. 이 가이드가 다루는 항목을 바로 검사하고 조치 방법을 알려드립니다.