Como corrigir HTTP 407 Proxy Authentication Required

Publicado:

19 min de leitura

Acar Diveroli
Autor: Acar Diveroli
Diante de um portão PROXY, os cartões NO AUTH e WRONG PASS param numa cruz vermelha; um cartão azul Proxy-Authorization passa

Você cola no cURL um proxy recém-comprado e a resposta é 407 Proxy Authentication Required. Os mesmos três dígitos aparecem no Python como Tunnel connection failed: 407, no Git como The requested URL returned error: 407 e no Chrome como uma janela de usuário que não para de voltar. Todos têm uma única origem: sua requisição nunca chegou ao site. O proxy no meio do caminho a parou no portão.

Este artigo explica o que o 407 significa no padrão HTTP, como ele difere do 401 e o que está faltando nesse portão. Depois passa pelos lugares em que o erro aparece: credenciais erradas ou ausentes em um proxy comprado, um IP residencial que mudou na autenticação por IP, um caractere especial na senha, o Chrome recusando a forma user:pass@, proxies NTLM e Kerberos em redes corporativas, e onde as credenciais entram no cURL, Python, Node.js, npm, pip, Git e Docker. Executamos todos os exemplos de código em 7 de outubro de 2026 contra um proxy de teste local que exige usuário e senha.

O que significa HTTP 407 Proxy Authentication Required?

Um 407 é o proxy dizendo "prove quem você é antes que eu encaminhe isto". O padrão HTTP, a RFC 9110, define o código na seção 15.5.8: o 407 é parecido com o 401, mas indica que o cliente precisa se autenticar para usar um proxy nesta requisição. O proxy deve enviar um cabeçalho Proxy-Authenticate com um desafio aplicável a esse proxy, e o cliente pode repetir a requisição com um cabeçalho Proxy-Authorization novo ou substituído.

Na prática, a troca acontece entre esses dois cabeçalhos. O proxy nomeia o esquema com Proxy-Authenticate: Basic realm="x"; no Basic, de longe o esquema mais comum, o cliente junta usuário e senha como user:pass, codifica a string em Base64 e adiciona Proxy-Authorization: Basic dXNlcjpwYXNz. Base64 não é criptografia; qualquer um que decodifique dXNlcjpwYXNz lê user:pass, então as credenciais dependem de uma rede confiável ou de TLS no caminho até o proxy.

Um detalhe do padrão ajuda na depuração: Proxy-Authorization é consumido pelo primeiro proxy que o recebe e não é encaminhado ao site. O proxy vê sua senha; o site, não. Quando vários proxies formam uma cadeia em uma rede corporativa, cada um exige as próprias credenciais, e o valor realm em Proxy-Authenticate diz qual deles enviou o 407.

Qual é a diferença entre 407 e 401?

Os dois códigos executam o mesmo mecanismo em duas partes diferentes. 401 Unauthorized é o site pedindo credenciais: a resposta traz WWW-Authenticate, o cliente responde com Authorization e o problema está entre você e o site. Em um 407, o mesmo diálogo acontece com o proxy, usando Proxy-Authenticate e Proxy-Authorization, e o site nem chegou a ver a requisição.

Essa distinção decide onde você procura. Depois de um 401, você confere sua conta, chave de API ou token no site. Depois de um 407, nada no site importa; o usuário, a senha ou a lista de IPs no painel do proxy, ou a conta que o administrador da rede lhe deu, é onde o problema mora. Você também pode receber os dois em sequência: primeiro o proxy é passado com um 407, depois o site devolve 401.

Como um 407 acontece, passo a passo?

Para observar o fluxo, escrevemos um proxy de teste de 40 linhas em Node.js. Ele responde a toda requisição cujo cabeçalho Proxy-Authorization não bate com user:pass com um 407 e Proxy-Authenticate: Basic realm="x", encaminha as requisições que batem, aplica a mesma regra ao túnel CONNECT usado pelo HTTPS e registra cada requisição. Para uma página HTTPS, o log ficou assim:

  1. O cliente envia CONNECT site.com:443 ao proxy, pedindo um túnel até o site. Para um endereço http://, ele envia a própria requisição (GET http://site.com/ …).
  2. Se a requisição não tem Proxy-Authorization, o proxy devolve 407 Proxy Authentication Required com Proxy-Authenticate: Basic realm="x" e não abre nada. A linha do log diz CONNECT site.com:443 Proxy-Authorization: (nenhum).
  3. O cliente adiciona o cabeçalho e repete a requisição: Proxy-Authorization: Basic dXNlcjpwYXNz. Muitas ferramentas fazem isso na primeira tentativa; o cURL com -U nunca espera o 407.
  4. O proxy confere o valor. Se estiver certo, abre o túnel e devolve 200 Connection Established; daqui em diante o tráfego TLS passa pelo túnel. Se estiver errado, repete o passo 2; senha errada e senha ausente são o mesmo 407 para um proxy.
  5. O site vê a requisição só agora e dá a própria resposta (200, 404, 401…). Os cabeçalhos que ele recebeu não incluíam Proxy-Authorization; o proxy o consumiu.

Com o cURL, o primeiro passo desse fluxo aparece em um único comando. Para um endereço HTTP simples, uma requisição sem autenticação recebeu esta resposta:

bash
curl -i -x http://pr.proxynet.io:8000 http://example.com/
text
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="x"
Connection: close

Para um endereço HTTPS, o cURL informa em vez disso o próprio erro: curl: (56) CONNECT tunnel failed, response 407. Os dois são o mesmo cabeçalho ausente.

Por que o erro 407 acontece?

As causas se dividem em dois grupos: as credenciais de um proxy que você comprou e as credenciais do proxy que a sua rede opera. O texto do erro é idêntico nos dois; o lugar de corrigir, não.

CausaSintomaCorreção
Credenciais nunca enviadasSem linha Proxy-Authorization, 407 na primeira requisiçãoUse a opção de credenciais de proxy da ferramenta (-U, proxies=, token)
Usuário ou senha erradosCabeçalho presente, 407 continuaCopie do painel; confira espaços e maiúsculas
@, : ou # na senhaURL "bad hostname" ou 407Codifique como %40, %3A, %23 na URL, ou use uma opção separada
Autenticação por IP, IP residencial mudouA configuração de ontem falha hojeAdicione o novo IP no painel ou mude para usuário e senha
Chrome ignora user:pass@ no endereçoERR_NO_SUPPORTED_PROXIES ou uma janela de loginpage.authenticate() no Puppeteer, a janela no navegador
Proxy corporativo quer NTLM ou KerberosProxy-Authenticate: Negotiate ou NTLMConta de domínio com --proxy-negotiate; NTLM está desligado nas builds do cURL desde a 8.20
Uma cadeia de proxiesA janela não para de reaparecerCada proxy quer as próprias credenciais; pergunte à equipe de rede

Como corrigir um 407 em um proxy comprado?

A maioria dos 407 vem de uma pequena diferença entre o que você copiou do painel e o formato que a ferramenta espera. Primeiro confira se as credenciais são enviadas: se curl -v não mostra nenhuma linha > Proxy-Authorization:, o problema é o formato. Se a linha está lá e a resposta continua 407, o valor está errado ou a conta tem um problema; no nosso teste, o proxy respondeu a uma senha errada exatamente como respondeu a nenhuma senha.

Caracteres especiais. Um endereço de proxy é uma URL na forma http://user:pass@host:port, e em uma URL o sinal @ separa as informações de usuário do host. Se a sua senha é p@ss, o endereço contém dois sinais @; o cURL 8.5 o recusou com curl: (5) Unsupported proxy syntax … Bad hostname. Com %40 no lugar do @ (http://user:p%40ss@host:port), o proxy decodificou user:p@ss e a requisição passou. Use %3A para :, %23 para # e %2F para /. O Python Requests aceitou também o endereço sem codificar, porque dividiu a URL no último @, mas codificar funciona igual em toda ferramenta. Opções que recebem as credenciais fora da URL (curl -U "user:p@ss", HTTPProxyAuth("user", "p@ss") no Requests) não precisam de codificação; as duas passaram com a senha crua.

Autenticação por IP. Se você registrou seu endereço IP no painel em vez de uma senha, o proxy olha o endereço de onde a conexão vem. Em uma conexão residencial, o IP público muda quando o modem reinicia ou a operadora rotaciona sua faixa de endereços, e a configuração que funcionava ontem devolve 407 hoje. Compare seu IP atual com o endereço salvo no painel. Se o seu endereço muda com frequência, usuário e senha é o método com menos surpresas; nosso guia de autenticação de proxy compara os dois.

Parâmetros de sessão. Parâmetros anexados ao usuário para sessões sticky ou escolha de país, como user-session-abc-country-tr, fazem parte do usuário; um erro de digitação ou um parâmetro sem suporte volta como 407 em alguns provedores. Tente primeiro um user:pass simples e recoloque os parâmetros um a um. Em um plano de Proxies residenciais, eles definem a duração da sessão e o país de saída, então separar um erro de parâmetro de um erro de senha economiza tempo.

Como corrigir um 407 em um proxy corporativo?

Na rede de uma empresa ou universidade, não foi você quem configurou o proxy; o sistema operacional o pega de um arquivo de configuração automática (PAC) ou de uma política de grupo, e as credenciais costumam ser a sua conta de domínio. Esses proxies pedem com frequência Proxy-Authenticate: Negotiate (Kerberos) ou NTLM em vez de Basic. Navegadores e Office usam a conta do Windows em que você está logado e passam em silêncio; ferramentas de linha de comando e scripts não, e devolvem 407.

O cURL deixa você escolher o esquema: --proxy-negotiate -U : usa a identidade da sessão para Kerberos, e --proxy-anyauth escolhe um método adequado entre os que o proxy oferece. No nosso teste, com --proxy-anyauth o cURL enviou primeiro uma requisição sem autenticação, leu o 407 e seu Proxy-Authenticate: Basic, depois repetiu com Basic; uma ida e volta a mais, mas útil quando você não sabe o que o proxy espera. NTLM é outro assunto. Desde a versão 8.20.0 (29 de abril de 2026), o projeto curl desliga o NTLM por padrão na build e planeja removê-lo por completo, citando a criptografia fraca do esquema, sua incompatibilidade com HTTP/2 e HTTP/3 e a própria descontinuação do NTLM pela Microsoft. Com o cURL 8.20 ou mais novo, --proxy-ntlm não funciona mais na maioria das distribuições; se o proxy aceita só NTLM, peça à equipe de rede que ative o Negotiate. Nosso cURL 8.5 ainda enviou a primeira mensagem do NTLM com --proxy-ntlm; nosso proxy de teste, que só aceita Basic, respondeu 407 de novo.

A janela que não para de reabrir no Windows. As configurações de proxy do Windows guardam só o endereço, não um usuário e uma senha; cada aplicativo pede as credenciais por conta própria quando recebe um 407. Se a janela fecha e reabre na hora, as credenciais foram recusadas: depois de uma troca de senha recente, o Gerenciador de Credenciais do Windows continua tentando a senha antiga salva até você apagá-la. Uma nota de suporte antiga da Microsoft descreve outra causa: quando vários proxies que exigem NTLM formam uma cadeia, o primeiro é passado, o segundo devolve 407 e a janela reaparece; a correção sugerida é ativar o Negotiate com Kerberos, o que é trabalho da equipe de rede.

Por que o Chrome e o Puppeteer mostram o 407 de outro jeito?

O Chrome não usa um usuário e uma senha escritos no endereço do proxy. A documentação de proxy do Chromium diz isso com clareza: o navegador não implementa credenciais embutidas nas configurações de proxy, e a autenticação de proxy passa pelo fluxo comum, ou seja, a janela que segue um 407. Proxies HTTP aceitam Basic, Digest, Negotiate e NTLM; o SOCKS5 não aceita autenticação nenhuma. Escrever user:pass@ em --proxy-server, portanto, não faz nada. No teste para o nosso artigo de Puppeteer de 6 de outubro, o Chrome 154 recusou o valor inteiro com net::ERR_NO_SUPPORTED_PROXIES; sem as credenciais em lugar nenhum, a primeira navegação falhou com net::ERR_INVALID_AUTH_CREDENTIALS. O 407 nunca chega ao usuário nessa versão; o Chrome o transforma no próprio código de erro.

A solução é colocar as credenciais onde a resposta do navegador ao 407 é montada. No Puppeteer, isso é page.authenticate(), e ele precisa vir antes do primeiro page.goto():

js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.proxynet.io:8000"], // sem credenciais no endereço
});
const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" }); // a resposta ao 407
await page.goto("https://httpbin.org/ip");

Segundo a documentação do Puppeteer, a chamada liga a interceptação de requisições nos bastidores e é desligada passando null. As credenciais pertencem ao contexto do navegador, não à página; toda página do mesmo contexto usa o primeiro valor informado. Em um Chrome que você opera à mão, você as digita na janela que abre depois do 407.

Como passar as credenciais do proxy em cada ferramenta?

Cada ferramenta recebe as credenciais em um lugar diferente. Todos os exemplos passaram pelo proxy de teste local; o endereço aparece como pr.proxynet.io:8000 e as credenciais como user:pass.

cURL. As credenciais vão no endereço ou em uma opção separada; -U e --proxy-user são a mesma coisa. O cURL usa Basic por padrão e envia as credenciais na primeira requisição.

bash
# credenciais em uma opção separada: não precisa codificar caracteres especiais
curl -x http://pr.proxynet.io:8000 -U user:pass https://httpbin.org/ip

# credenciais no endereço: %40 para @, %3A para :
curl -x http://user:p%40ss@pr.proxynet.io:8000 https://httpbin.org/ip

# ver qual esquema o proxy espera
curl -v -x http://pr.proxynet.io:8000 https://httpbin.org/ip 2>&1 | grep -i proxy-auth

As variáveis de ambiente aceitam o mesmo endereço: export HTTPS_PROXY=http://user:pass@pr.proxynet.io:8000, com HTTP_PROXY para HTTP simples e NO_PROXY para os hosts a ignorar. As demais opções de proxy do cURL estão no nosso guia de cURL com proxy.

Python Requests. As credenciais vão no endereço do dicionário proxies ou em HTTPProxyAuth, que adiciona o cabeçalho Proxy-Authorization. A documentação do Requests define a forma http://user:password@host/ e avisa que guardar usuário e senha em uma variável de ambiente ou em um arquivo sob controle de versão é um risco de segurança.

python
import requests
from requests.auth import HTTPProxyAuth

PROXY = "http://user:pass@pr.proxynet.io:8000"
proxies = {"http": PROXY, "https": PROXY}

r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=15)
print(r.status_code, r.json())

# credenciais separadas do endereço: não precisa codificar caracteres especiais
auth = HTTPProxyAuth("user", "pass")
r = requests.get("https://httpbin.org/ip", proxies={"https": "http://pr.proxynet.io:8000"}, auth=auth, timeout=15)

Os dois caminhos passaram pelo proxy no nosso teste com o Requests 2.34. A forma do erro depende do destino: para um endereço HTTPS, uma requisição sem autenticação levantou requests.exceptions.ProxyError com Tunnel connection failed: 407 Proxy Authentication Required na mensagem; para um endereço HTTP simples não houve exceção, r.status_code foi 407 e r.headers["Proxy-Authenticate"] trazia o desafio. Um script que só captura exceções perde o 407 em endereços HTTP; ele precisa de uma verificação r.status_code == 407. HTTPProxyAuth adiciona só o cabeçalho Proxy-Authorization; o login do próprio site passa por HTTPBasicAuth. Para enviar uma série de requisições POST por uma mesma sessão, adicione proxies à configuração de Session do nosso guia de POST e JSON com Requests.

Node.js e undici. O fetch embutido do Node passa por um proxy com a classe ProxyAgent do undici. As credenciais vão em uri ou na opção token como um valor de cabeçalho pronto; a documentação do ProxyAgent do undici descreve os dois e observa que as credenciais na URI são decodificadas como URL antes de serem codificadas em Base64.

js
import { fetch, ProxyAgent } from "undici";

const agent = new ProxyAgent({
  uri: "http://pr.proxynet.io:8000",
  token: `Basic ${Buffer.from("user:pass").toString("base64")}`, // o valor de Proxy-Authorization
});

const res = await fetch("https://httpbin.org/ip", { dispatcher: agent });
console.log(res.status, await res.json());

Com o undici 8.10, token e uma uri contendo user:p%40ss deram o mesmo resultado. Uma requisição sem autenticação falhou com TypeError: fetch failed; o motivo real estava em error.cause como InvalidArgumentError: Proxy Authentication Required (407), e para um endereço HTTPS dizia apenas Request was cancelled. O Node também não lê as variáveis de ambiente de proxy por conta própria: com HTTPS_PROXY definida, o fetch continuou conectando direto. A variável NODE_USE_ENV_PROXY=1, introduzida no Node 22.21 e 24.0, liga isso; no nosso teste funcionou com um aviso de recurso experimental.

npm, pip, Git e Docker. Os quatro aceitam o mesmo formato de endereço, com as credenciais dentro do endereço.

bash
# npm: configuração permanente (a variável de ambiente HTTPS_PROXY também é lida)
npm config set proxy http://user:pass@pr.proxynet.io:8000
npm config set https-proxy http://user:pass@pr.proxynet.io:8000

# pip: --proxy para um único comando, proxy = no pip.conf de forma permanente
pip install --proxy http://user:pass@pr.proxynet.io:8000 requests

# Git: formato [protocol://][user[:password]@]proxyhost[:port]
git config --global http.proxy http://user:pass@pr.proxynet.io:8000
git config --global http.proxyAuthMethod basic   # anyauth, basic, digest, negotiate, ntlm

Para o cliente Docker, a configuração vai no objeto proxies.default (httpProxy, httpsProxy, noProxy) em ~/.docker/config.json; a documentação do Docker observa que esses valores ficam em texto puro na configuração do contêiner e podem ser lidos pela API remota. No nosso teste, o Git disse fatal: unable to access '…': The requested URL returned error: 407 sem credenciais e passou com elas. O pip escondeu o 407 na saída padrão atrás de Could not find a version that satisfies the requirement; a causa real apareceu com pip -vvv: 407 Client Error: Proxy Authentication Required.

FerramentaComo mostra um 407Onde vão as credenciais
cURLCONNECT tunnel failed, response 407-U user:pass ou o endereço
Python RequestsProxyError em HTTPS, status_code == 407 em HTTPEndereço em proxies= ou HTTPProxyAuth
Node undicifetch failed, motivo em causeProxyAgent token ou uri
Chrome / PuppeteerERR_INVALID_AUTH_CREDENTIALS, uma janelapage.authenticate()
GitThe requested URL returned error: 407Endereço em http.proxy
pipSó com -vvv--proxy ou pip.conf

Como um IP estático evita o 407?

Na autenticação por IP, a causa mais frequente de um 407 é um endereço que mudou. De um IP público fixo, como um servidor na nuvem ou uma linha de escritório, você preenche a lista uma vez e não carrega senha no código; de uma conexão residencial ou de redes que mudam, usuário e senha causa menos interrupções.

A estabilidade também importa do lado do proxy. Em um plano de Proxies ISP, o IP de saída não muda, então um único endereço entra na lista de permissões do site ou de um sistema corporativo e nessa ponta também não há problema de credenciais.

Erros comuns

  • Procurar o erro no site. Uma requisição com 407 nunca chegou ao site; a conta, os cookies ou a chave de API dele não mudam o resultado.
  • Escrever uma senha sem codificar na URL. Uma senha com @ quebra o endereço; use %40 ou uma opção de credenciais separada.
  • Colocar user:pass@ na flag do Chrome. O Chromium ignora; use page.authenticate() no Puppeteer e a janela à mão.
  • Capturar só exceções. O Requests não levanta nada para um endereço HTTP simples e devolve um objeto de resposta com 407; confira o código de status.
  • Confiar no IP antigo. Na autenticação por IP, a configuração de ontem pode não bater com o endereço de hoje.
  • Escrever credenciais em scripts ou repositórios. Tanto a documentação do Requests quanto a do Docker chamam isso de risco; use uma variável de ambiente limitada à sessão ou um gerenciador de segredos.
  • Esperar NTLM depois do cURL 8.20. O NTLM está desligado na build; peça Negotiate (Kerberos) nos proxies corporativos.

Guia de decisão

Sua situaçãoO que fazer
Sem Proxy-Authorization em curl -vAdicione as credenciais com -U ou no endereço
Cabeçalho presente, 407 continuaCopie a senha do painel de novo; tente um user:pass simples
@, : ou # na senha%40, %3A, %23 na URL, ou uma opção de credenciais separada
Autenticação por IP, endereço mudouNovo IP no painel; mude para usuário e senha se ele muda com frequência
Chrome ou Puppeteerpage.authenticate(); sem credenciais na flag
Rede da empresa, Proxy-Authenticate: Negotiate--proxy-negotiate -U : ou a conta de domínio; Kerberos em vez de NTLM
A janela não some no WindowsApague a senha antiga no Gerenciador de Credenciais; pergunte à equipe de rede sobre proxies encadeados

Perguntas frequentes

O que significa o erro HTTP 407?

Significa que o proxy quer credenciais antes de encaminhar sua requisição, e a requisição não levava nenhuma ou levava as erradas. O cabeçalho Proxy-Authenticate da resposta nomeia o esquema esperado; a solução é adicionar um cabeçalho Proxy-Authorization com o valor correto.

Qual é a diferença entre 407 e 401?

Em um 401, o site pede credenciais, usando WWW-Authenticate e Authorization; em um 407, quem pede é o proxy no meio do caminho, usando Proxy-Authenticate e Proxy-Authorization. Uma requisição que recebeu 407 nunca chegou ao site.

Por que recebo 407 se a senha do meu proxy está correta?

Três causas frequentes: um caractere especial da senha não está codificado na URL, a ferramenta não lê credenciais do endereço (o Chrome funciona assim) ou a conta está no modo de autenticação por IP e o seu endereço atual não está na lista. Confira com curl -v se alguma linha Proxy-Authorization é enviada.

Como escrevo uma senha com o sinal @ no endereço do proxy?

Troque @ por %40: http://user:p%40ss@host:port. Pela mesma lógica, use %3A para : e %23 para #. Opções separadas, como -U "user:p@ss" no cURL ou HTTPProxyAuth("user", "p@ss") no Requests, não precisam de codificação.

Onde informo o usuário e a senha do proxy no Chrome?

As configurações de proxy do Chrome não têm campo de usuário, e o navegador ignora a parte user:pass@ de um endereço. Você digita as credenciais na janela que abre depois do primeiro 407; em ferramentas de automação como o Puppeteer, page.authenticate({ username, password }) substitui essa janela.

Recebo 407 no proxy da minha empresa. O que fazer?

O proxy provavelmente quer NTLM ou Kerberos (Negotiate) e espera a sua conta de domínio. Tente --proxy-negotiate -U : no cURL; como o NTLM está desligado nas builds do cURL a partir da 8.20, peça à equipe de rede que ative o Negotiate para um proxy que só aceita NTLM.

Em resumo

HTTP 407 Proxy Authentication Required diz que o proxy que encaminha sua requisição quer credenciais; o site não sabe de nada. A solução é o mesmo cabeçalho com o valor correto em cada ferramenta: -U no cURL, o endereço em proxies= ou HTTPProxyAuth no Requests, token no undici, page.authenticate() no Puppeteer e o endereço http://user:pass@host:port no npm, pip, Git e Docker. Um @ na senha vira %40, a autenticação por IP precisa do seu endereço atual na lista, e as redes corporativas falam Negotiate em vez de Basic, com o NTLM desligado nas builds do cURL desde a 8.20. Para planos que funcionam com usuário e senha ou com uma lista de IPs, veja nossos serviços de proxy.