Skip to content

How to Fix 407 Proxy Authentication Required

What a 407 means, what curl, Python, PowerShell, npm and Java print for it, and the fix for each cause: the login, symbols in the password, NTLM.

HProxy TeamJuly 3, 2026Updated September 27, 202614 min read

How to Fix 407 Proxy Authentication Required

A 407 Proxy Authentication Required comes from the proxy between you and the site, not from the site. The proxy wants a login before it forwards anything, and it did not get one it could use: none was sent, the one sent was changed on the way, or it came in a form the proxy does not accept.

What is a 407 Proxy Authentication Required error?

407 Proxy Authentication Required

HTTP status code

The proxy's version of a 401. The proxy between you and the site wants a login before it forwards anything, and it did not get one it could use: none was sent, the one sent was changed on the way, or it came in a form the proxy does not accept.

Source: RFC 9110; HProxy · hproxy.comProxy.

We reproduced each of those on 27 September 2026. We set up a site that only a proxy can reach and four proxies on our own machine. Then we ran 129 test cases from curl, Python, PowerShell and two shells, each of them twice. This page shows what each tool printed and what fixed it.

What a 407 means

The HTTP standard defines 407 as the proxy's version of a 401: the client "needs to authenticate itself in order to use a proxy for this request". A proxy that answers 407 has to name the login it wants in a Proxy-Authenticate header. The client answers with a Proxy-Authorization header, which goes to that proxy and no further (RFC 9110).

This is the whole exchange as curl -v printed it in our lab, for an https:// site:

> CONNECT site.lab.test:443 HTTP/1.1
> Host: site.lab.test:443
> User-Agent: curl/8.21.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 407 Proxy Authentication Required
< Proxy-Authenticate: Basic realm="lab"
< Content-Type: text/plain
< Content-Length: 34
<
curl: (7) CONNECT tunnel failed, response 407

For an https:// site the client first asks the proxy to open a tunnel with a CONNECT request, and the 407 answers that request. The site is not involved yet. In all 44 of our cases that ended in a 407, the site received nothing.

With a login, the same CONNECT carried one more line:

> Proxy-Authorization: Basic bGFidXNlcjpsYWJwYXNz

That string is labuser:labpass in base64, which is a way of writing, not encryption. Basic sends the user name and password "over the network as cleartext" (RFC 7617). The CONNECT also goes out before the encryption with the site starts. So with an http:// proxy, the password crosses the network readable, even when the site is https://.

Three status codes get mixed up here, and they come from two places:

CodeSent byWhat it says
401 Unauthorizedthe sitelog in to the site
403 Forbiddenthe siteunderstood, and refused
407 Proxy Authentication Requiredthe proxylog in to the proxy first

If yours is the site-side refusal, see how to fix 403 Forbidden through a proxy.

What each tool prints

Every tool words the same 407 differently. Find yours:

ToolWhat it prints for a 407
curl 8.20.0 and newercurl: (7) CONNECT tunnel failed, response 407
curl 7.87.0 to 8.19.0curl: (56) CONNECT tunnel failed, response 407
curl 7.86.0 and oldercurl: (56) Received HTTP code 407 from proxy after CONNECT
curl, http:// sitenothing, exit 0; with --fail: curl: (22) The requested URL returned error: 407
Python requests, and pipProxyError('Cannot connect to proxy.', OSError('Tunnel connection failed: 407 Proxy Authentication Required'))
Python requests, http:// siteno exception; status_code is 407
Python urllib<urlopen error Tunnel connection failed: 407 Proxy Authentication Required>
httpxhttpx.ProxyError: 407 Proxy Authentication Required
PowerShell 7 and .NETThe proxy tunnel request to proxy '...' failed with status code '407'."
PowerShell 7, http:// siteResponse status code does not indicate success: 407 (Proxy Authentication Required).
npm 10.9 and 11.8npm error code E407
Node.js fetch with undici's ProxyAgentProxy response (407) !== 200 when HTTP Tunneling
axiosRequest failed with status code 407
gitCONNECT tunnel failed, response 407
Javano exception; the status code is 407
GradleReceived status code 407 from server: Proxy Authentication Required
Chromea sign-in box

curl changed the exit code in version 8.20.0, released on 29 April 2026. A refused tunnel used to exit with 56 and now exits with 7, with the same message. A script that checks for 56 needs to check for 7 as well. The older wording, Received HTTP code 407 from proxy after CONNECT, was replaced in curl 7.87.0 in December 2022, and many guides still quote it. On our Windows 11 test machine, the curl in Windows was 8.21.0 and Git for Windows had brought its own 8.16.0, so the same machine gave both codes.

On a plain http:// site there is no tunnel. The 407 is the answer itself, and curl, requests and Java hand it back as a normal response. A script that only catches errors will miss it. Add --fail to curl, call raise_for_status() in requests, or check the status code.

PowerShell writes its message in the language of Windows. Only the 407 and the reason phrase from the proxy stay the same, so search for the number, not the sentence. The odd ." at the end of the tunnel message is really in the .NET source.

Behind a Squid proxy, cancelling the browser's sign-in box shows a page titled "ERROR: Cache Access Denied", which Squid sends with its 407. Why Chrome keeps asking is covered in the sign-in box guide. Postman takes a proxy login under its proxy settings: "This proxy requires authentication" for the default proxy, "Proxy auth" for a custom one. Gradle takes it as systemProp.https.proxyUser and systemProp.https.proxyPassword in gradle.properties.

The lines for npm, pip, git, Node.js, Java and .NET come from our own tests behind these guides: npm, pip, git, Node.js, Java and C# and .NET. For Python, see requests and httpx.

The causes, and the fix for each

Six things produced a 407 in our tests. They are listed in the order to check them.

Six causes of a 407, in the order to check them
  1. No login reached the proxy

    the tool never sent one

  2. The login went in a header the proxy never sees

    for an https:// site it travels inside the tunnel

  3. A symbol in the password changed it on the way

    @, #, /, ? and % must be encoded in the proxy URL

  4. The proxy wants NTLM, and the tool speaks only Basic

    Proxy-Authenticate names NTLM or Negotiate

  5. Your address is not on the allowlist

    from any other address, the line needs its login

  6. Java, with the right password

    Basic is off for https:// tunnels by default

Source: HProxy, our lab of 27 September 2026 · hproxy.comProxy.
Our own console window: the 407 lab of 27 September 2026. curl 8.21.0 exits with 7 and curl 8.16.0 with 56 when the proxy refuses the tunnel. Lower-case -u sends no login to the proxy. A raw @ in the proxy URL stops curl with exit 5, %40 works. pass%41word given with -U reaches the proxy as passAword. Against an NTLM-only proxy, curl 8.21.0 cannot answer and curl 8.16.0 can. requests fails with a raw #, urllib accepts it. A Proxy-Authorization header and HTTPProxyAuth never reach the proxy for an https site. PowerShell 7 answers NTLM. In Git Bash, pa$word in double quotes reaches the proxy as pa.
Captured on our own machine on 27 September 2026: a summary of our second run. Every case gave the same result in both runs.

1. No login reached the proxy

The plain case: the tool never sent a login to the proxy. In curl, both of these reached our proxy with the right password:

curl -x http://proxy.example.com:8080 -U 'user:pass' https://example.com/
curl -x http://user:pass@proxy.example.com:8080 https://example.com/

Lower-case -u is the login for the site. With it, our proxy received no login and answered 407, for https:// and http:// sites alike. The proxy login is capital -U, or --proxy-user.

In Python, the login goes in the proxy URL, http://user:pass@proxy.example.com:8080, either in proxies= or in HTTPS_PROXY. requests read it from both. In PowerShell 7 it goes in -ProxyCredential:

$cred = Get-Credential
Invoke-WebRequest https://example.com/ -Proxy http://proxy.example.com:8080 -ProxyCredential $cred

PowerShell sent the login only after the proxy had answered 407 once, so a proxy log shows one refused request first. A login written into the -Proxy address did nothing: the proxy received none. The error message then printed that address with the password in it, so the password ends up in any log that keeps the error.

2. The login went in a header the proxy never sees

Some guides build the Proxy-Authorization header by hand and add it to the request. For an https:// site that header never reaches the proxy. The request's headers travel inside the tunnel, to the site, and the proxy only sees the CONNECT. In our lab, curl -H and headers= in requests and in httpx all got a 407, and the proxy saw no login.

That is worse than a failed request. Through a proxy that needed no login, the same header arrived at the site, password included, from all three.

requests has a helper for proxy logins, HTTPProxyAuth, with the same limit. It worked for an http:// site, where the proxy receives the whole request, and failed with a 407 for an https:// one. Put the login in the proxy URL instead. In curl, a header meant for the proxy goes in --proxy-header, which delivered it and got through.

3. A symbol in the password changed it on the way

A password can be right and still arrive wrong. The proxy address is a URL, and some characters mean something in a URL. We gave our proxy accounts whose passwords held one symbol each, and typed them into the proxy URL as they are:

Symbol → write it ascurlrequests
@ → %40exit 5works
: → as it isworksworks
# → %23exit 5InvalidURL
/ → %2Fexit 5InvalidURL
? → %3Fexit 5InvalidURL
%41 → %2541turns into Aturns into A

Both curl versions behaved the same. Python's built-in urllib took every one of these as it is, except %41, which turned into A there too. curl stops before it sends anything, with curl: (5) Unsupported proxy syntax. A /, ? or # ends the address part of a URL (RFC 3986), so curl read the rest of the password as a port number. The colon is safe, because the first colon separates the user name from the password. Percent-encoded, every one of these passwords reached the proxy intact in both curls, requests and urllib.

Whether a raw @ works depends on the tool. curl refused it and git sent a wrong login, while requests, urllib and npm got it right. %40 worked in all of them.

curl's -U takes @ # / ? as they are, which makes it the easy way out. It is not fully literal, though. curl decodes percent codes in -U too, as its documentation says, so pass%41word given with -U arrived as passAword. Write a % as %25 even there.

The shell can change the password before curl sees it. Inside double quotes, Git Bash and PowerShell both read $word in pa$word as a variable, found nothing, and sent pa. The proxy answered 407. In single quotes the password arrived intact:

curl -x 'http://user:pa$word@proxy.example.com:8080' https://example.com/

Put a login with a $ in it inside single quotes.

4. The proxy wants NTLM, and the tool only speaks Basic

A company proxy may want the Windows sign-in, NTLM or Negotiate, instead of Basic. The Proxy-Authenticate line in curl -v tells you: it names NTLM or Negotiate. We ran a proxy that accepts only NTLM and tried each client with the right password:

ClientAgainst an NTLM-only proxy
Python requests and urllibsent Basic, got 407 again
PowerShell 7, -ProxyCredentialanswered NTLM, got through
curl 8.16.0 (Git for Windows)407 by default; got through with --proxy-ntlm or --proxy-anyauth
curl 8.21.0 (in Windows)--proxy-ntlm: "the installed libcurl version does not support this"

curl made NTLM an optional part of the build in version 8.20.0, and plans to remove it in September 2026. Microsoft has deprecated NTLM as well, in favour of Negotiate with Kerberos. Run curl -V: if NTLM is missing from the Features line, that curl cannot answer such a proxy. Which curl runs depends on your PATH. On our test machine, PowerShell 7, cmd and Git Bash all started Git's curl, not the one in Windows.

When the proxy takes your Windows sign-in, PowerShell has -ProxyUseDefaultCredentials, and a curl built with SSPI takes -U :, a single colon, to use the account you are logged in with. We did not run those two, because no test was ever given the Windows account of the machine. Python's requests and urllib have no NTLM of their own: both sent Basic and got the 407 again. Our pip guide covers the workaround for pip. Gradle wants the Windows domain as well, as domain/username in http.proxyUser.

5. Your address is not on the allowlist

Some proxies check where a request comes from instead of asking for a password. With us, a whitelisted IP connects without any login, and a Residential Premium plan takes up to 150 addresses (our docs). From any other address, the line needs its user name and password.

Addresses change: a new network, a VPN, or a cloud server whose outgoing address is not your office's. Check the address the proxy sees from the machine that sends the requests, for example with our IP checker, and compare it with your list. Two limits from our docs. A whitelisted connection carries no user name, so it takes no country or city targeting. And every device behind a shared office, VPN or cloud address spends your plan's traffic, so whitelist only addresses you alone control.

6. Java: the right password, and still a 407

Java turns off Basic login for https:// tunnels by default. In our test, Java 17 and Java 8 sent the CONNECT without the password and returned 407, without an exception. Clearing one property as the first line of main fixed it:

System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

Why it has to be the first line is in our Java proxy guide.

Test it in one line

Before you change any code, reproduce the 407 with curl. It shows what the proxy asks for and what you send:

curl -v -x http://proxy.example.com:8080 -U 'user:pass' -o /dev/null https://example.com/

Read three lines. Proxy-Authenticate names the login the proxy wants. Proxy-Authorization shows whether yours went out. The last line says whether the tunnel opened. On Windows, write -o NUL. In Windows PowerShell 5.1, type curl.exe: there, curl runs Invoke-WebRequest, not curl. The curl proxy guide covers the other options.

If curl gets through and your program does not, the login is fine and the problem is how your program passes it: see the causes above. If curl fails with another code, the curl error guide explains each one. To rule out the network, try a proxy that needs no login, from our free proxy list or its API, which needs no key. Our proxy checker tests a line with or without its login, on our site. If the proxy answers but the site times out, that is a different error: 504 Gateway Timeout through a proxy.

If the proxy is ours

On our gateway, a 407 means the user name or password on the line is wrong, or the line belongs to another plan. The fix is to generate the line again (error codes). Our lines come as host:port:username:password, with the targeting inside the user name: the four parts every client on this page takes, each in its own syntax. On Residential Premium, a whitelisted address needs no login at all. Residential Lite starts at $0.44 per GB (residential proxies), and everything else about the lines is in our docs.

How we tested

On 27 September 2026 we ran one lab on a Windows 11 machine, twice. The site, site.lab.test, ran on the machine itself, over HTTP and over HTTPS with a certificate from a lab authority. That name exists nowhere else, so a client could reach the site only through one of four proxies on the same machine:

  • one that wanted a Basic login and knew eight accounts, seven of them with one symbol each in the password;
  • one that accepted only NTLM and checked each NTLMv2 answer against the password;
  • one that offered both;
  • one that wanted no login.

The proxies logged what each request carried. The site logged every request that reached it.

The clients were the curl in Windows (8.21.0), the curl of Git for Windows (8.16.0), Python 3.13.7 with urllib, requests 2.32.3 and httpx 0.24.1, and PowerShell 7.6.6, plus Git Bash and PowerShell for the quoting tests. All 129 cases gave the same result in both runs. No client was given the Windows account of the machine, and nothing left it. The lines for npm, pip, git, Node.js, Java and .NET come from the tests behind those guides.

Sources

Frequently asked questions

What does 407 Proxy Authentication Required mean?

The proxy between you and the site wants a login before it forwards your request, and it did not get one it could use. The site never saw the request. The proxy names the login it wants in a Proxy-Authenticate header, and your tool has to answer with a Proxy-Authorization header.

Why do I get a 407 when the password is right?

Because the proxy did not receive it the way you typed it. In our tests that happened five ways: the login went to the site instead of the proxy (curl -u, or a header added by hand), a % in the password turned into another character inside the proxy URL (and in git so did a raw @; a raw #, / or ? makes curl and requests stop with a different error before anything is sent), the shell swallowed a $ inside double quotes, the proxy wanted NTLM and the tool sent Basic, or Java refused to send Basic over an https:// tunnel.

What is the difference between a 407 and a 401 or 403?

A 401 is the site asking you to log in, and a 403 is the site refusing a request it understood. A 407 is the proxy asking for its own login, before your request reaches the site at all.

How do I pass a proxy username and password in curl?

With -U 'user:pass', or inside the proxy URL as http://user:pass@host:port. Lower-case -u is the login for the site, not the proxy. In the URL, write @ as %40, # as %23, / as %2F, ? as %3F and % as %25. -U accepts @ # / ? as they are, but curl still decodes percent codes there, so a % needs %25 in -U too.

How do I fix Tunnel connection failed: 407 Proxy Authentication Required in Python?

Put the login in the proxy URL, http://user:pass@host:port, in proxies= or in HTTPS_PROXY, with symbols percent-encoded. A Proxy-Authorization header in headers= and requests' HTTPProxyAuth both failed for https:// sites in our test, because they never reach the proxy. If the proxy asks for NTLM, requests and urllib cannot answer it at all.

Why does curl exit with code 7, or 56, on a 407?

It depends on the version. From curl 8.20.0, released on 29 April 2026, a refused tunnel exits with 7; up to 8.19.0 it exited with 56. The message is the same in both: CONNECT tunnel failed, response 407. For an http:// site curl exits 0, unless you add --fail, which makes it 22.

Get proxies that are alive right now

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump. When the location has to survive a real check, the paid network holds up.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup

HProxy.

Honest guides and comparisons on proxies, scraping and staying unblocked, from the team that runs the network.

RSS feed