혜야의 코딩스토리

[Java] 클라이언트 IP 가져오기: Apache 웹서버와 리버스 프록시 환경에서 본문

꿈 : 멋진 개발자 🧸/Java

[Java] 클라이언트 IP 가져오기: Apache 웹서버와 리버스 프록시 환경에서

hyeya_ 2025. 3. 7. 13:25

1. 문제 발견

접속 IP에 따라 접근을 허용/차단하는 기능을 개발하면서 이상한 점을 발견했습니다.

✅ 초기 코드

import javax.servlet.http.HttpServletRequest;

request.getRemoteAddr();

❌ 예상과 다른 결과

어떤 IP로 접속하든 항상 같은 IP가 찍혔습니다. 예를 들어:

99.1.99.2 - - [07/Mar/2025:13:01:03 +0900] "HEAD / HTTP/1.1" 200 - "-" "-"

Apache 웹서버의 로그에서도 클라이언트 IP가 아닌 99.1.99.2만 기록되었습니다.


2. 원인 분석: 리버스 프록시와 로드 밸런서

현재 우리는 리버스 프록시와 로드 밸런서를 사용 중이므로, 요청이 여러 장비를 거쳐 들어옵니다.

🔍 원인

  • 실제 클라이언트는 X-Forwarded-For 같은 헤더에 IP를 포함해서 요청을 보냅니다.
  • 하지만 Apache 웹서버는 마지막으로 요청을 보낸 장비(리버스 프록시)의 IP만 기록합니다.
  • 따라서, 99.1.99.2는 리버스 프록시의 내부 IP일 가능성이 큽니다.

❓ Apache는 왜 X-Forwarded-For을 로그에 기록하지 않을까?

기본적으로 Apache의 access_log는 클라이언트의 IP만 기록합니다. 따라서 설정을 변경하지 않으면 리버스 프록시의 IP만 남습니다.

✅ 해결 방법: LogFormat 수정

Apache 로그에 실제 클라이언트 IP를 남기려면 LogFormat을 수정해야 합니다.

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

이렇게 설정하면 X-Forwarded-For 값이 로그에 기록됩니다.


3. 해결 방법: 클라이언트 IP 가져오기

✅ 수정된 코드

public static String getRemoteAddr(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");

    if (ip == null || ip.isEmpty()) {
        ip = request.getRemoteAddr();
    } else {
        // X-Forwarded-For에 여러 개의 IP가 있을 수 있으므로 첫 번째 IP를 선택
        String[] remoteAddrArray = ip.split(",");
        ip = remoteAddrArray[0].trim();

        // 포트번호가 포함되어 있으면 제거
        if (ip.contains(":")) {
            ip = ip.split(":")[0];
        }
    }
    return ip;
}

⚠️ 주의할 점

이 방식은 리버스 프록시나 로드 밸런서가 X-Forwarded-For 헤더를 올바르게 설정한 경우에만 정상적으로 동작합니다.

예를 들어, X-Forwarded-For 값이 다음과 같이 들어올 수 있습니다:

192.168.0.1, 99.1.99.2

이 경우, 첫 번째 IP(192.168.0.1)가 실제 클라이언트 IP이므로 이를 추출해야 합니다.


4. 결론

  • 리버스 프록시나 로드 밸런서를 사용하면 request.getRemoteAddr()는 실제 클라이언트가 아니라 프록시 서버의 IP를 반환함.
  • 클라이언트 IP를 정확하게 가져오려면 X-Forwarded-For 헤더를 확인해야 함.
  • Apache 로그에도 클라이언트 IP를 기록하려면 LogFormat 설정을 수정해야 함.