Backend Development 15 min read

Retrieving Client IP and Geolocation in Java with HttpServletRequest and ip2region

This article explains how to obtain a user's IP address from an HttpServletRequest in Java, convert it to geographic location using the ip2region library, and provides detailed code examples, Maven setup, caching strategies, and performance testing procedures for backend developers.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Retrieving Client IP and Geolocation in Java with HttpServletRequest and ip2region

Many Chinese platforms now display a user's IP‑derived location, prompting backend developers to implement similar functionality; this guide shows how to retrieve the client IP from an HttpServletRequest and map it to province and city information.

The process consists of two steps: first, extract the raw IP address from request headers, handling proxies and local addresses; second, query an offline IP‑to‑region database to obtain location details.

A utility class NetworkUtil is provided to encapsulate the IP extraction logic, handling headers such as x-forwarded-for , Proxy-Client-IP , WL-Proxy-Client-IP , HTTP_CLIENT_IP , and HTTP_X_FORWARDED_FOR , with fallback to request.getRemoteAddr() and special handling for localhost.

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;

public class NetworkUtil {
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if ("localhost".equalsIgnoreCase(ip) || "127.0.0.1".equalsIgnoreCase(ip) || "0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)) {
            try {
                InetAddress inet = InetAddress.getLocalHost();
                ip = inet.getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
        if (ip != null && ip.length() > 15 && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }
        return ip;
    }

    public static String getMacAddress() throws Exception {
        byte[] macAddressBytes = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).getHardwareAddress();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < macAddressBytes.length; i++) {
            if (i != 0) sb.append("-");
            String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
            sb.append(s.length() == 1 ? "0" + s : s);
        }
        return sb.toString().trim().toUpperCase();
    }
}

Previously the author used the Taobao IP library (now discontinued) and switched to the open‑source ip2region project, which offers an offline binary XDB database and fast lookup implementations for many languages.

ip2region (v2.0) provides a standardized region format (country|area|province|city|ISP), data deduplication/compression, microsecond‑level query latency, and support for custom fields. It ships bindings for Java, C#, C, Go, Rust, Python, Node.js, PHP, Lua, and Nginx.

The Java client is added via Maven:

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.6.4</version>
</dependency>

Typical usage creates a Searcher object, loads the XDB file, and calls search(ip) . Two caching strategies are demonstrated: loading only the vector index for fast file‑based queries, and loading the entire XDB into memory for maximum throughput.

import org.lionsoul.ip2region.xdb.Searcher;
import java.io.*;
import java.util.concurrent.TimeUnit;

public class SearcherTest {
    public static void main(String[] args) {
        String dbPath = "ip2region.xdb file path";
        Searcher searcher = null;
        try {
            searcher = Searcher.newWithFileOnly(dbPath);
        } catch (IOException e) {
            System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e);
            return;
        }
        try {
            String ip = "1.2.3.4";
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
        } catch (Exception e) {
            System.out.printf("failed to search(%s): %s\n", ip, e);
        }
    }
}

For higher performance, the vector index can be pre‑loaded and reused across threads, or the whole XDB can be loaded into a byte buffer and shared globally.

After building the Java binding with mvn compile package , the resulting JAR can be used to run interactive search or benchmark commands, e.g., java -jar target/ip2region-2.6.0.jar search --db=../../data/ip2region.xdb and java -jar target/ip2region-2.6.0.jar bench --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt , which report microsecond‑level query times.

The article concludes that with ip2region v2.0, developers can reliably obtain IP‑based location data without external services, and encourages readers to explore the GitHub repository for further details.

BackendJavanetworkHttpServletRequestGeolocationIP addressip2region
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.