Warum Java-Entwickler HTTP-Proxies benötigen
Wenn Sie Web-Scraping, API-Automatisierung oder verteilte Anwendungen in Java entwickeln, stoßen Sie unweigerlich auf IP-basierte Rate Limits, Geo-Restriktionen oder Bot-Erkennungssysteme. HTTP-Proxies sind dann keine Option mehr, sondern eine Notwendigkeit.
Java bietet mehrere HTTP-Client-Bibliotheken, jede mit eigener Proxy-Architektur. Dieser Guide zeigt Ihnen, wie Sie Proxies in Java 11+ HttpClient, OkHttp, Jsoup und Apache HttpClient konfigurieren – mit produktionsreifem Code, Authentifizierung, Connection Pooling und parallelem Scraping über einen Residential-Proxy-Pool.
Java 11+ HttpClient mit ProxySelector
Der moderne java.net.http.HttpClient (eingeführt in Java 11) bietet eine saubere, reactive API. Proxies werden über ProxySelector konfiguriert.
Einfache Proxy-Konfiguration
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class JavaHttpClientProxy {
public static void main(String[] args) throws Exception {
// Proxy-Selector für HTTP-Proxy konfigurieren
ProxySelector proxySelector = ProxySelector.of(
new InetSocketAddress("gate.proxyhat.com", 8080)
);
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.connectTimeout(java.time.Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/ip"))
.timeout(java.time.Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
}
}
Authentifizierte Proxies mit Authenticator
Die meisten kommerziellen Proxy-Dienste erfordern Authentifizierung. Java 11+ HttpClient verwendet Authenticator für Proxy-Credentials:
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class JavaHttpClientAuthProxy {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-DE";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws Exception {
// Authenticator für Proxy-Credentials
Authenticator proxyAuth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
}
return null;
}
};
ProxySelector proxySelector = ProxySelector.of(
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.authenticator(proxyAuth)
.connectTimeout(java.time.Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/ip"))
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Response: " + response.body());
}
}
Hinweis: Der Username bei ProxyHat kann Geo-Targeting-Flags enthalten, z.B.
user-country-USfür US-IPs oderuser-country-DE-city-berlinfür stadtbezogene IPs.
OkHttp mit Proxy und Authenticator
OkHttp von Square ist weit verbreitet und bietet exzellente Connection-Pooling- und Retry-Mechanismen. Die Proxy-Konfiguration erfolgt über OkHttpClient.Builder.
Grundlegende OkHttp Proxy-Konfiguration
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
public class OkHttpProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-US";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws IOException {
// Proxy definieren
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// Proxy-Authenticator
Authenticator proxyAuthenticator = new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(USERNAME, PASSWORD);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(Duration.ofSeconds(10).toMillis(), java.util.concurrent.TimeUnit.MILLISECONDS)
.readTimeout(Duration.ofSeconds(30).toMillis(), java.util.concurrent.TimeUnit.MILLISECONDS)
.writeTimeout(Duration.ofSeconds(30).toMillis(), java.util.concurrent.TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/ip")
.header("User-Agent", "Mozilla/5.0")
.get()
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("Status: " + response.code());
System.out.println("Body: " + response.body().string());
}
}
}
Connection Pooling und Retry-Policy
OkHttp verwaltet Connection Pools automatisch. Für produktive Scraping-Anwendungen sollten Sie Connection-Limits und Retry-Strategien explizit konfigurieren:
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
public class OkHttpProductionConfig {
public static OkHttpClient createOptimizedClient() {
// Connection Pool: max 50 idle connections, 5 min keep-alive
ConnectionPool connectionPool = new ConnectionPool(50, 5, TimeUnit.MINUTES);
// Proxy-Authenticator (wie im vorherigen Beispiel)
okhttp3.Authenticator proxyAuth = (route, response) -> {
String credential = okhttp3.Credentials.basic("user-country-DE", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
return new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("gate.proxyhat.com", 8080)))
.proxyAuthenticator(proxyAuth)
.connectionPool(connectionPool)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
// Interceptor für Logging/Retry-Logic
.addInterceptor(new RetryInterceptor(3))
.build();
}
// Custom Interceptor für Retry mit Exponential Backoff
static class RetryInterceptor implements okhttp3.Interceptor {
private final int maxRetries;
private int retryCount = 0;
RetryInterceptor(int maxRetries) {
this.maxRetries = maxRetries;
}
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
while (!response.isSuccessful() && retryCount < maxRetries) {
retryCount++;
try {
Thread.sleep((long) Math.pow(2, retryCount) * 1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
response.close();
response = chain.proceed(request);
}
return response;
}
}
}
Jsoup mit Proxy-Support für HTML-Parsing
Jsoup ist die Standardbibliothek für HTML-Parsing in Java. Für Web-Scraping müssen Sie Jsoup mit einem Proxy verbinden, bevor Sie HTML parsen.
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.IOException;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
public class JsoupProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-DE-city-berlin";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws IOException {
// Proxy konfigurieren
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT));
// Jsoup-Verbindung mit Proxy
Document doc = Jsoup.connect("https://example.com")
.proxy(proxy)
.header("Proxy-Authorization", java.util.Base64.getEncoder()
.encodeToString((USERNAME + ":" + PASSWORD).getBytes()))
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.timeout(30000)
.followRedirects(true)
.get();
// HTML parsen
String title = doc.title();
System.out.println("Title: " + title);
// Links extrahieren
for (Element link : doc.select("a[href]")) {
System.out.println("Link: " + link.attr("href") + " - " + link.text());
}
}
}
Wichtig: Jsoup's
.proxy()-Methode setzt den Proxy, aber bei authentifizierten Proxies müssen Sie denProxy-Authorization-Header manuell hinzufügen, da Jsoup keine eingebaute Authenticator-Unterstützung bietet.
Jsoup mit OkHttp als HTTP-Backend
Für produktive Anwendungen empfiehlt es sich, OkHttp für die HTTP-Verbindung zu nutzen und Jsoup nur für das Parsing:
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
public class JsoupOkHttpIntegration {
private final OkHttpClient httpClient;
public JsoupOkHttpIntegration() {
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("gate.proxyhat.com", 8080));
this.httpClient = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator((route, response) -> {
String credential = okhttp3.Credentials.basic("user-country-US", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
})
.build();
}
public Document fetchAndParse(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.header("User-Agent", "Mozilla/5.0")
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response.code());
}
String html = response.body().string();
return Jsoup.parse(html, url);
}
}
public static void main(String[] args) throws IOException {
JsoupOkHttpIntegration scraper = new JsoupOkHttpIntegration();
Document doc = scraper.fetchAndParse("https://news.ycombinator.com");
doc.select(".titleline > a").forEach(link -> {
System.out.println(link.text() + " -> " + link.attr("href"));
});
}
}
Apache HttpClient (Legacy-Ökosysteme)
Viele Enterprise-Systeme nutzen noch Apache HttpClient 4.x. Die Proxy-Konfiguration erfolgt über HttpHost und CredentialsProvider:
import org.apache.http.HttpHost;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.util.concurrent.TimeUnit;
public class ApacheHttpClientProxy {
public static void main(String[] args) throws Exception {
// Connection Pool Manager
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100);
cm.setDefaultMaxPerRoute(20);
// Proxy-Credentials
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new org.apache.http.auth.AuthScope("gate.proxyhat.com", 8080),
new org.apache.http.auth.UsernamePasswordCredentials("user-country-DE", "password")
);
// HTTP Client mit Proxy
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultCredentialsProvider(credsProvider)
.setProxy(new HttpHost("gate.proxyhat.com", 8080))
.build()) {
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
HttpGet request = new HttpGet("https://httpbin.org/ip");
request.setHeader("User-Agent", "Mozilla/5.0");
try (CloseableHttpResponse response = httpClient.execute(request, context)) {
String body = EntityUtils.toString(response.getEntity());
System.out.println("Response: " + body);
}
}
}
}
Vergleich der Java HTTP-Clients
| Feature | Java 11+ HttpClient | OkHttp | Apache HttpClient | Jsoup |
|---|---|---|---|---|
| Proxy-Unterstützung | ProxySelector + Authenticator | Proxy + proxyAuthenticator | HttpHost + CredentialsProvider | .proxy() + manueller Header |
| Connection Pooling | Eingebaut | ConnectionPool-Klasse | PoolingHttpClientConnectionManager | N/A (nutzt andere Clients) |
| Async-Unterstützung | Native (CompletableFuture) | Callback-basiert | N/A (nur sync) | N/A |
| HTTP/2 | Ja | Ja | Nein (4.x) | N/A |
| Wartungsstatus | Aktiv (JDK) | Aktiv | Wartungsmodus | Aktiv |
| Empfehlung | Beste Wahl für neue Projekte | Beste Features, weit verbreitet | Legacy-Systeme | Nur für Parsing, nicht für HTTP |
Paralleles Scraping mit Executors und Residential-Proxies
Für große Scraping-Aufgaben ist parallele Ausführung über mehrere Proxies essentiell. Residential-Proxies rotieren IPs automatisch, was ideale Bedingungen für gleichzeitige Requests schafft.
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelScrapingExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String BASE_USERNAME = "user-country-US-session-";
private static final String PASSWORD = "your_password";
private static final List<String> TARGETS = List.of(
"https://httpbin.org/ip",
"https://httpbin.org/headers",
"https://httpbin.org/user-agent",
"https://httpbin.org/get",
"https://httpbin.org/status/200"
);
// Thread-safe Counter für Session-IDs
private static final AtomicInteger sessionCounter = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
int threadCount = 5;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CompletionService<ScrapeResult> completionService =
new ExecutorCompletionService<>(executor);
// Tasks einreichen
for (String url : TARGETS) {
completionService.submit(() -> scrapeUrl(url));
}
// Ergebnisse sammeln
for (int i = 0; i < TARGETS.size(); i++) {
try {
Future<ScrapeResult> future = completionService.take();
ScrapeResult result = future.get();
System.out.println(result);
} catch (ExecutionException e) {
System.err.println("Task failed: " + e.getCause().getMessage());
}
}
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
}
private static ScrapeResult scrapeUrl(String url) throws IOException {
// Eindeutige Session-ID für jeden Request (IP-Rotation)
String sessionId = "sess" + sessionCounter.incrementAndGet();
String username = BASE_USERNAME + sessionId;
OkHttpClient client = createClient(username);
Request request = new Request.Builder()
.url(url)
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.get()
.build();
long startTime = System.currentTimeMillis();
try (Response response = client.newCall(request).execute()) {
long duration = System.currentTimeMillis() - startTime;
String body = response.body().string();
return new ScrapeResult(
url,
response.code(),
duration,
body.substring(0, Math.min(200, body.length()))
);
}
}
private static OkHttpClient createClient(String username) {
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT));
return new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator((route, response) -> {
String credential = okhttp3.Credentials.basic(username, PASSWORD);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
})
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
record ScrapeResult(String url, int statusCode, long durationMs, String preview) {
@Override
public String toString() {
return String.format("[%d ms] %s -> %d: %s",
durationMs, url, statusCode, preview);
}
}
}
ProxyHat Session-Rotation: Mit
user-session-abc123im Username behalten Sie eine feste IP für die Session. Neue Session-IDs bedeuten neue IPs – ideal für Rate-Limit-Umgehung.
TLS/SSL-Konfiguration und Custom SSLContext
Manche Upstream-Server nutzen selbstsignierte Zertifikate oder nicht-Standard-TLS-Konfigurationen. In diesen Fällen müssen Sie einen custom SSLContext konfigurieren.
Custom TrustManager für Entwicklungsumgebungen
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.http.HttpClient;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class TlsConfiguration {
/**
* WARNUNG: Nur für Entwicklung/Testumgebungen verwenden!
* Akzeptiert alle Zertifikate ohne Validierung.
*/
public static SSLContext createTrustAllContext() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext;
}
public static HttpClient createHttpClientWithCustomTls() throws Exception {
return HttpClient.newBuilder()
.sslContext(createTrustAllContext())
.connectTimeout(java.time.Duration.ofSeconds(10))
.build();
}
}
OkHttp mit Custom SSLContext
import okhttp3.OkHttpClient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class OkHttpTlsConfig {
public static OkHttpClient createTrustAllClient() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
.hostnameVerifier((hostname, session) -> true)
.build();
}
}
Sicherheitshinweis: Deaktivieren Sie SSL-Validierung niemals in Produktionsumgebungen. Nutzen Sie stattdessen einen lokalen TrustStore mit Ihren internen CA-Zertifikaten.
Best Practices für produktive Java-Proxy-Anwendungen
- Connection Pooling: Nutzen Sie immer Connection Pools, um TCP-Handshake-Overhead zu reduzieren. OkHttp verwaltet dies automatisch, Java 11+ HttpClient ebenfalls.
- Timeouts: Setzen Sie explizite Connect-, Read- und Write-Timeouts. Netzwerkbedingungen variieren, besonders über Proxy-Netzwerke.
- Retry-Logik: Implementieren Sie Exponential Backoff. Ein einfacher Retry-Interceptor reicht oft aus.
- Session-Management: Nutzen Sie Session-IDs im Proxy-Username für sticky Sessions, wenn Sie konsistente IPs benötigen.
- Rate Limiting: Respektieren Sie robots.txt und implementieren Sie client-seitige Rate Limits zwischen Requests.
- Error Handling: Unterscheiden Sie zwischen Proxy-Fehlern (407, Proxy-Timeouts) und Target-Server-Fehlern (403, 429, 500).
- Logging: Loggen Sie Proxy-IPs, Response-Codes und Latenzzeiten für Debugging und Optimierung.
Key Takeaways
- Java 11+ HttpClient bietet native Proxy-Unterstützung via
ProxySelectorundAuthenticator– ideal für neue Projekte. - OkHttp ist die beste Wahl für produktive Anwendungen mit Connection Pooling, Retry-Logik und flexibler Proxy-Konfiguration.
- Jsoup sollte nur für HTML-Parsing verwendet werden; nutzen Sie OkHttp oder HttpClient für die HTTP-Verbindung.
- Apache HttpClient ist nur noch für Legacy-Systeme relevant; neue Projekte sollten Java 11+ HttpClient oder OkHttp bevorzugen.
- Paralleles Scraping mit Executors und Residential-Proxies mit Session-IDs ermöglicht hohe Durchsatzraten ohne IP-Blocks.
- TLS-Konfiguration ist für nicht-Standard-Upstream-Server wichtig; produktive Anwendungen sollten eigene TrustStores nutzen.
Für weitere Informationen zu Proxy-Features wie Geo-Targeting und IP-Rotation besuchen Sie unsere Web-Scraping Use Cases oder schauen Sie sich die ProxyHat Pricing-Optionen an.






