이번 프로젝트를 진행하면서 통합 테스트 중 고객이 테스트를 하는 도중에 요청을 주셨다.
"제 로컬 PC로 접속했을 때랑 VPN을 연결해서 접속했을 때, 찍히는 IP 주소가 다른데 이게 정상인가요?
X-Forwarded-For 한번 확인 요청드립니다!"
이 요청을 보고 첫번째로 든 생각은 "VPN으로 접속했으면 IP 주소가 다른게 정상 아닌가?" 가 첫번째였고,
"근데 왜 정상이지?" 가 두번째였다.
나는 그냥 "VPN을 연결하면 VPN의 IP 주소로 접속되어서 IP 주소가 다르다" 만 알고있고
그 안에 자세한 내용, XFF 조차도 모르고 있었던 것이었다.
✅X-Forwarded-For (XFF) 란?
X-Forwarded-For 는 원래 요청을 보낸 클라이언트의 IP 주소를 전달하기 위한 HTTP 헤더이다.
보통 클라이언트와 서버 사이에 아래와 같은 중간 장비가 있을 때 사용된다.
- 프록시 서버
- 로드밸런서
- 리버스 프록시
- VPN 게이트웨이
- 웹 방화벽 (WAF)
이런 장비를 거쳐 서버에 요청이 들어오면,
서버 입장에서는 직접 연결한 대상이 최종 사용자가 아니라 중간 장비로 보일 수 있다.
이때 중간 장비가 원래 클라이언트 IP를 헤더에 담아서 넘겨주는데,
그 대표적인 값이 X-Forwarded-For 다.
✅소스코드
현재 IP를 구하는 코드가 이렇게 구현이 되어있다.
private String getIp() {
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
String ip = req.getHeader("X-FORWARDED-FOR");
return ip == null ? req.getRemoteAddr() : ip;
}
코드 동작 방식
1. X-Forwarded-For 헤더가 있으면 그 값을 사용
2. 없으면 getRemoteAddr() 값을 사용
즉,
프록시나 VPN 같은 중간 장비가 실제 사용자 IP를 전달해주면 XFF를 사용하고,
그렇지 않으면 서버와 직접 연결된 클라이언트 주소를 사용하는 방식
getRemoteAddr() 만으로는 부족할 수 있는 이유
request.getRemoteAddr() 는 서버와 직접 TCP 연결을 맺은 상대의 IP를 반환한다.
중요한 점은 이 값이 항상 "실제 사용자 PC의 IP"를 뜻하지는 않는다는 것이다.
중간에 프록시나 VPN 장비가 있으면 서버는 그 장비와 통신하기 때문에,
getRemoteAddr() 는 사용자 IP가 아니라 중간 장비의 IP가 될 수 있다.
즉,
- 중간 장비 없음 → getRemoteAddr() = 사용자 IP
- 중간 장비 있음 → getRemoteAddr() = 프록시/VPN/게이트웨이 IP
✅로컬 접속과 VPN 접속에서 IP가 달라지는 이유
1. 로컬 PC에서 직접 접속하는 경우
사용자가 별도의 프록시나 VPN 없이 서버에 접속한다고 가정해보면,
사용자 PC ----------------------> 서버
이 경우 서버가 직접 통신하는 대상은 사용자 PC 다.
따라서,
- X-Forwarded-For 없음
- getRemoteAddr() = 실제 접속한 사용자 IP
즉, 이 상황에서는 getRemoteAddr() 값이 곧 원격 사용자 IP라고 볼 수 있다.
2. VPN을 통해 접속하는 경우
이번에는 사용자가 VPN을 켠 상태에서 접속한다고 가정해보면,
사용자 PC ----------------------------> VPN 장비 ---------------------------> 서버
이때 서버는 사용자 PC와 직접 통신하지 않는다.
서버가 직접 보는 대상은 VPN 장비다.
따라서,
- getRemoteAddr() = VPN 게이트웨이 또는 내부 네트워크 장비 IP
- X-Forwarded-For = 원래 사용자 ip가 전달될 수 있음
즉, VPN 환경에서는
서버 입장에서 "중간 장비를 통해 들어온 요청" 으로 보이기 때문에
직접 접속 때와 IP가 달라지는 것이 정상이다.
⚠️번외
1️⃣주의할 점 : X-Forwarded-For 를 무조건 믿으면 안된다.
여기서 중요한 포인트가 하나 있다.
X-Forwarded-For 는 HTTP 헤더이기 때문에,
신뢰할 수 없는 외부 요청에서는 클라이언트가 임의로 넣을 수도 있다.
즉, 모든 환경에서 무조건
req.getHeader("X-FORWARDED-FOR")
를 신뢰하면 안 된다.
신뢰 가능한 경우
- 사내 프록시
- 운영 중인 로드밸런서
- 회사 VPN 게이트웨이
- 신뢰 가능한 리버스 프록시
주의가 필요한 경우
- 인터넷에서 직접 들어오는 요청
- 프록시 신뢰 구간이 명확하지 않은 환경
- 헤더 위변조 가능성이 있는 환경
실무에서는 보통
"신뢰할 수 있는 프록시가 추가한 XFF만 사용한다"
는 기준이 필요하다.
2️⃣X-Forwarded-For 에는 여러 IP가 들어올 수도 있다.
프록시를 여러 번 거치면 X-Forwarded-For 에는 IP가 하나만 들어오지 않을 수 있다.
X-Forwarded-For: client-ip, proxy1-ip, proxy2-ip
일반적으로는 맨 앞의 값이 원래 클라이언트 IP인 경우가 많다.
하지만 이것도 인프라 구성에 따라 다를 수 있으므로
운영 환경의 프록시 정책을 같이 확인해야 한다.
'Server' 카테고리의 다른 글
| [Elasticsearch] Elasticsearch 란? (역인덱스, 애널라이저) (0) | 2026.03.31 |
|---|---|
| [Network] SSL 인증서를 갱신하는 방법을 알아보자 (0) | 2026.03.20 |