Skip to content
NextProxyNextProxyDocs

Java and PHP

Java via HttpClient, OkHttp and Apache HttpClient; PHP via the cURL extension and Guzzle.

Each language has one signature trap: Java's HttpClient will not send credentials to a proxy by default, and PHP's CURLOPT_PROXY needs an explicit proxy type. Both are handled below.

Java 11+ HttpClient

Part of the standard library, but proxy authentication needs an Authenticator and one system property:

java
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;
import java.time.Duration;

public class NextProxyExample {

    private static final String GATEWAY_HOST = "GATEWAY_HOST";
    private static final int GATEWAY_PORT = 58971;
    private static final String PROXY_USER =
            "USERNAME-country-US-session-job1-time-30";
    private static final String PROXY_PASS = "PASSWORD";

    public static void main(String[] args) throws Exception {
        // Without this line the JDK refuses to send Basic credentials to the proxy,
        // which shows up as a permanent 407
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

        HttpClient client = HttpClient.newBuilder()
                .proxy(ProxySelector.of(
                        new InetSocketAddress(GATEWAY_HOST, GATEWAY_PORT)))
                .authenticator(new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(
                                PROXY_USER, PROXY_PASS.toCharArray());
                    }
                })
                .connectTimeout(Duration.ofSeconds(15))
                .build();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.ipify.org?format=json"))
                .timeout(Duration.ofSeconds(30))
                .build();

        HttpResponse<String> response =
                client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}

OkHttp

Considerably less friction than the standard library; credentials go straight into an Authenticator:

java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;

import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class OkHttpExample {

    static OkHttpClient buildClient(String proxyUser, String proxyPass) {
        Proxy proxy = new Proxy(Proxy.Type.HTTP,
                new InetSocketAddress("GATEWAY_HOST", 58971));

        return new OkHttpClient.Builder()
                .proxy(proxy)
                .proxyAuthenticator((route, response) -> {
                    // Already sent credentials and still 401/407? Stop, or we loop forever
                    if (response.request().header("Proxy-Authorization") != null) {
                        return null;
                    }
                    return response.request().newBuilder()
                            .header("Proxy-Authorization",
                                    Credentials.basic(proxyUser, proxyPass))
                            .build();
                })
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();
    }

    public static void main(String[] args) throws IOException {
        OkHttpClient client = buildClient(
                "USERNAME-country-US-session-job1-time-30", "PASSWORD");

        Request request = new Request.Builder()
                .url("https://api.ipify.org?format=json")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println(response.code() + " " + response.body().string());
        }
    }
}

SOCKS5 with OkHttp

java
Proxy proxy = new Proxy(Proxy.Type.SOCKS,
        new InetSocketAddress("GATEWAY_HOST", 58971));

// SOCKS5 credentials go through java.net.Authenticator, not proxyAuthenticator
java.net.Authenticator.setDefault(new java.net.Authenticator() {
    @Override
    protected java.net.PasswordAuthentication getPasswordAuthentication() {
        return new java.net.PasswordAuthentication(
                "USERNAME-country-US", "PASSWORD".toCharArray());
    }
});

OkHttpClient client = new OkHttpClient.Builder().proxy(proxy).build();

Rotating IPs

OkHttp's connection pool reuses connections. To change IP each time, either build a new client or call evictAll():

java
OkHttpClient client = buildClient("USERNAME-country-US", "PASSWORD");

for (int i = 0; i < 5; i++) {
    Request request = new Request.Builder()
            .url("https://api.ipify.org")
            .build();

    try (Response response = client.newCall(request).execute()) {
        System.out.println(response.body().string());
    }

    // Drain the pool so the next call opens a new connection and re-routes
    client.connectionPool().evictAll();
}

Concurrency with per-task sessions

java
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class ConcurrentExample {

    public static void main(String[] args) throws Exception {
        try (ExecutorService pool = Executors.newFixedThreadPool(8)) {
            List<?> futures = IntStream.range(0, 8)
                    .mapToObj(id -> pool.submit(() -> {
                        // session values allow only [A-Za-z0-9.-]; dots, not underscores
                        String user = "USERNAME-country-US-session-task."
                                + id + "-time-30";
                        OkHttpClient client =
                                OkHttpExample.buildClient(user, "PASSWORD");

                        Request request = new Request.Builder()
                                .url("https://api.ipify.org")
                                .build();
                        try (Response response =
                                     client.newCall(request).execute()) {
                            return id + " -> " + response.body().string();
                        }
                    }))
                    .toList();

            for (Object future : futures) {
                System.out.println(((java.util.concurrent.Future<?>) future).get());
            }
        }
    }
}

Apache HttpClient 5

java
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;

public class ApacheExample {

    public static void main(String[] args) throws Exception {
        HttpHost proxy = new HttpHost("http", "GATEWAY_HOST", 58971);

        BasicCredentialsProvider credentials = new BasicCredentialsProvider();
        credentials.setCredentials(
                new AuthScope(proxy),
                new UsernamePasswordCredentials(
                        "USERNAME-country-US-session-job1-time-30",
                        "PASSWORD".toCharArray()));

        try (CloseableHttpClient client = HttpClients.custom()
                .setProxy(proxy)
                .setDefaultCredentialsProvider(credentials)
                .build()) {

            HttpGet request = new HttpGet("https://api.ipify.org?format=json");
            client.execute(request, response -> {
                System.out.println(response.getCode() + " "
                        + EntityUtils.toString(response.getEntity()));
                return null;
            });
        }
    }
}

PHP's cURL extension

php
<?php

declare(strict_types=1);

function proxyRequest(string $url, string $proxyUser, string $proxyPass): string
{
    $handle = curl_init($url);

    curl_setopt_array($handle, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_PROXY          => 'GATEWAY_HOST:58971',
        // Set the type explicitly; libcurl's default may not be what you expect
        CURLOPT_PROXYTYPE      => CURLPROXY_HTTP,
        CURLOPT_PROXYUSERPWD   => $proxyUser . ':' . $proxyPass,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_TIMEOUT        => 30,
    ]);

    $body = curl_exec($handle);
    if ($body === false) {
        $error = curl_error($handle);
        curl_close($handle);
        throw new RuntimeException('request failed: ' . $error);
    }

    $status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
    curl_close($handle);

    if ($status !== 200) {
        throw new RuntimeException('target returned HTTP ' . $status);
    }

    return (string) $body;
}

echo proxyRequest(
    'https://api.ipify.org?format=json',
    'USERNAME-country-US-session-job1-time-30',
    'PASSWORD'
), PHP_EOL;

SOCKS5 in PHP

php
curl_setopt_array($handle, [
    CURLOPT_PROXY        => 'GATEWAY_HOST:58971',
    // Let the proxy resolve the hostname; equivalent to socks5h://
    CURLOPT_PROXYTYPE    => CURLPROXY_SOCKS5_HOSTNAME,
    CURLOPT_PROXYUSERPWD => 'USERNAME-country-US:PASSWORD',
]);

Guzzle

shell
composer require guzzlehttp/guzzle
php
<?php

declare(strict_types=1);

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$proxyUser = 'USERNAME-country-US-session-job1-time-30';
$proxyPass = 'PASSWORD';
$proxy = sprintf('http://%s:%s@GATEWAY_HOST:58971',
    rawurlencode($proxyUser), rawurlencode($proxyPass));

$client = new Client([
    // Both http and https point at the same plaintext proxy; there is no inbound TLS
    'proxy'           => ['http' => $proxy, 'https' => $proxy],
    'timeout'         => 30,
    'connect_timeout' => 15,
    'http_errors'     => false,
]);

try {
    $response = $client->get('https://api.ipify.org?format=json');
    echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;
} catch (RequestException $exception) {
    echo 'request failed: ', $exception->getMessage(), PHP_EOL;
}

Concurrency with Guzzle

php
<?php

use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;

$password = 'PASSWORD';
$requests = static function () use ($password): Generator {
    for ($i = 0; $i < 8; $i++) {
        // session values allow only [A-Za-z0-9.-]
        $user = sprintf('USERNAME-country-US-session-task.%d-time-30', $i);
        $proxy = sprintf('http://%s:%s@GATEWAY_HOST:58971',
            rawurlencode($user), rawurlencode($password));

        yield new Request('GET', 'https://api.ipify.org') => [
            'proxy' => ['http' => $proxy, 'https' => $proxy],
        ];
    }
};

// Guzzle's Pool cannot swap proxies per request, so build a client per session
for ($i = 0; $i < 8; $i++) {
    $user = sprintf('USERNAME-country-US-session-task.%d-time-30', $i);
    $proxy = sprintf('http://%s:%s@GATEWAY_HOST:58971',
        rawurlencode($user), rawurlencode($password));

    $client = new Client(['proxy' => ['http' => $proxy, 'https' => $proxy]]);
    echo $i, ' -> ', $client->get('https://api.ipify.org')->getBody(), PHP_EOL;
}

Troubleshooting checklist

LanguageSymptomCause
JavaHTTPS always 407, Authenticator never calledjdk.http.auth.tunneling.disabledSchemes="" not set
JavaSOCKS5 credentials cross-contaminateThe global Authenticator is process-wide; use HTTP instead
JavaIP never changesOkHttp pool reuse; call evictAll()
PHPProxy has no effectCURLOPT_PROXYTYPE not set
PHPCredentials truncatedPassword placed in a URL without rawurlencode
Any400invalid_proxy_parameters: option syntax, order or dependency
Any402traffic_exhausted / account_quota_exhausted / quota_exhausted: traffic or quota exhausted
Any403account_disabled / account_expired / access_denied / target_denied: account or policy denial
Any429connection_limit: reduce concurrency and connection rate
Any502Exit connection failed
Any503No eligible route or service temporarily unavailable
Any504Proxy connection establishment timed out

For HTTP / CONNECT failures, inspect X-NextProxy-Error and JSON error.code. Handle destination statuses and CONNECT handshake exceptions separately. See the error reference for the full mapping.

Did this page solve your problem?