Nginx real IP 모듈은 set_real_ip_from에 등록한 신뢰 주소가 보내는 헤더를 기준으로 client 주소를 바꿉니다. real_ip_recursive on이면 신뢰된 주소 체인을 따라 헤더에서 마지막 비신뢰 주소를 선택합니다.

$realip_remote_addr는 real IP 처리 전 원래 연결 주소를 보존합니다. X-Forwarded-For 자체를 무조건 신뢰할 수는 없으며, 실제 proxy CIDR과 헤더를 정리하는 ingress 동작을 확인해야 합니다.

X-Forwarded-For는 증거가 아니라 입력값입니다

Nginx가 reverse proxy 뒤에 있으면 TCP 연결에서 직접 보이는 peer는 최종 client가 아니라 바로 앞 proxy일 수 있습니다. real IP 모듈은 설정한 헤더에서 주소를 읽어 Nginx가 사용하는 client 주소를 바꿀 수 있습니다. 이 기능은 set_real_ip_from으로 올바른 대체 주소를 보낼 것으로 신뢰하는 proxy 주소를 선언하는 방식입니다. 그래서 X-Forwarded-For라는 이름이 있다는 이유만으로 그 안의 왼쪽 첫 주소를 진짜 사용자 IP라고 믿으면 안 됩니다. 누가 헤더를 만들었는지와 중간 proxy가 값을 추가·정리했는지 확인해야 합니다.

설정 구문을 보여 주는 교육용 예시입니다. CIDR은 문서용 주소이며 실제 환경 설정이 아닙니다.
# 가상 예: 승인된 proxy 범위를 실제 목록으로 대체해야 함
set_real_ip_from 198.51.100.0/24;
set_real_ip_from 192.0.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

신뢰한 hop을 오른쪽에서 따라가며 주소를 고릅니다

직접 만든 주소 체인 예시입니다. 실제 proxy 구성이 아닙니다.
체인 요소주소 예시설정상 신뢰 여부판독
client203.0.113.44비신뢰체인 왼쪽에 있는 후보 client
내부 proxy192.0.2.18신뢰실제 구성에서 해당 proxy가 신뢰 범위에 있을 때만 통과
edge proxy / Nginx의 원래 peer198.51.100.27신뢰Nginx에 직접 연결한 마지막 proxy

가상 X-Forwarded-For 값이 203.0.113.44, 192.0.2.18이고 Nginx의 직접 peer가 198.51.100.27이라고 하겠습니다. 두 proxy 대역을 신뢰하고 real_ip_recursive on이면 오른쪽 신뢰 hop을 지나 마지막 비신뢰 주소 203.0.113.44가 선택됩니다. $remote_addr에는 처리 뒤 선택한 주소가, $realip_remote_addr에는 원래 peer인 198.51.100.27이 남습니다. 내부 proxy 주소를 신뢰 목록에서 빼면 탐색이 그곳에서 멈출 수 있습니다. 재귀 처리를 끄면 마지막으로 보낸 헤더 주소를 쓰므로 결과가 달라집니다.

헤더를 보내는 proxy와 받는 Nginx를 함께 확인합니다

real IP 모듈은 신뢰 목록과 header 값을 함께 사용합니다. upstream에 전달할 헤더는 Nginx의 proxy_set_header로 다시 정의하거나 추가할 수 있습니다. 하지만 이 지시문만으로 앞단 load balancer나 CDN이 어떤 값을 받았는지 알 수는 없습니다. ingress가 요청자가 보낸 X-Forwarded-For를 정리한 뒤 신뢰된 hop을 기록하는지 확인해야 합니다. 신뢰 CIDR에 실제 proxy가 아닌 넓은 고객망을 넣거나 필요한 hop을 빼면 주소가 잘못 계산될 수 있습니다.

  1. Nginx가 real IP 모듈을 포함해 빌드됐는지 확인합니다. 공식 문서상 이 모듈은 기본 빌드에 포함되지 않을 수 있습니다.
  2. 요청을 직접 전달하는 peer와 그 앞의 proxy 구간을 네트워크 구성 자료로 확인합니다.
  3. set_real_ip_from을 실제 신뢰 proxy 주소로만 한정하고 real_ip_header가 실제 전달 헤더와 일치하는지 검토합니다.
  4. real_ip_recursive 값을 확인한 뒤 교육용 체인처럼 오른쪽에서 신뢰·비신뢰 hop을 손으로 표시합니다.
  5. 로그의 $remote_addr뿐 아니라 원래 연결 peer인 $realip_remote_addr와 ingress의 헤더 처리 기록을 함께 비교합니다.

신뢰 범위를 넓게 잡으면 공격자 입력을 주소로 채택할 수 있습니다

첫 번째 실수는 set_real_ip_from 0.0.0.0/0;처럼 모든 송신지를 신뢰하는 식의 넓은 범위를 별 검증 없이 쓰는 것입니다. 그러면 요청자가 조작한 header가 신뢰된 교체 값처럼 취급될 위험이 커집니다. 두 번째는 모든 proxy가 같은 헤더 순서와 정규화 규칙을 쓴다고 가정하는 것입니다. 세 번째는 $remote_addr만 남기고 원래 peer 변수를 기록하지 않아 어느 proxy에서 온 요청인지 추적할 단서를 없애는 것입니다. 실제 로그 신뢰도는 Nginx 지시문만이 아니라 앞단 proxy의 동작과 운영자가 정한 CIDR 경계에도 달려 있습니다.

주소 체인과 신뢰 목록을 나란히 적습니다

조사표의 한쪽에는 연결 peer, X-Forwarded-For 각 항목을 오른쪽에서 왼쪽으로 적고, 다른 쪽에는 실제로 신뢰한다고 검증한 CIDR을 씁니다. 가장 오른쪽의 직접 peer가 신뢰되는지 먼저 확인하고, 신뢰되는 헤더 hop을 하나씩 건너뛰다가 처음 만난 비신뢰 주소를 표시합니다. 그 주소를 곧바로 사용자 신원으로 확정하지 말고 ingress가 헤더를 검증·정리했다는 별도 증거와 맞춰 보세요. 여기서 쓴 IPv4 대역은 설명을 위한 문서용 주소일 뿐, 사용할 CIDR을 제안하지 않습니다.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

직접 연결 peer 198.51.100.27과 헤더 주소 203.0.113.44, 192.0.2.18이 있고, 두 proxy 대역은 모두 set_real_ip_from으로 신뢰하며 real_ip_recursive on입니다. real_ip 모듈은 어떤 헤더 주소를 선택하나요? $realip_remote_addr는 무엇을 보존하나요?

정답과 해설 확인마지막 비신뢰 주소인 203.0.113.44를 선택합니다. $realip_remote_addr는 원래 연결 peer인 198.51.100.27을 보존합니다.

Nginx 문서는 recursive 검색이 켜져 있으면 원래 peer가 신뢰 주소인 경우 X-Forwarded-For 체인에서 마지막 비신뢰 주소를 사용한다고 설명합니다. realip_remote_addr 변수에는 real IP 처리 전 원래 client address가 남습니다. 이 결과는 신뢰 목록과 앞단 proxy의 헤더 처리 가정이 맞을 때만 유효합니다.

이 주제의 회상 연습 이어가기