Proxies in Kotlin verwenden – die Grundlagen
Wenn Sie Android-Apps oder Kotlin-Backends entwickeln und HTTP-Anfragen über einen Proxy leiten müssen, stehen Sie schnell vor engine-spezifischen Eigenheiten: Ktor abstrahiert den Proxy-Transport, aber Proxy-Authentifizierung ist je nach Engine unterschiedlich gelöst. OkHttp bietet zwar eine saubere java.net.Proxy-API, erfordert aber einen eigenen Authenticator für 407-Herausforderungen. Dieser Guide zeigt, wie Sie Proxies in Kotlin produktiv einsetzen – von der Projekt-Einrichtung über Geo-Targeting und Sticky Sessions bis hin zu Coroutine-basiertem Fan-out mit Rate-Limiting.
Wir verwenden ProxyHat als Beispiel-Provider. Die Verbindungsdetails sind universell: Gateway gate.proxyhat.com, HTTP-Port 8080, SOCKS5-Port 1080. Geo- und Session-Flags werden direkt im Benutzernamen kodiert, was die Konfiguration in Kotlin besonders elegant macht.
Warum residierende Proxies für Kotlin-Web-Scraping?
Viele App- und Social-Media-Ziele blockieren Anfragen aus Datacenter-ASNs aggressiv. Eine Autonome System (AS)-basierte Filterung kann Datacenter-IP-Ranges mit über 99% Trefferquote erkennen, bevor die Anwendung überhaupt eine HTTP-Antwort generiert. Residierende Proxies routen Anfragen durch echte Endnutzer-IPs aus ISP-Bereichen und sind daher wesentlich robuster gegen ASN-Blockaden.
Beim Kotlin Web Scraping mit hoher Parallelität kommt es nicht nur auf die IP-Qualität an, sondern auch auf Verbindungspooling, Timeout-Management und Retry-Logik. Ktor und OkHttp bieten hierfür unterschiedliche Abstraktionen – beide lassen sich jedoch mit ProxyHat kombinieren. Mehr zu den Unterschieden zwischen Proxy-Typen finden Sie auf unserer Proxy-Standorte-Seite.
Projekt-Setup: Ktor 3 und OkHttp
Für Ktor 3 benötigen Sie die CIO- oder OkHttp-Engine. Die OkHttp-Engine ist besonders nützlich, wenn Sie bereits OkHttp-spezifische Features wie Authenticator oder Interceptors nutzen wollen. Hier ist die build.gradle.kts-Konfiguration:
// build.gradle.kts
plugins {
kotlin("jvm") version "2.0.21"
application
}
dependencies {
implementation("io.ktor:ktor-client-core:3.0.3")
implementation("io.ktor:ktor-client-okhttp:3.0.3")
implementation("io.ktor:ktor-client-content-negotiation:3.0.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.3")
implementation("com.squareup.okhttp3:okhttp:4.12.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
implementation("org.slf4j:slf4j-simple:2.0.16")
}
Raw OkHttp-Baseline mit java.net.Proxy
Bevor wir Ktor konfigurieren, zeigen wir die nackte OkHttp-Variante. Dies ist nützlich als Referenz und für Library-Maintainer, die keinen Ktor-Stack nutzen:
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.InetSocketAddress
import java.net.Proxy
import java.net.URI
import java.util.Base64
fun buildOkHttpProxyClient(
proxyHost: String = "gate.proxyhat.com",
proxyPort: Int = 8080,
username: String,
password: String
): OkHttpClient {
val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyHost, proxyPort))
val authHeader = "Basic " + Base64.getEncoder()
.encodeToString("$username:$password".toByteArray())
return OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator { _, response ->
// Reagiert auf 407 Proxy Authentication Required
response.request.newBuilder()
.header("Proxy-Authorization", authHeader)
.build()
}
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
}
fun main() {
val username = "user-country-DE-city-berlin-session-abc123"
val password = "your-api-key"
val client = buildOkHttpProxyClient(username = username, password = password)
val request = Request.Builder()
.url("https://httpbin.org/ip")
.build()
client.newCall(request).execute().use { response ->
println(response.body?.string())
}
}
Beachten Sie, dass der proxyAuthenticator für OkHttp Proxy-Authentifizierung zwingend ist – ohne ihn erhalten Sie bei jedem Request einen 407-Status. Der Authenticator wird nur bei Bedarf aufgerufen, was Overhead spart.
Ktor 3 HttpClient mit OkHttp-Engine
In Ktor ist die Proxy-Konfiguration engine-spezifisch. Die OkHttp-Engine nutzt intern denselben OkHttpClient.Builder, aber Proxy-Authentifizierung wird am besten über defaultRequest gelöst:
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.Base64
fun buildKtorProxyClient(
username: String,
password: String,
proxyHost: String = "gate.proxyhat.com",
proxyPort: Int = 8080
): HttpClient {
val authHeader = "Basic " + Base64.getEncoder()
.encodeToString("$username:$password".toByteArray())
return HttpClient(OkHttp) {
engine {
proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyHost, proxyPort))
config {
retryOnConnectionFailure(true)
connectTimeout(10_000, java.util.concurrent.TimeUnit.MILLISECONDS)
readTimeout(30_000, java.util.concurrent.TimeUnit.MILLISECONDS)
}
}
defaultRequest {
header("Proxy-Authorization", authHeader)
}
}
}
suspend fun main() {
val username = "user-country-DE-city-berlin"
val password = "your-api-key"
val client = buildKtorProxyClient(username, password)
val response: HttpResponse = client.get("https://httpbin.org/ip")
println(response.bodyAsText())
client.close()
}
Wichtig: Ktor's
defaultRequest-Block setzt denProxy-Authorization-Header auf jede Anfrage. Die CIO-Engine unterstützt diese Methode ebenfalls, hat aber keine interneOkHttpClient.Builder-Konfiguration – dort müssen Sie den Header manuell setzen.
Geo-Targeting und Sticky Sessions im Benutzernamen
ProxyHat kodiert Geo-Targeting und Session-Stickiness direkt im Benutzernamen. Das ist besonders in Kotlin elegant, weil Sie String-Templates nutzen können:
fun buildUsername(
country: String? = null,
city: String? = null,
session: String? = null
): String {
val parts = mutableListOf("user")
country?.let { parts.add("country-$it") }
city?.let { parts.add("city-$it") }
session?.let { parts.add("session-$it") }
return parts.joinToString("-")
}
// Beispiele:
// buildUsername(country = "US") -> "user-country-US"
// buildUsername(country = "DE", city = "berlin") -> "user-country-DE-city-berlin"
// buildUsername(session = "abc123") -> "user-session-abc123"
// buildUsername(country = "DE", city = "berlin", session = "order-42")
// -> "user-country-DE-city-berlin-session-order-42"
Sticky Sessions halten dieselbe Exit-IP für die Dauer der Session aufrecht – ideal für Login-Flows oder mehrstufige Checkout-Prozesse, bei denen IP-Wechsel zwischen Requests verdächtig wirken. Die Proxy-Authorization-Header-Spezifikation definiert das Basic-Auth-Schema, das wir hier verwenden.
SOCKS5 auf Port 1080
Für SOCKS5-Verbindungen nutzt ProxyHat Port 1080. In Kotlin/JVM setzen Sie die System-Properties socksProxyHost, socksProxyPort und optional java.net.socks.username / java.net.socks.password:
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
fun configureSocks5SystemProperties(password: String) {
System.setProperty("socksProxyHost", "gate.proxyhat.com")
System.setProperty("socksProxyPort", "1080")
System.setProperty("java.net.socks.username", "user-country-DE")
System.setProperty("java.net.socks.password", password)
}
suspend fun main() {
configureSocks5SystemProperties("your-api-key")
val client = HttpClient(CIO) {
engine {
// CIO respektiert JVM-Proxy-System-Properties
}
defaultRequest {
header("User-Agent", "MyKotlinApp/1.0")
}
}
val response = client.get("https://httpbin.org/ip")
println(response.bodyAsText())
client.close()
}
Beachten Sie, dass System-Properties pro JVM-Prozess global sind. In Container-Umgebungen mit mehreren Proxy-Routen ist es besser, OkHttp direkt mit Proxy(Proxy.Type.SOCKS, ...) zu konfigurieren und einen Authenticator zu registrieren, der java.net.PasswordAuthentication zurückgibt:
import java.net.Authenticator
import java.net.InetSocketAddress
import java.net.PasswordAuthentication
import java.net.Proxy
fun buildSocks5Proxy(
username: String,
password: String,
host: String = "gate.proxyhat.com",
port: Int = 1080
): Proxy {
Authenticator.setDefault(object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication {
return if (requestingScheme.equals("SOCKS", ignoreCase = true)) {
PasswordAuthentication(username, password.toCharArray())
} else {
super.getPasswordAuthentication()
}
}
})
return Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port))
}
Coroutine-Fan-out mit Semaphore-Rate-Control
Residierende Proxies sind nicht unbegrenzt schnell – typische Latenzen liegen bei 200–800 ms pro Request. Wenn Sie Kotlin Web Scraping mit hoher Parallelität betreiben, müssen Sie die Concurrency begrenzen, sonst laufen Sie gegen Rate-Limits des Targets oder des Proxy-Providers. Die Idiom- Wahl in Kotlin ist async / awaitAll kombiniert mit einem Semaphore:
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.Base64
data class ScrapeResult(val url: String, val status: Int, val body: String)
suspend fun fanOutRequests(
urls: List<String>,
maxConcurrency: Int = 20,
proxyUsername: String,
proxyPassword: String
): List<Result<ScrapeResult>> {
val semaphore = Semaphore(maxConcurrency)
val authHeader = "Basic " + Base64.getEncoder()
.encodeToString("$proxyUsername:$proxyPassword".toByteArray())
val client = HttpClient(OkHttp) {
engine {
proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("gate.proxyhat.com", 8080))
config {
connectTimeout(10_000, java.util.concurrent.TimeUnit.MILLISECONDS)
readTimeout(30_000, java.util.concurrent.TimeUnit.MILLISECONDS)
}
}
defaultRequest {
header("Proxy-Authorization", authHeader)
}
}
return coroutineScope {
val deferreds = urls.map { url ->
async(Dispatchers.IO) {
semaphore.withPermit {
runCatching {
var attempt = 0
var lastError: Throwable? = null
while (attempt < 3) {
try {
val response = client.get(url)
return@runCatching ScrapeResult(
url = url,
status = response.status.value,
body = response.bodyAsText()
)
} catch (e: Exception) {
lastError = e
delay(500L * (attempt + 1)) // exponential backoff
attempt++
}
}
throw lastError ?: RuntimeException("Unknown error")
}
}
}
}
deferreds.awaitAll().also { client.close() }
}
}
suspend fun main() = coroutineScope {
val urls = (1..100).map { "https://httpbin.org/delay/1?id=$it" }
val results = fanOutRequests(
urls = urls,
maxConcurrency = 20,
proxyUsername = "user-country-DE-session-batch-${System.currentTimeMillis()}",
proxyPassword = "your-api-key"
)
val successes = results.count { it.isSuccess }
println("Erfolgreich: $successes / ${results.size}")
}
Hier steuert das Semaphore(20) die maximale Anzahl gleichzeitiger Requests. Mit 100 concurrent Sessions und 20 als Concurrency-Limit dauert eine Batch von 100 Requests typischerweise 5–8 Sekunden bei 1-Sekunden-Server-Verzögerung. Der Retry-Loop mit exponentiellem Backoff fängt transiente Proxy-Fehler ab.
Produktions-Härtung
OkHttp Authenticator für 407-Challenges
In Produktion kann der Proxy-Server gelegentlich einen 407 Proxy Authentication Required zurückgeben – etwa bei Session-Timeouts. Der Authenticator-Callback von OkHttp ist der richtige Ort, um darauf zu reagieren:
import okhttp3.Authenticator
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.Route
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.Base64
fun buildHardenedOkHttp(
username: String,
password: String
): OkHttpClient {
val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("gate.proxyhat.com", 8080))
var currentAuth = "Basic " + Base64.getEncoder()
.encodeToString("$username:$password".toByteArray())
val authenticator = Authenticator { _: Route?, response: Response ->
if (response.code == 407) {
// Neue Session-ID generieren bei Auth-Failure
val newSession = "session-${System.currentTimeMillis()}"
val newUser = username.substringBefore("-session-") + "-$newSession"
currentAuth = "Basic " + Base64.getEncoder()
.encodeToString("$newUser:$password".toByteArray())
response.request.newBuilder()
.header("Proxy-Authorization", currentAuth)
.build()
} else {
null // Bei 401 etc. nicht erneut versuchen
}
}
return OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(authenticator)
.connectionPool(okhttp3.ConnectionPool(20, 5, java.util.concurrent.TimeUnit.MINUTES))
.build()
}
Timeouts und Connection Pooling
Setzen Sie immer beide Timeout-Typen: connectTimeout für den TCP-Handshake zum Proxy und readTimeout für die Antwort des Targets. Residierende Proxies haben höhere Latenzen als Datacenter-Proxies – ein connectTimeout von 10 Sekunden und ein readTimeout von 30 Sekunden sind solide Defaults. Für Connection-Pooling empfiehlt sich ein Pool von 20 Verbindungen mit 5-Minuten-Keepalive.
TLS-Konfiguration
Bei HTTPS-Anfragen durch einen HTTP-Proxy findet ein CONNECT-Tunnel statt – der Proxy terminiert nicht das TLS, sondern leitet den verschlüsselten Stream weiter. Dennoch sollten Sie sicherstellen, dass Ihre TLS-Konfiguration aktuell ist:
import okhttp3.OkHttpClient
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import java.security.KeyStore
import javax.net.ssl.X509TrustManager
fun configureTls(builder: OkHttpClient.Builder): OkHttpClient.Builder {
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
)
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
val trustManager = trustManagers.filterIsInstance<X509TrustManager>().first()
val sslContext = SSLContext.getInstance("TLSv1.3")
sslContext.init(null, arrayOf(trustManager), java.security.SecureRandom())
return builder.sslSocketFactory(sslContext.socketFactory, trustManager)
}
Android NetworkSecurityConfig
Auf Android müssen Sie sicherstellen, dass die App cleartext-Verbindungen zum Proxy erlaubt, falls Sie HTTP-Ziele scrapen. In res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-16"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">httpbin.org</domain>
</domain-config>
</network-security-config>
Referenzieren Sie diese Datei im <application>-Tag des Manifests mit android:networkSecurityConfig="@xml/network_security_config". Die offizielle Android Network Security Configuration-Dokumentation enthält weitere Details.
Ethisches Scraping und rechtliche Hinweise
Proxies sind ein Werkzeug, kein Freifahrtschein. Beim Kotlin Web Scraping gelten folgende Prinzipien:
- Nur öffentliche Daten: Scrapen Sie keine Inhalte hinter Login-Walls oder Paywalls ohne ausdrückliche Erlaubnis.
- robots.txt respektieren: Die RFC 9309 definiert das robots.txt-Protokoll – ignorieren Sie es nicht.
- CFAA (USA): Der Computer Fraud and Abuse Act kann bei unautorisiertem Zugriff auf geschützte Systeme greifen, auch wenn keine technischen Barrieren umgangen werden.
- GDPR (EU): Personenbezogene Daten unterliegen der DSGVO – auch wenn sie öffentlich zugänglich sind. Verarbeiten Sie keine personenbezogenen Daten ohne Rechtsgrundlage.
- Offizielle APIs bevorzugen: Wenn eine Plattform eine API anbietet, nutzen Sie diese. Sie ist stabiler, legal sicherer und oft schneller als Scraping.
Die ProxyHat-Dokumentation enthält weitere Hinweise zu verantwortungsvoller Nutzung. Für konkrete Use-Cases wie Web-Scraping oder SERP-Tracking haben wir eigene Leitfäden. Preisinformationen finden Sie auf unserer Preisseite.
ProxyHat SDK – Spiegelung des Musters
Das ProxyHat SDK folgt demselben Muster wie die manuelle Konfiguration oben. Anstatt Proxy-Authorization-Header manuell zu setzen, kapselt das SDK die Authentifizierung und bietet zusätzliche Features wie automatische Rotation und Health-Checks. Die Kernlogik bleibt jedoch dieselbe: Username-kodierte Parameter, HTTP-Port 8080, SOCKS5-Port 1080.
Key Takeaways
- Ktor-Proxy-Auth ist engine-spezifisch: Nutzen Sie
defaultRequestmitProxy-Authorization-Header für die OkHttp-Engine. - OkHttp benötigt einen
Authenticator: Ohne diesen Callback schlägt die Proxy-Authentifizierung mit 407 fehl. - Geo-Targeting im Benutzernamen:
user-country-DE-city-berlinist die kanonische Form für ProxyHat. - Sticky Sessions:
-session-abc123hält die Exit-IP stabil – wichtig für Login-Flows. - Concurrency begrenzen: Ein
Semaphoremit 20–50 Permits ist ein solider Default für residierende Proxies. - SOCKS5 nutzt Port 1080: System-Properties für einfache Setups,
java.net.Proxyfür isolierte Konfiguration. - Ethisch scrapen: Nur öffentliche Daten, robots.txt respektieren, APIs bevorzugen.
FAQ
Was bedeutet „Proxies in Kotlin verwenden"?
Es bezeichnet die Konfiguration von HTTP- oder SOCKS5-Proxys in Kotlin-Anwendungen – typischerweise mit Ktor Client oder OkHttp. Dazu gehören Proxy-Authentifizierung, Geo-Targeting, Session-Management und Retry-Logik. In Kotlin ist die Implementierung besonders elegant dank Coroutines und String-Templates für Username-Kodierung.
Warum ist das für Proxy-Nutzer wichtig?
Ohne korrekte Proxy-Konfiguration erhalten Sie 407-Fehler, IP-Blockaden oder unzuverlässige Scraping-Ergebnisse. Residierende Proxies sind für viele App- und Social-Media-Ziele unerlässlich, da Datacenter-ASNs blockiert werden. Eine saubere Implementierung mit Authentifizierung, Timeouts und Retry-Logik ist die Voraussetzung für produktiven Einsatz.
Welcher Proxy-Typ funktioniert am besten?
Residierende Proxies sind die beste Wahl für Kotlin Web Scraping, wenn das Ziel Datacenter-IPs blockiert. Für einfachere Aufgaben wie Geo-Tests oder schnelle API-Anfragen können Datacenter-Proxies ausreichen. Mobile Proxies bieten die höchste Vertrauensstufe, sind aber teurer. ProxyHat bietet alle drei Typen – die Konfiguration in Kotlin ist für alle identisch.
Wie vermeidet man Blockaden bei der Implementierung?
Nutzen Sie residierende Proxies, rotieren Sie IPs regelmäßig, begrenzen Sie die Concurrency mit einem Semaphore, respektieren Sie robots.txt, und setzen Sie realistische User-Agent-Header. Vermeiden Sie zu schnelle Request-Bursts – 1–2 Requests pro Sekunde pro Session ist ein sicherer Richtwert. Bei 407-Fehlern sollten Sie einen Authenticator mit automatischer Session-Erneuerung implementieren.
Funktioniert SOCKS5 mit Ktor?
Ja, aber die Konfiguration unterscheidet sich. Für Ktor mit CIO-Engine setzen Sie die JVM-System-Properties socksProxyHost, socksProxyPort, java.net.socks.username und java.net.socks.password. Für OkHttp können Sie Proxy(Proxy.Type.SOCKS, ...) direkt setzen und einen Authenticator mit PasswordAuthentication registrieren. ProxyHat nutzt Port 1080 für SOCKS5.






