Almost every "wrong username or password" on a proxy turns out to be one of six things, and they are not equally likely. Work through them in the order below and you will normally find it in the first two minutes rather than the first two hours.
Client libraries and browsers flatten every failure into the same unhelpful sentence. The status code underneath does not. There are two, and they mean opposite things.
407 Proxy Authentication Required comes from the proxy. It means your connection reached the gateway, the gateway looked at the credentials you presented, and refused them. This is a proxy credential problem and everything else in this article applies.
401 Unauthorized comes from the target website, on the far side of the proxy. It means the proxy authenticated you perfectly, carried the request, and the site itself wants a login you did not supply. Nothing about your proxy username is wrong. People lose whole afternoons rewriting a working proxy config because they saw the word "unauthorized" and did not check who said it.
A connection that hangs or is refused outright, with no status code at all, is neither. That is a host, port or firewall problem, and changing your password will not help.
Before touching any configuration, take your application, your framework and your browser out of the picture. This is the smallest possible test that exercises nothing except the credential handshake:
curl -v -x http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111 https://httpbin.org/ip
Read the verbose output rather than the body. If you see Proxy-Authorization sent and a
200 back with a JSON address, your credentials are fine and the fault is in whatever tool
was failing. If you see HTTP/1.1 407, the credentials as typed on that line are being
rejected, and you now have a two-second reproduction to test fixes against. Keep the target boring:
testing authentication against a site that has its own login or bot protection mixes two problems
together.
This is the most common cause and the hardest to see. Credentials are usually copied out of a dashboard, an email or a chat message, and copying picks up things that do not show on screen.
" may have become curly ”. The shell treats those as literal characters and
your password silently gains two of them.\r from a .env file saved with Windows
line endings, which breaks authentication and shows up nowhere else.Do not squint at it. Retype the credentials by hand once into a plain text editor and test again; if the hand-typed version works, the paste was the bug. You can also make the invisible visible:
printf '%s' "$PROXY_PASS" | od -c | tail -3
Anything other than your intended characters followed by the end of the string is your answer.
On a rotating gateway the username is not just an account name. It is a small instruction string, and the whole thing is compared as one unit:
USERNAME-country-us
That means a mistake anywhere after the account name produces a 407 that reads as "bad username", even though your account name and password are both perfect. The usual suspects:
-country-usa fails where -country-us works.USERNAME--country-us or
USERNAMEcountry-us.The fix is bisection. Authenticate with the bare USERNAME and no options at all. If
that succeeds, the credentials are correct and the option string is the fault; add options back one at
a time until it breaks again. If the bare username also fails, the problem is the account, not the
options.
Writing credentials inside the proxy URL is convenient and it is also where special characters go
wrong. The URL format is scheme://user:password@host:port, so the parser is looking for a
colon to split user from password and an @ to mark where the host begins. If your password
contains either character, it splits in the wrong place: p@ss:word makes the parser look
for a host called ss:word. You do not get a clear error — you get a 407, a DNS failure, or
something stranger.
The fix is percent-encoding. Replace each reserved character with its hex escape:
@ -> %40 : -> %3A / -> %2F # -> %23 ? -> %3F % -> %25 & -> %26 + -> %2B space -> %20
So p@ss:word becomes p%40ss%3Aword. Encode the password only, never the
separators themselves, and encode any % first or you will double-encode what follows.
The cleaner answer is to stop embedding credentials in the URL. Most clients accept them separately, and then no encoding is needed at all:
curl -x http://gw.pyproxy.com:1111 -U 'USERNAME-country-us:p@ss:word' https://httpbin.org/ip
Python's requests, Node's proxy agents and most HTTP libraries have an equivalent. Use
single quotes in the shell so it does not expand anything inside the password.
Proxy authentication is a two-step conversation. The polite client connects, receives
407 with a Proxy-Authenticate header, and only then resends the request with
Proxy-Authorization. Some clients instead send credentials pre-emptively on the first
request. Both are valid, and the mismatch causes two distinct failures.
If your tool waits for the challenge but does not retry — common in scripts that treat any non-2xx as
fatal — you see a 407 even though the credentials would have been accepted on the second attempt. Look
for a "proxy authentication" or "preemptive auth" setting and turn it on; in curl,
--proxy-anyauth lets it negotiate.
The reverse case matters for HTTPS. A client that only authenticates on the plain request may fail to
attach credentials to the CONNECT tunnel that HTTPS requires, so HTTP targets work and
HTTPS targets return 407. Plain pages fine, secure pages refused means you are looking at the tunnel,
not the password. And some desktop applications cache credentials: quit fully and reopen before
concluding a new password is wrong.
Check this before rewriting anything. On per-gigabyte billing the gateway stops authorising sessions when the traffic on the account is exhausted, and the refusal arrives as an authentication failure rather than a clear "you are out of traffic" message. Correct username, correct password, 407 anyway.
Open your dashboard and look at the remaining traffic. If it is zero, that was the whole mystery. The same applies to a sub-user whose own allocation ran out while the parent account still shows a balance. Top-ups start at $5 and traffic does not expire.
@, :, / or %? Encode
it, or pass it separately.One last thing specific to residential traffic: it is HTTP and HTTPS only and does not support SOCKS5. Some clients report that protocol mismatch as an authentication error, so if SOCKS5 is selected anywhere in your tool, switch it to HTTP before debugging the password any further.
A 407 Proxy Authentication Required comes from the proxy itself and means your proxy username or password was rejected. A 401 comes from the target website and means the proxy let you through fine but the site wants its own login. Only a 407 is a proxy credential problem.
Because those characters have meaning inside a URL. An @ ends the credential section and a colon separates user from password, so the parser splits the string in the wrong place. Percent-encode them: @ becomes %40 and : becomes %3A, or pass the credentials with a separate option instead of embedding them.
Yes. The whole string is the username, so a typo anywhere in the option part, an unsupported country code or a stray extra dash makes the gateway read the entire thing as an unknown user and answer 407. Test with the bare username first, then add options one at a time.
It can. When traffic runs out some gateways stop authorising sessions, so correct credentials still come back refused. Check the balance in your dashboard before you start rewriting configuration; a new account gets one gigabyte free after linking Telegram.