Somos a Geonode e comercializamos proxies; por isso, este é um guia sobre como utilizar o nosso tipo de produto com um cliente específico. A frase que vale a pena ler, mesmo que ignore o resto: verifique o endereço de saída após a configuração, porque uma configuração de proxy que não funciona não produz qualquer erro. O SuperAgent enviará alegremente o seu pedido diretamente, devolverá um código 200 e não lhe dará qualquer indicação de que o proxy foi contornado. A secção de verificação tem quatro linhas e é a diferença entre saber e supor.
Note também que o SuperAgent funciona também em navegadores, onde nada disto se aplica — não é possível instruir um navegador a utilizar um proxy a partir de JavaScript, pelo que tudo aqui se aplica apenas ao Node.
O Problema da Terminologia
É importante esclarecer isto primeiro, porque dá origem a erros reais.
No SuperAgent, o comando .agent() sem argumentos cria uma cópia do SuperAgent que guarda os cookies. A documentação é explícita: «No Node, o SuperAgent não guarda cookies por predefinição, mas pode utilizar o método .agent() para criar uma cópia do SuperAgent que guarde cookies. Cada cópia tem um conjunto de cookies separado.»
const agent = request.agent();
await agent.post("/login").send({ user, pass });
await agent.get("/cookied-page"); // session cookie carried over
Esse agente também tem predefinições: «Os métodos de pedido normais chamados no agente serão utilizados como predefinições para todos os pedidos feitos por esse agente.»
Por outro lado, .agent(httpAgent) com um argumento define o http.Agent do Node para a solicitação, que é onde reside o suporte a proxy.
O mesmo nome de método, duas funções distintas, diferenciadas apenas pelo facto de se passar ou não um argumento. Se tiver lido sobre os agentes SuperAgent e sobre os agentes proxy na mesma sessão, vale a pena esclarecer isto antes de escrever qualquer código.
Opção 1: Um agente proxy
Esta é a abordagem a preferir, e a razão prende-se com a manutenção.
import request from "superagent";
import { HttpsProxyAgent } from "https-proxy-agent";
const agent = new HttpsProxyAgent("http://myuser:mypass@proxy.example.com:9000");
const res = await request
.get("https://api.example.com/items")
.agent(agent);
Para SOCKS, substitua o pacote:
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://proxy.example.com:1080");
Note que é socks5h
em vez de socks5
. A variante h
resolve nomes de host no proxy em vez de localmente, o que impede que as consultas DNS sejam encaminhadas para o seu próprio resolvedor enquanto o tráfego sai por outro lado — uma fuga que anula silenciosamente o objetivo de utilizar um proxy para geolocalização.
E o proxy-agent
lida com qualquer protocolo que a URL especifique, o que é útil quando o proxy provém de uma configuração:
import { ProxyAgent } from "proxy-agent";
const agent = new ProxyAgent(); // reads http_proxy / https_proxy / no_proxy
Porquê estes pacotes em vez da extensão SuperAgent: todos os três são mantidos ativamente. Verificado no registo npm em setembro de 2026, proxy-agent
está na versão 8.0.2, https-proxy-agent
na 9.1.0 e socks-proxy-agent
na 10.1.0, todas publicadas em junho de 2026.
Rota Dois: superagent-proxy
A extensão criada especificamente para este fim e a ressalva que a acompanha.
import request from "superagent";
import superagentProxy from "superagent-proxy";
superagentProxy(request);
const res = await request
.get("https://api.example.com/items")
.proxy("http://myuser:mypass@proxy.example.com:9000");
O seu ficheiro README descreve-a como uma extensão da «classe Request do superagent com uma função .proxy(uri)» e refere que «é suportada pelo módulo proxy-agent».
A API é mais prática do que passar um agente. A chamada .proxy(uri) fica mais legível numa cadeia de chamadas e aceita URIs «HTTP, HTTPS ou SOCKS», delegando a seleção do protocolo a proxy-agent.
A ressalva é a data de lançamento. superagent-proxy está na versão 3.0.0, publicada em setembro de 2021 — com cerca de cinco anos à data da redação deste artigo, enquanto a sua dependência subjacente proxy-agent continuou a ser atualizada. Não está obsoleta nem está avariada, mas é um invólucro fino que não evoluiu, ao passo que o que envolve sim.
A consequência prática: se já o utiliza e funciona, não há urgência. Para código novo, utilizar um agente proxy diretamente implica apenas uma linha adicional e remove uma camada obsoleta da sua árvore de dependências — e, uma vez que a extensão é um invólucro em torno precisamente desse agente, não perde nada além da sintaxe.
Verificar se funcionou mesmo
As quatro linhas mais importantes.
const res = await request
.get("https://api.ipify.org?format=json")
.agent(agent);
console.log(res.body);
Execute-o com o agente e sem ele. Se o endereço não mudar, o proxy não está no caminho. O SuperAgent não apresenta nenhum erro, nenhum aviso nem nenhuma solicitação falhada quando isto acontece — o tráfego simplesmente segue diretamente.
Três razões pelas quais uma configuração normalmente não tem efeito:
Chamou .agent() sem nenhum argumento, criando uma cópia do SuperAgent com persistência de cookies em vez de definir um agente HTTP. Esta é uma armadilha terminológica e produz exatamente este sintoma.
Aplicou o agente à solicitação errada. O encadeamento do SuperAgent é por solicitação, pelo que um agente definido numa chamada não se aplica à seguinte. Para um comportamento consistente, coloque a criação da solicitação numa função.
O tipo de agente não corresponde ao destino. Um «HttpsProxyAgent» lida com destinos HTTPS; um destino HTTP simples pode necessitar da variante HTTP. O «proxy-agent» contorna isto, escolhendo por si.
Para proxies com segmentação geográfica, a verificação do endereço não é suficiente. Verifique pelo resultado — solicite algo que diferencie genuinamente consoante a região e confirme se a resposta mudou. Um serviço de pesquisa que indique o país correto, enquanto a sua API devolve dados da sua região de origem, significa que a segmentação não está a atingir o destino pretendido, o que corresponde ao padrão de falha silenciosa que descrevemos em por que razão é importante testar proxies.
Tempos limite através de um proxy
O modelo de tempo limite do SuperAgent é excepcionalmente bom e vale a pena utilizá-lo corretamente quando um proxy aumenta a latência.
A documentação descreve duas configurações. req.timeout({deadline: ms})
— ou req.timeout(ms)
— «define um prazo para que toda a solicitação (incluindo todos os uploads, redirecionamentos e tempo de processamento do servidor) seja concluída. Se a resposta não for totalmente descarregada dentro desse prazo, a solicitação será abortada.» E req.timeout({response: ms})
«define o tempo máximo de espera pela chegada do primeiro byte do servidor, mas não limita a duração total do descarregamento.»
O próprio conselho da documentação sobre o dimensionamento é diretamente relevante para pedidos encaminhados por proxy: «O tempo limite de resposta deve ser, pelo menos, alguns segundos mais longo do que apenas o tempo que o servidor demora a responder, porque também inclui o tempo para efetuar a pesquisa de DNS, as ligações TCP/IP e TLS, e o tempo para enviar os dados do pedido.»
Através de um proxy, cada uma dessas fases tem um custo adicional. Uma saída residencial acrescenta latência real por pedido, e isso deve-se à distância e não a uma falha.
const res = await request
.get("https://api.example.com/items")
.agent(agent)
.timeout({ response: 15000, deadline: 60000 });
A documentação recomenda a utilização de ambos, e a razão é a mesma que em qualquer outro contexto: um tempo limite de resposta deteta um servidor que nunca responde, enquanto um prazo deteta um que responde e depois demora a enviar os dados. Nenhuma delas, por si só, abrange ambos os casos.
Defina os valores com base nas medições efetuadas através do proxy, em vez de o fazer por hábito, ou irá gerar falhas que parecem ser causadas por um proxy avariado, mas que são simplesmente latência que não teve em conta.
Tratamento de erros
O comportamento predefinido do SuperAgent difere do da maioria dos clientes e isso é importante neste contexto.
A documentação é enfática: «O SuperAgent considera as respostas 4xx e 5xx (bem como as respostas 3xx não tratadas) como erros por predefinição». Acrescenta ainda que «esta informação de estado estará disponível através de err.status», e que tais erros «também contêm um campo err.response».
Assim, uma falha na autenticação do proxy surge como uma rejeição, em vez de uma resposta:
try {
const res = await request.get(url).agent(agent).timeout({ deadline: 30000 });
return res.body;
} catch (err) {
if (err.status === 407) throw new Error("Proxy rejected credentials");
if (err.status === 401) throw new Error("Target requires authentication");
if (!err.status) throw new Error(`Network error: ${err.code} ${err.message}`);
throw err;
}
Vale a pena ter em conta a distinção entre 407 e 401. Um 407 significa que o proxy bloqueou o acesso e que o destino nunca foi alcançado; um 401 significa que o proxy funcionou e que o destino requer credenciais. São situações diferentes, com soluções diferentes, e são facilmente confundíveis quando ambas aparecem como erros lançados.
Um erro sem err.status significa que não chegou qualquer resposta HTTP, o que aponta para a ligação e não para a autenticação de alguém. ECONNREFUSED significa que nada está a ouvir no endereço do proxy; ETIMEDOUT significa que os pacotes estão a desaparecer.
Para tratar alguns códigos de erro como sucessos — interpretando um 404 como dados em vez de uma falha — utilize .ok():
.ok(res => res.status < 500)
Tentativas de repetição: com cuidado
O SuperAgent possui uma funcionalidade de repetição integrada, com uma restrição documentada que vale a pena respeitar.
const res = await request.get(url).agent(agent).retry(2);
A documentação explica que .retry() «irá repetir automaticamente as solicitações, caso estas falhem de forma transitória ou devido a uma ligação à Internet instável», aceitando um número opcional de tentativas (padrão 1) e uma função de retorno de chamada invocada «antes de cada nova tentativa». A função de retorno «pode devolver true/false para controlar se a solicitação deve ser repetida (mas o número máximo de tentativas é sempre aplicado)».
E a restrição, claramente indicada na documentação: utilize .retry() «apenas com solicitações que sejam idempotentes».
Através de um proxy, isto é mais importante do que o habitual, por uma razão específica. Um timeout não é prova de falha — a solicitação pode ter chegado ao destino e sido bem-sucedida, enquanto a resposta se perdeu no caminho de volta. Existe um salto adicional onde isso pode acontecer. Repetir uma chamada POST nessa situação pode duplicar uma gravação, e nenhuma configuração de repetição torna isso seguro. Quando a operação for importante, utilize uma chave de idempotência, se a API a disponibilizar.
O callback é também o local certo para evitar repetir uma tentativa após uma falha de autenticação, uma vez que um erro 407 com credenciais erradas produzirá um 407 em todas as tentativas:
.retry(3, (err, res) => {
if (res?.status === 407 || res?.status === 401) return false;
return true;
})
Um wrapper que vale a pena escrever
O encadeamento do SuperAgent é feito por pedido, o que significa que é fácil esquecer a configuração do proxy naquela chamada que realmente importa. Envolver a criação do pedido resolve esse problema e oferece um local onde colocar o resto das predefinições.
import request from "superagent";
import { ProxyAgent } from "proxy-agent";
const agent = process.env.PROXY_URL ? new ProxyAgent(process.env.PROXY_URL) : undefined;
const UA = "AcmeBot/1.0 (+https://acme.example.com/bot)";
function req(method, url) {
const r = request[method](url)
.set("User-Agent", UA)
.timeout({ response: 15000, deadline: 60000 })
.retry(2, (err, res) => {
if (res?.status === 407 || res?.status === 401) return false;
if (res?.status === 429) return false; // honour the rate limit instead
return true;
});
return agent ? r.agent(agent) : r;
}
export const get = url => req("get", url);
export const post = url => req("post", url);
export async function verifyExit() {
const res = await get("https://api.ipify.org?format=json");
console.log(`Exit address: ${res.body.ip}`);
return res.body.ip;
}
As cinco opções apresentadas foram escolhidas deliberadamente.
O proxy é opcional e provém do ambiente. Sem PROXY_URL, o agente é undefined e as solicitações são encaminhadas diretamente, o que faz com que o desenvolvimento local e a produção se comportem de forma previsível, sem ramificações no código da aplicação. Nenhuma credencial aparece no código-fonte.
ProxyAgent sem nenhum argumento no construtor seria interpretado como http_proxy e similares, caso prefira uma configuração orientada pelo ambiente; passar o URL explicitamente torna a fonte de verdade óbvia, o que normalmente tem mais valor.
O agente do utilizador é honesto e inclui um URL de contacto. Não custa nada e altera o que acontece quando um operador do site repara em si.
As tentativas de repetição excluem os estados em que repetir a tentativa é inútil ou indelicado. Um 407 com credenciais inválidas será sempre um 407; um 429 é uma instrução para abrandar, e tentar novamente converte um limite temporário num limite mais prolongado.
O ficheiroverifyExit() é exportado e chamado no arranque. Seis linhas que transformam um proxy silenciosamente contornado numa entrada nos registos — o que é o único tema recorrente do funcionamento do proxy em todos os clientes, e a única coisa que nenhuma biblioteca fará por si.
O «.connect()» não é um proxy
Vale a pena destacar isto porque parece ser um proxy, mas não é.
O SuperAgent disponibiliza um método «.connect()» que, de acordo com a documentação, permite «ignorar a resolução DNS e direcionar todos os pedidos para um endereço IP específico». Suporta um mapeamento, incluindo um fallback «*»:
const res = await request.get("http://redir.example.com:555")
.connect({
"redir.example.com": "127.0.0.1",
"www.example.com": false,
"mapped.example.com": { host: "127.0.0.1", port: 8080 },
"*": "proxy.example.com",
});
A documentação refere que «as solicitações manterão o seu cabeçalho Host com o valor original» e que .connect(undefined) desativa esta funcionalidade.
Trata-se de redirecionamento de host, não de proxy. Altera o endereço para o qual a ligação é estabelecida, mantendo a solicitação inalterada — não há nenhum túnel CONNECT, nenhum protocolo de proxy nem autenticação de proxy. Existe para fins de teste e a documentação coloca-a na secção «Testes no localhost» por uma boa razão.
A linha «"*": "proxy.example.com"» no exemplo oficial é a fonte da confusão. Utilize «.connect()» para direcionar os pedidos para um servidor de teste local; utilize um agente para um proxy real.
Perguntas frequentes
Como utilizo um proxy com o SuperAgent?
Passe um agente proxy para o .agent(): crie um HttpsProxyAgent ou um SocksProxyAgent com o URL do seu proxy e passe-o para o pedido. Em alternativa, utilize a extensão superagent-proxy, que adiciona um método .proxy(uri) — embora esse pacote não tenha sido lançado desde 2021.
Qual é a diferença entre .agent() com e sem argumentos?
Sem argumentos, cria uma cópia do SuperAgent com persistência de cookies, com o seu próprio ficheiro jar e opções predefinidas. Com um argumento, define o parâmetro http.Agent do Node para essa solicitação, sendo essa a forma como o suporte ao proxy é aplicado. O nome comum causa uma verdadeira confusão.
O superagent-proxy ainda é mantido?
Não está obsoleto, mas a versão 3.0.0 data de setembro de 2021, enquanto a sua dependência subjacente proxy-agent continuou a ser lançada — mais recentemente em junho de 2026. Para código novo, utilizar um agente proxy diretamente evita um wrapper desatualizado, com o custo de apenas uma linha adicional.
Porque é que o meu proxy do SuperAgent não está a funcionar?
Na maioria das vezes, porque .agent() foi chamado sem nenhum argumento, o que cria um «cookie jar» em vez de configurar um proxy. Verifique também se o agente foi aplicado à solicitação correta, uma vez que o encadeamento do SuperAgent é feito por chamada. Verifique solicitando um serviço que indique o seu endereço — um proxy contornado não produz nenhum erro.
O SuperAgent respeita as variáveis de ambiente HTTP_PROXY?
Por si só, não. O pacote proxy-agent lê http_proxy, https_proxy e no_proxy; por isso, criar um ProxyAgent() sem argumentos e passá-lo para .agent() proporciona-lhe um comportamento orientado pelo ambiente.
Como defino os tempos de espera para pedidos encaminhados por proxy?
Utilize ambas as definições: .timeout({ response: 15000, deadline: 60000 }). O tempo de espera da resposta limita a espera pelo primeiro byte, enquanto o prazo limite restringe todo o pedido. Defina-os com base em medições realizadas através do proxy, uma vez que uma saída residencial acrescenta latência real às fases de DNS, ligação e TLS.
Como distinguir um erro de proxy de um erro no destino?
Pelo código de estado. O SuperAgent trata os códigos 4xx e 5xx como erros, por isso detete e analise err.status — 407 significa que o proxy rejeitou o pedido e o destino nunca foi alcançado, enquanto 401 significa que o proxy funcionou e o destino requer credenciais. A ausência total de «err.status» significa que não chegou qualquer resposta HTTP.
Posso utilizar o .connect() como proxy?
Não. Este redireciona os pedidos para um IP específico, mantendo o cabeçalho «Host» original, o que corresponde a um mapeamento de host para fins de teste e não a um proxy. Não existe túnel, nem protocolo de proxy, nem autenticação. Utilize um agente para um proxy verdadeiro.
Conclusão
O SuperAgent não dispõe de uma opção de proxy própria, pelo que a escolha recai sobre um agente ou uma extensão — e o agente é a melhor opção por predefinição, uma vez que, em ambos os casos, são os pacotes mantidos que realizam o trabalho efetivo.
A terminologia é a principal armadilha. .agent() sem nenhum argumento fornece-lhe um «cookie jar»; .agent(something) define um agente HTTP. As pessoas configuram o primeiro, vêem que os pedidos são bem-sucedidos e concluem que o proxy está a funcionar. Ninguém as corrige, porque um proxy contornado falha silenciosamente, por definição.
O que torna a verificação um hábito que vale a pena cultivar. Solicite um serviço que indique o seu endereço, com e sem o agente, e confirme se a resposta muda. Para trabalhos com segmentação geográfica, vá mais longe e confirme se o conteúdo regionalmente distinto difere realmente — o endereço é a parte fácil e a menos informativa.
Em seguida, defina ambos os tempos de espera, utilize o «err.status» para que um 407 e um 401 conduzam a mensagens diferentes e mantenha o «.retry()» afastado de tudo o que não seja idempotente. Através de um proxy, existe um salto adicional em que uma solicitação bem-sucedida pode perder a sua resposta, e uma nova tentativa nessa situação é uma duplicação, em vez de uma recuperação.