Aplicativos Java corporativos frequentemente acessam APIs externas, analisam dados ou operam em redes corporativas isoladas — e em cada um desses casos, cedo ou tarde, surge a questão da roteação de tráfego através de um servidor proxy. Configurar um proxy em Java parece uma tarefa simples, até que você se depara com o fato de que RestTemplate, OkHttp e WebClient se comportam de maneiras diferentes, e as propriedades do sistema JVM não funcionam em todos os lugares. Neste guia, você encontrará exemplos de código específicos, explicações sobre armadilhas e recomendações sobre a escolha do tipo de proxy para diferentes tarefas.
Por que aplicativos Java precisam de proxies
Java continua sendo uma das principais linguagens de desenvolvimento corporativo. Serviços Spring Boot, arquiteturas de microsserviços, tarefas em lote, pipelines ETL — tudo isso faz solicitações HTTP ao mundo externo regularmente. E em cada um desses cenários, um proxy resolve uma tarefa específica:
- Redes corporativas com proxy obrigatório. Em muitas empresas, todo o tráfego de saída passa por um proxy corporativo (Squid, Zscaler, BlueCoat). Sem sua configuração, o aplicativo simplesmente não conseguirá acessar a internet.
- Análise e coleta de dados. Serviços que coletam dados de sites externos (preços, cotações, notícias) usam proxies para contornar limites de taxa e bloqueios por IP.
- Teste geográfico. Equipes de QA verificam como a API ou o site se comporta de diferentes regiões — um proxy permite simular uma solicitação do país desejado.
- Contornar restrições do lado da API de destino. Algumas APIs externas têm limites para o número de solicitações de um único IP. A rotação de proxies permite distribuir a carga.
- Segurança e anonimização. Ocultar o IP real do aplicativo ao acessar serviços externos é uma prática padrão para integrações sensíveis.
Dependendo da tarefa, as abordagens para configurar proxies em Java variam significativamente. As propriedades globais do sistema JVM são a opção mais simples, mas não funcionam com alguns clientes HTTP. OkHttp e WebClient exigem configuração explícita. Vamos analisar cada caso em ordem.
Configuração global através de propriedades do sistema JVM
A maneira mais rápida de direcionar todo o tráfego HTTP de um aplicativo Java através de um proxy é usar as propriedades do sistema JVM. Isso funciona para o padrão HttpURLConnection e muitas bibliotecas que o utilizam por baixo dos panos.
Você pode definir um proxy de várias maneiras:
1. Através de argumentos JVM ao iniciar:
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. Através do código do aplicativo (por exemplo, no método main ou em @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");
// Exceções — hosts que não devem passar pelo proxy
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.internal.corp");
3. Através do arquivo application.properties no Spring Boot (com aplicação subsequente através de @PostConstruct):
# application.properties proxy.host=proxy.example.com proxy.port=8080 proxy.username=user proxy.password=secret
⚠️ Importante saber
As propriedades do sistema JVM funcionam apenas para HttpURLConnection. OkHttp, Apache HttpClient e Reactor Netty (WebClient) ignoram essas propriedades — para eles, é necessária uma configuração explícita, descrita abaixo.
Se o proxy exigir autenticação, use Authenticator:
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication("username", "password".toCharArray());
}
return null;
}
});
Proxy no Spring Boot: RestTemplate e Apache HttpClient
RestTemplate — o cliente HTTP síncrono padrão no Spring. Por padrão, ele usa SimpleClientHttpRequestFactory, que se baseia em HttpURLConnection e lê as propriedades do sistema JVM. Mas para uma configuração mais flexível (timeouts, pool de conexões, proxy com autenticação), é recomendado usar o Apache HttpClient como backend.
Dependência no Maven:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
Configuração do RestTemplate com proxy através do 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);
}
}
Essa configuração permite extrair os parâmetros do proxy para application.properties ou variáveis de ambiente, o que é crítico para ambientes de produção/estágio/desenvolvimento com proxies diferentes.
Se você precisar que parte das solicitações passe pelo proxy e parte diretamente, crie dois beans separados de RestTemplate com nomes diferentes e use @Qualifier para injetar o necessário.
Proxy no Spring WebClient (stack reativa)
WebClient — o cliente HTTP reativo do Spring, construído sobre o Reactor Netty. Ele não lê as propriedades do sistema JVM para proxy — a configuração é feita através de ProxyProvider ao criar o cliente.
Dependência (incluída no spring-boot-starter-webflux):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Configuração do WebClient com proxy 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();
}
}
Para proxies SOCKS5 no WebClient:
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.SOCKS5)
.host("socks5.proxy.example.com")
.port(1080)
.username("user")
.password(user -> "password")
);
💡 Dica de configuração
Em aplicativos reativos, muitas vezes é necessário criar várias instâncias de WebClient com proxies diferentes para diferentes serviços externos. Use WebClient.Builder como um bean protótipo e sobrescreva as configurações através de .mutate().
Configuração de proxy no OkHttp
OkHttp — uma biblioteca HTTP popular, utilizada por muitos aplicativos Java e Android, assim como algumas integrações Spring (por exemplo, Feign com backend OkHttp). Ela possui seu próprio mecanismo de configuração de proxy e também não lê as propriedades do sistema JVM.
Configuração básica de proxy HTTP/HTTPS no OkHttp:
import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
// Criando o proxy
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("proxy.example.com", 8080)
);
// Autenticação do proxy
Authenticator proxyAuthenticator = (route, response) -> {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
// Construindo o cliente
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// Exemplo de solicitação
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());
}
Integração do OkHttp com Spring Boot através de um bean:
@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();
}
}
Note o @ConditionalOnProperty — isso permite ativar o proxy apenas nos ambientes necessários através da configuração, sem alterar o código.
Se você estiver usando OkHttp juntamente com proxies residenciais para análise de dados, é importante configurar corretamente o pool de conexões. Proxies residenciais costumam ter latência mais alta em comparação com proxies de data center, portanto, aumente os timeouts e diminua o tamanho do pool para não esgotar as conexões disponíveis.
Proxy SOCKS5 em Java: características e exemplos
SOCKS5 — um protocolo de nível mais baixo em comparação com proxies HTTP. Ele opera na camada de transporte e suporta quaisquer protocolos (HTTP, HTTPS, FTP, etc.), assim como resolução de DNS do lado do proxy. Para aplicativos Java, SOCKS5 é especialmente útil quando é necessário proxificar não apenas HTTP, mas também outras conexões TCP.
SOCKS5 através de propriedades do sistema 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 no OkHttp (via 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();
Uma característica importante do SOCKS5 e DNS: Por padrão, o Java resolve DNS localmente e, através do proxy, envia já o endereço IP. Isso pode revelar a verdadeira solicitação DNS. Para que a resolução de DNS também passe pelo proxy, use a seguinte abordagem com OkHttp:
// DNS personalizado que força a resolução através do SOCKS5
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.dns(hostname -> {
// Retornamos o host não resolvido — OkHttp o enviará para o proxy
return Collections.singletonList(InetAddress.getByName(hostname));
})
.build();
Quando escolher SOCKS5 vs HTTP-proxy
| Critério | HTTP-proxy | SOCKS5-proxy |
|---|---|---|
| Protocolos | HTTP/HTTPS | Qualquer TCP/UDP |
| DNS através do proxy | Depende da implementação | Suportado |
| Compatibilidade com Java | Nativa | Nativa (java.net.Proxy.Type.SOCKS) |
| Uso | Gateways corporativos, web scraping | Anonimização, protocolos não padrão |
Rotação de proxies em aplicativos Java
Se um aplicativo faz um grande número de solicitações a um único recurso, o uso de um único IP de proxy rapidamente levará ao bloqueio. A rotação de proxies — alternar entre diferentes endereços IP — resolve esse problema. Em Java, isso pode ser implementado de várias maneiras.
Opção 1: Rotação através de um endpoint de proxy rotativo
A abordagem mais simples é usar um provedor de proxy com um único endpoint, que altera automaticamente o IP a cada solicitação ou em um intervalo definido. Você especifica um host e uma porta, e a rotação ocorre do lado do provedor. Não é necessário escrever código para rotação.
Opção 2: Rotação do lado do aplicativo através de uma lista de proxies
@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) {
// Analisando a string no formato "host:port" ou "user:pass@host:port"
String[] parts = proxyStr.split(":");
return new ProxyConfig(parts[0], Integer.parseInt(parts[1]));
}
}
Opção 3: Rotação através de ProxySelector
O Java fornece um mecanismo embutido ProxySelector, que permite escolher proxies dinamicamente com base na URL de destino. Isso funciona com HttpURLConnection e 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) {
// Você pode adicionar lógica de seleção por domínio
int i = idx.getAndIncrement() % proxyPool.size();
return Collections.singletonList(proxyPool.get(i));
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// Lógica para lidar com proxy indisponível
log.warn("Proxy falhou: {}", sa);
}
});
Qual tipo de proxy escolher para tarefas Java
A escolha do tipo de proxy depende da tarefa específica. Para aplicativos Java corporativos, geralmente consideram-se três tipos principais:
| Tipo de proxy | Velocidade | Anonimato | Melhor para |
|---|---|---|---|
| Datacenter | Alta | Média | Análise de dados abertos, teste de APIs, alta carga |
| Residencial | Média | Alta | Análise de sites protegidos, teste geográfico, contornar bloqueios |
| Móvel | Média | Máxima | Integrações com APIs móveis, serviços com proteção anti-fraude rigorosa |
Recomendações práticas para tipos de tarefas:
- Gateway corporativo (Squid, Zscaler): O tipo de proxy é determinado pelo departamento de TI. Use proxies HTTP com autenticação através do Apache HttpClient ou propriedades do sistema JVM.
- Análise de dados públicos (notícias, cotações, APIs abertas): Proxies de datacenter são a escolha ideal em termos de velocidade e custo.
- Análise de e-commerce (Wildberries, Ozon, Amazon): Proxies residenciais com rotação. Essas plataformas bloqueiam ativamente IPs de datacenter.
- Teste geográfico (verificação de localização, preços por regiões): Proxies residenciais com seleção de país/cidade.
- Integração com serviços e aplicativos móveis: Proxies móveis — IPs de redes móveis reais imitam melhor usuários reais.
Erros comuns ao configurar proxies em Java e como corrigi-los
Mesmo desenvolvedores experientes enfrentam os mesmos problemas ao configurar proxies em Java. Vamos analisar os mais comuns.
1. Proxy configurado, mas o tráfego ainda vai diretamente
Causa: Você definiu as propriedades do sistema JVM, mas está usando OkHttp ou Reactor Netty (WebClient), que as ignoram.
Solução: Configure o proxy explicitamente na configuração do cliente HTTP específico, como mostrado nas seções acima.
2. javax.net.ssl.SSLHandshakeException ao usar HTTPS através do proxy
Causa: O proxy corporativo realiza inspeção SSL (MITM), substituindo o certificado. O Java não confia na CA corporativa.
Solução: Importe o certificado corporativo para o truststore do Java:
keytool -import -trustcacerts \ -alias corporate-ca \ -file corporate-ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit
3. java.net.ConnectException: Conexão recusada
Causas: Host ou porta do proxy incorretos; servidor proxy indisponível; firewall bloqueando a conexão.
Diagnóstico:
# Verificando a disponibilidade do proxy curl -x http://proxy.example.com:8080 https://httpbin.org/ip # Ou através do telnet telnet proxy.example.com 8080
4. Proxy funciona para HTTP, mas não para HTTPS
Causa: Apenas as propriedades http.proxyHost/Port foram definidas, mas não https.proxyHost/Port.
Solução: Defina ambos os conjuntos de propriedades. Para HTTPS através de um proxy HTTP, o método de tunelamento CONNECT é utilizado — certifique-se de que o proxy o suporta.
5. 407 Proxy Authentication Required
Causa: O proxy requer autenticação, mas ela não está configurada.
Solução: Adicione um Authenticator para as propriedades do sistema JVM ou configure um CredentialsProvider para Apache HttpClient / proxyAuthenticator para OkHttp (exemplos acima).
6. Vazamento de memória ao rotacionar proxies
Causa: A cada solicitação, uma nova instância de OkHttpClient é criada. Cada cliente mantém seu próprio pool de threads e conexões.
Solução: Reutilize os clientes. Crie um pool de vários clientes (um para cada proxy) e rotacione-os, em vez de criar novos.
7. Habilitando logging de depuração para diagnóstico
# application.properties — habilitando logs para diagnóstico de proxy logging.level.org.apache.http=DEBUG logging.level.org.apache.http.wire=DEBUG logging.level.reactor.netty.http.client=DEBUG # Para JVM — propriedade do sistema -Djava.net.debug=all
Conclusão
Configurar proxies em aplicativos Java requer compreensão de qual cliente HTTP está sendo usado por baixo dos panos. As propriedades do sistema JVM são um bom ponto de partida para HttpURLConnection, mas OkHttp, WebClient e Apache HttpClient exigem configuração explícita. Para aplicativos corporativos, é recomendado extrair os parâmetros do proxy para application.properties e usar perfis Spring para diferentes ambientes. A rotação de proxies é melhor delegada a um provedor com um endpoint rotativo — isso é mais simples e confiável do que implementar sozinho.
Se seu aplicativo Java está envolvido na análise de dados de plataformas protegidas ou requer alta anonimidade em solicitações externas, recomendamos considerar proxies residenciais — eles têm IPs reais de usuários domésticos, o que reduz significativamente a probabilidade de bloqueios por sistemas anti-fraude. Para análise de dados abertos com alta carga, os proxies de datacenter são ideais — eles oferecem a máxima velocidade a um custo menor de tráfego.
```