Java IP Geolocation: Retrieve Client Location Using ip2region
This guide explains how to obtain a client’s IP address behind proxies in Java, choose appropriate HTTP headers, and use the open‑source ip2region library to query offline databases for accurate, millisecond‑level IP geolocation, including code examples for Spring Boot integration.
Client IP Retrieval
In a typical Java web application the HttpServletRequest object provides request metadata. request.getRemoteAddr() returns the address of the immediate sender, which is often a reverse proxy such as Nginx. To obtain the original client IP the proxy adds headers such as X-Forwarded-For, X-Real-IP, Proxy-Client-IP, WL-Proxy-Client-IP, and HTTP_CLIENT_IP. The first IP in X-Forwarded-For is usually the real client IP.
@Slf4j
public class IpUtils {
private static final String UNKNOWN_VALUE = "unknown";
private static final String LOCALHOST_V4 = "127.0.0.1";
private static final String LOCALHOST_V6 = "0:0:0:0:0:0:0:1";
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
private static final String X_REAL_IP = "X-Real-IP";
private static final String PROXY_CLIENT_IP = "Proxy-Client-IP";
private static final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";
private static final String HTTP_CLIENT_IP = "HTTP_CLIENT_IP";
/**
* Get client IP address
*/
public static String getRemoteHost(HttpServletRequest request) {
String ip = request.getHeader(X_FORWARDED_FOR);
if (StringUtils.isNotEmpty(ip) && !UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader(X_REAL_IP);
if (StringUtils.isNotEmpty(ip) && !UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
return ip;
}
if (StringUtils.isBlank(ip) || UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
ip = request.getHeader(PROXY_CLIENT_IP);
}
if (StringUtils.isBlank(ip) || UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
ip = request.getHeader(WL_PROXY_CLIENT_IP);
}
if (StringUtils.isBlank(ip) || UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (StringUtils.isBlank(ip) || UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
ip = request.getHeader(HTTP_CLIENT_IP);
}
if (StringUtils.isBlank(ip) || UNKNOWN_VALUE.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.equals(LOCALHOST_V6) ? LOCALHOST_V4 : ip;
}
}IP Geolocation with ip2region
After obtaining the client IP, an offline database ip2region.xdb can resolve its geographic location. The open‑source ip2region project provides a database with 99.9% accuracy, sub‑millisecond query speed, and a file size of only a few megabytes.
Features of ip2region
Standardized data format : each record stores Country|Region|Province|City|ISP. Chinese entries are usually city‑level; other countries may contain only country information.
Deduplication and compression : the xdb generator removes duplicate entries; the full database is about 11 MiB.
Ultra‑fast query : pure file‑based queries take tens of microseconds. Two caching options accelerate performance:
Vector index cache (≈512 KiB) reduces one disk I/O, keeping average latency 10‑20 µs.
Full file cache loads the entire xdb into memory, eliminating disk I/O and achieving microsecond‑level latency.
Customizable region field : the v2.0 format allows user‑defined extra fields (e.g., GPS, postal code).
High accuracy : data aggregated from major providers (Taobao > 80%, GeoIP ≈ 10%, CZ88 ≈ 2%) yields a tested 99.9% correctness rate.
Java Client Usage
Add the Maven dependency:
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.5</version>
</dependency>Query Modes
File‑only query (no caching)
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
", dbPath, e);
return;
}
try {
String ip = "1.2.3.4";
long sTime = System.nanoTime();
String region = searcher.search(ip);
long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
System.out.printf("{region: %s, ioCount: %d, took: %d μs}
", region, searcher.getIOCount(), cost);
} catch (Exception e) {
System.out.printf("failed to search(%s): %s
", ip, e);
}
searcher.close();
}
}Vector‑index cache query
byte[] vIndex;
try {
vIndex = Searcher.loadVectorIndexFromFile(dbPath);
} catch (Exception e) {
System.out.printf("failed to load vector index from `%s`: %s
", dbPath, e);
return;
}
Searcher searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
// perform search as in the file‑only exampleFull‑memory cache query
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
System.out.printf("failed to load content from `%s`: %s
", dbPath, e);
return;
}
Searcher searcher = Searcher.newWithBuffer(cBuff);
// perform search as in the file‑only exampleBoth cached approaches are safe for concurrent use; each thread should create its own Searcher instance while sharing the same cached data.
Spring Boot Integration Example
Load the entire ip2region.xdb into a static byte array at application startup and expose a utility method that returns a structured IpRegion object.
@Slf4j
public class IpUtils {
private static final String IP_DATA_PATH = "/Users/shepherdmy/Desktop/ip2region.xdb";
private static byte[] contentBuff;
static {
try {
contentBuff = Searcher.loadContentFromFile(IP_DATA_PATH);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Query IP region, format: China|0|Zhejiang|Hangzhou|Telecom
*/
public static IpRegion getIpRegion(String ip) {
Searcher searcher = null;
IpRegion ipRegion = new IpRegion();
try {
searcher = Searcher.newWithBuffer(contentBuff);
String region = searcher.search(ip);
String[] info = StringUtils.split(region, "|");
ipRegion.setCountry(info[0]);
ipRegion.setArea(info[1]);
ipRegion.setProvince(info[2]);
ipRegion.setCity(info[3]);
ipRegion.setIsp(info[4]);
} catch (Exception e) {
log.error("get ip region error: ", e);
} finally {
if (searcher != null) {
try { searcher.close(); } catch (IOException e) { log.error("close searcher error:", e); }
}
}
return ipRegion;
}
}This utility can be injected into Spring services to provide real‑time IP geolocation without external network calls.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Shepherd Advanced Notes
Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
