Назад к блогу

Настройка прокси в Java-приложениях: Spring Boot, OkHttp и RestTemplate — полный гайд с кодом

Полное руководство по настройке прокси в корпоративных Java-приложениях: Spring Boot, OkHttp, RestTemplate, WebClient — с примерами кода и рекомендациями по выбору прокси.

📅12 августа 2026 г.

Корпоративные Java-приложения регулярно обращаются к внешним API, парсят данные или работают в изолированных корпоративных сетях — и в каждом из этих случаев рано или поздно встаёт вопрос маршрутизации трафика через прокси-сервер. Настройка прокси в Java выглядит простой задачей, пока не сталкиваешься с тем, что RestTemplate, OkHttp и WebClient ведут себя по-разному, а системные свойства JVM работают не везде. В этом руководстве — конкретные примеры кода, объяснение подводных камней и рекомендации по выбору типа прокси под разные задачи.

Зачем Java-приложениям нужны прокси

Java остаётся одним из главных языков корпоративной разработки. Spring Boot-сервисы, микросервисные архитектуры, batch-задачи, ETL-пайплайны — всё это регулярно делает HTTP-запросы во внешний мир. И в каждом из этих сценариев прокси решает конкретную задачу:

  • Корпоративные сети с обязательным прокси-шлюзом. Во многих компаниях весь исходящий трафик проходит через корпоративный прокси (Squid, Zscaler, BlueCoat). Без его настройки приложение просто не выйдет в интернет.
  • Парсинг и сбор данных. Сервисы, которые собирают данные с внешних сайтов (цены, котировки, новости), используют прокси для обхода rate-limit и блокировок по IP.
  • Гео-тестирование. QA-команды проверяют, как API или сайт ведёт себя из разных регионов — прокси позволяет эмулировать запрос из нужной страны.
  • Обход ограничений на стороне целевого API. Некоторые внешние API имеют лимиты на количество запросов с одного IP. Ротация прокси позволяет распределить нагрузку.
  • Безопасность и анонимизация. Скрытие реального IP приложения при обращении к внешним сервисам — стандартная практика для чувствительных интеграций.

В зависимости от задачи подходы к настройке прокси в Java существенно различаются. Глобальные системные свойства JVM — самый простой вариант, но он не работает с некоторыми HTTP-клиентами. OkHttp и WebClient требуют явной конфигурации. Разберём каждый случай по порядку.

Глобальная настройка через системные свойства JVM

Самый быстрый способ направить весь HTTP-трафик Java-приложения через прокси — использовать системные свойства JVM. Это работает для стандартного HttpURLConnection и многих библиотек, которые используют его под капотом.

Задать прокси можно несколькими способами:

1. Через аргументы JVM при запуске:

java -Dhttp.proxyHost=proxy.example.com \
     -Dhttp.proxyPort=8080 \
     -Dhttps.proxyHost=proxy.example.com \
     -Dhttps.proxyPort=8080 \
     -Dhttp.nonProxyHosts="localhost|127.0.0.1|*.internal.corp" \
     -jar myapp.jar

2. Через код приложения (например, в методе main или в @PostConstruct):

System.setProperty("http.proxyHost", "proxy.example.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.example.com");
System.setProperty("https.proxyPort", "8080");
// Исключения — хосты, которые не должны идти через прокси
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.internal.corp");

3. Через файл application.properties в Spring Boot (с последующим применением через @PostConstruct):

# application.properties
proxy.host=proxy.example.com
proxy.port=8080
proxy.username=user
proxy.password=secret

⚠️ Важно знать

Системные свойства JVM работают только для HttpURLConnection. OkHttp, Apache HttpClient и Reactor Netty (WebClient) игнорируют эти свойства — для них нужна явная конфигурация, описанная ниже.

Если прокси требует аутентификации, используйте Authenticator:

Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        if (getRequestorType() == RequestorType.PROXY) {
            return new PasswordAuthentication("username", "password".toCharArray());
        }
        return null;
    }
});

Прокси в Spring Boot: RestTemplate и Apache HttpClient

RestTemplate — стандартный синхронный HTTP-клиент в Spring. По умолчанию он использует SimpleClientHttpRequestFactory, который опирается на HttpURLConnection и читает системные свойства JVM. Но для более гибкой настройки (таймауты, пул соединений, прокси с аутентификацией) рекомендуется использовать Apache HttpClient в качестве бэкенда.

Зависимость в Maven:

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3.1</version>
</dependency>

Конфигурация RestTemplate с прокси через Apache HttpClient 5:

@Configuration
public class RestTemplateConfig {

    @Value("${proxy.host}")
    private String proxyHost;

    @Value("${proxy.port}")
    private int proxyPort;

    @Value("${proxy.username:}")
    private String proxyUsername;

    @Value("${proxy.password:}")
    private String proxyPassword;

    @Bean
    public RestTemplate restTemplate() {
        HttpHost proxy = new HttpHost("http", proxyHost, proxyPort);

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        if (!proxyUsername.isEmpty()) {
            credentialsProvider.setCredentials(
                new AuthScope(proxyHost, proxyPort),
                new UsernamePasswordCredentials(proxyUsername, proxyPassword.toCharArray())
            );
        }

        CloseableHttpClient httpClient = HttpClients.custom()
            .setProxy(proxy)
            .setDefaultCredentialsProvider(credentialsProvider)
            .build();

        HttpComponentsClientHttpRequestFactory factory =
            new HttpComponentsClientHttpRequestFactory(httpClient);
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(10000);

        return new RestTemplate(factory);
    }
}

Такая конфигурация позволяет вынести параметры прокси в application.properties или переменные окружения, что критично для prod/staging/dev окружений с разными прокси.

Если нужно, чтобы часть запросов шла через прокси, а часть — напрямую, создайте два отдельных бина RestTemplate с разными именами и используйте @Qualifier для инжекции нужного.

Прокси в Spring WebClient (реактивный стек)

WebClient — реактивный HTTP-клиент Spring, построенный на Reactor Netty. Он не читает системные свойства JVM для прокси — настройка делается через ProxyProvider при создании клиента.

Зависимость (входит в spring-boot-starter-webflux):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Конфигурация WebClient с HTTP-прокси:

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient() {
        HttpClient httpClient = HttpClient.create()
            .proxy(proxy -> proxy
                .type(ProxyProvider.Proxy.HTTP)
                .host("proxy.example.com")
                .port(8080)
                .username("user")
                .password(user -> "password")
            );

        ClientHttpConnector connector =
            new ReactorClientHttpConnector(httpClient);

        return WebClient.builder()
            .clientConnector(connector)
            .build();
    }
}

Для SOCKS5-прокси в WebClient:

HttpClient httpClient = HttpClient.create()
    .proxy(proxy -> proxy
        .type(ProxyProvider.Proxy.SOCKS5)
        .host("socks5.proxy.example.com")
        .port(1080)
        .username("user")
        .password(user -> "password")
    );

💡 Совет по конфигурации

В реактивных приложениях часто нужно создавать несколько экземпляров WebClient с разными прокси для разных внешних сервисов. Используйте WebClient.Builder как прототип-бин и переопределяйте настройки через .mutate().

Настройка прокси в OkHttp

OkHttp — популярная HTTP-библиотека, которую используют многие Java и Android-приложения, а также некоторые Spring-интеграции (например, Feign с OkHttp-бэкендом). Она имеет собственный механизм настройки прокси и также не читает системные свойства JVM.

Базовая настройка HTTP/HTTPS прокси в OkHttp:

import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;

// Создаём прокси
Proxy proxy = new Proxy(
    Proxy.Type.HTTP,
    new InetSocketAddress("proxy.example.com", 8080)
);

// Аутентификация прокси
Authenticator proxyAuthenticator = (route, response) -> {
    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(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .build();

// Пример запроса
Request request = new Request.Builder()
    .url("https://api.example.com/data")
    .build();

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

Интеграция OkHttp с Spring Boot через бин:

@Configuration
@ConditionalOnProperty(name = "proxy.enabled", havingValue = "true")
public class OkHttpConfig {

    @Value("${proxy.host}")
    private String proxyHost;

    @Value("${proxy.port}")
    private int proxyPort;

    @Bean
    public OkHttpClient okHttpClient() {
        Proxy proxy = new Proxy(
            Proxy.Type.HTTP,
            new InetSocketAddress(proxyHost, proxyPort)
        );

        return new OkHttpClient.Builder()
            .proxy(proxy)
            .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
            .build();
    }
}

Обратите внимание на @ConditionalOnProperty — это позволяет включать прокси только в нужных окружениях через конфигурацию, не меняя код.

Если вы используете OkHttp вместе с резидентными прокси для парсинга данных, важно правильно настроить пул соединений. Резидентные прокси часто имеют более высокую латентность по сравнению с датацентровыми, поэтому увеличьте таймауты и уменьшите размер пула, чтобы не исчерпать доступные соединения.

SOCKS5-прокси в Java: особенности и примеры

SOCKS5 — более низкоуровневый протокол по сравнению с HTTP-прокси. Он работает на транспортном уровне и поддерживает любые протоколы (HTTP, HTTPS, FTP и др.), а также DNS-резолвинг на стороне прокси. Для Java-приложений SOCKS5 особенно полезен, когда нужно проксировать не только HTTP, но и другие TCP-соединения.

SOCKS5 через системные свойства JVM:

System.setProperty("socksProxyHost", "socks5.proxy.example.com");
System.setProperty("socksProxyPort", "1080");
System.setProperty("java.net.socks.username", "user");
System.setProperty("java.net.socks.password", "password");

SOCKS5 в OkHttp (через java.net.Proxy):

Proxy socks5Proxy = new Proxy(
    Proxy.Type.SOCKS,
    new InetSocketAddress("socks5.proxy.example.com", 1080)
);

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

Важная особенность SOCKS5 и DNS: По умолчанию Java резолвит DNS локально, а через прокси передаёт уже IP-адрес. Это может раскрыть реальный DNS-запрос. Чтобы DNS-резолвинг тоже шёл через прокси, используйте следующий подход с OkHttp:

// Кастомный DNS, который форсирует резолвинг через SOCKS5
OkHttpClient client = new OkHttpClient.Builder()
    .proxy(socks5Proxy)
    .dns(hostname -> {
        // Возвращаем нерезолвленный хост — OkHttp передаст его прокси
        return Collections.singletonList(InetAddress.getByName(hostname));
    })
    .build();

Когда выбирать SOCKS5 vs HTTP-прокси

Критерий HTTP-прокси SOCKS5-прокси
Протоколы HTTP/HTTPS Любые TCP/UDP
DNS через прокси Зависит от реализации Поддерживается
Совместимость с Java Нативная Нативная (java.net.Proxy.Type.SOCKS)
Использование Корпоративные шлюзы, веб-парсинг Анонимизация, нестандартные протоколы

Ротация прокси в Java-приложениях

Если приложение делает большое количество запросов к одному ресурсу, использование одного прокси-IP быстро приведёт к блокировке. Ротация прокси — переключение между разными IP-адресами — решает эту проблему. В Java это реализуется несколькими способами.

Вариант 1: Ротация через rotating proxy endpoint

Самый простой подход — использовать прокси-провайдера с единым endpoint, который автоматически меняет IP при каждом запросе или через заданный интервал. Вы указываете один хост и порт, а ротация происходит на стороне провайдера. Никакого кода для ротации писать не нужно.

Вариант 2: Ротация на стороне приложения через список прокси

@Component
public class ProxyRotator {

    private final List<ProxyConfig> proxies;
    private final AtomicInteger counter = new AtomicInteger(0);

    public ProxyRotator(@Value("${proxy.list}") List<String> proxyList) {
        this.proxies = proxyList.stream()
            .map(this::parseProxy)
            .collect(Collectors.toList());
    }

    public OkHttpClient getClientWithNextProxy() {
        int index = counter.getAndIncrement() % proxies.size();
        ProxyConfig config = proxies.get(index);

        Proxy proxy = new Proxy(
            Proxy.Type.HTTP,
            new InetSocketAddress(config.getHost(), config.getPort())
        );

        return new OkHttpClient.Builder()
            .proxy(proxy)
            .build();
    }

    private ProxyConfig parseProxy(String proxyStr) {
        // Парсим строку вида "host:port" или "user:pass@host:port"
        String[] parts = proxyStr.split(":");
        return new ProxyConfig(parts[0], Integer.parseInt(parts[1]));
    }
}

Вариант 3: Ротация через ProxySelector

Java предоставляет встроенный механизм ProxySelector, который позволяет динамически выбирать прокси на основе URL назначения. Это работает с HttpURLConnection и Apache HttpClient:

ProxySelector.setDefault(new ProxySelector() {
    private final List<Proxy> proxyPool = Arrays.asList(
        new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy1.example.com", 8080)),
        new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy2.example.com", 8080)),
        new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy3.example.com", 8080))
    );
    private final AtomicInteger idx = new AtomicInteger(0);

    @Override
    public List<Proxy> select(URI uri) {
        // Можно добавить логику выбора по домену
        int i = idx.getAndIncrement() % proxyPool.size();
        return Collections.singletonList(proxyPool.get(i));
    }

    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        // Логика обработки недоступного прокси
        log.warn("Proxy failed: {}", sa);
    }
});

Какой тип прокси выбрать для Java-задач

Выбор типа прокси зависит от конкретной задачи. Для корпоративных Java-приложений обычно рассматривают три основных типа:

Тип прокси Скорость Анонимность Лучше всего для
Датацентровые Высокая Средняя Парсинг открытых данных, тестирование API, высокая нагрузка
Резидентные Средняя Высокая Парсинг защищённых сайтов, гео-тестирование, обход блокировок
Мобильные Средняя Максимальная Интеграции с мобильными API, сервисы с жёсткой антифрод-защитой

Практические рекомендации по типам задач:

  • Корпоративный шлюз (Squid, Zscaler): Тип прокси определяет IT-отдел. Используйте HTTP-прокси с аутентификацией через Apache HttpClient или системные свойства JVM.
  • Парсинг публичных данных (новости, котировки, открытые API): Датацентровые прокси — оптимальный выбор по соотношению скорости и стоимости.
  • Парсинг e-commerce (Wildberries, Ozon, Amazon): Резидентные прокси с ротацией. Эти платформы активно блокируют датацентровые IP.
  • Гео-тестирование (проверка локализации, цен по регионам): Резидентные прокси с выбором страны/города.
  • Интеграция с мобильными сервисами и приложениями: Мобильные прокси — IP из реальных мобильных сетей лучше всего имитируют реальных пользователей.

Частые ошибки при настройке прокси в Java и как их исправить

Даже опытные разработчики сталкиваются с одними и теми же проблемами при настройке прокси в Java. Разберём самые распространённые.

1. Прокси настроен, но трафик всё равно идёт напрямую

Причина: Вы задали системные свойства JVM, но используете OkHttp или Reactor Netty (WebClient), которые их игнорируют.
Решение: Настройте прокси явно в конфигурации конкретного HTTP-клиента, как показано в разделах выше.

2. javax.net.ssl.SSLHandshakeException при HTTPS через прокси

Причина: Корпоративный прокси делает SSL-inspection (MITM), подменяя сертификат. Java не доверяет корпоративному CA.
Решение: Импортируйте корпоративный сертификат в Java truststore:

keytool -import -trustcacerts \
  -alias corporate-ca \
  -file corporate-ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit

3. java.net.ConnectException: Connection refused

Причины: Неверный хост или порт прокси; прокси-сервер недоступен; firewall блокирует соединение.
Диагностика:

# Проверяем доступность прокси
curl -x http://proxy.example.com:8080 https://httpbin.org/ip

# Или через telnet
telnet proxy.example.com 8080

4. Прокси работает для HTTP, но не для HTTPS

Причина: Заданы только свойства http.proxyHost/Port, но не https.proxyHost/Port.
Решение: Задайте оба набора свойств. Для HTTPS через HTTP-прокси используется метод CONNECT-туннелирования — убедитесь, что прокси его поддерживает.

5. 407 Proxy Authentication Required

Причина: Прокси требует аутентификации, но она не настроена.
Решение: Добавьте Authenticator для системных свойств JVM или настройте CredentialsProvider для Apache HttpClient / proxyAuthenticator для OkHttp (примеры выше).

6. Утечка памяти при ротации прокси

Причина: При каждом запросе создаётся новый экземпляр OkHttpClient. Каждый клиент держит свой пул потоков и соединений.
Решение: Переиспользуйте клиенты. Создавайте пул из нескольких клиентов (по одному на прокси) и ротируйте их, а не создавайте новые.

7. Включение отладочного логирования для диагностики

# application.properties — включаем логи для диагностики прокси
logging.level.org.apache.http=DEBUG
logging.level.org.apache.http.wire=DEBUG
logging.level.reactor.netty.http.client=DEBUG

# Для JVM — системное свойство
-Djava.net.debug=all

Заключение

Настройка прокси в Java-приложениях требует понимания того, какой HTTP-клиент используется под капотом. Системные свойства JVM — быстрый старт для HttpURLConnection, но OkHttp, WebClient и Apache HttpClient требуют явной конфигурации. Для корпоративных приложений рекомендуется выносить параметры прокси в application.properties и использовать профили Spring для разных окружений. Ротацию прокси лучше делегировать провайдеру с rotating endpoint — это проще и надёжнее, чем реализовывать самостоятельно.

Если ваше Java-приложение занимается парсингом данных с защищённых платформ или требует высокой анонимности при внешних запросах, рекомендуем рассмотреть резидентные прокси — они имеют реальные IP домашних пользователей, что существенно снижает вероятность блокировок со стороны антифрод-систем. Для высоконагруженного парсинга открытых данных оптимальны прокси дата-центров — они обеспечивают максимальную скорость при меньшей стоимости трафика.