407 Proxy Authentication Required
Almost always yours: the credentials, or the way your tool passes them. The target never saw the request.
- Usually from
- The proxy, about your credentials
- Try first
- Copy USER:PASS fresh from the dashboard and retry in curl
What a 407 means through a proxy
HTTP gives proxies their own login status. A 401 is a website asking you to sign in to it; a 407 is the proxy in the middle asking. It arrives with a Proxy-Authenticate header naming the scheme the proxy wants, which for a USER:PASS proxy is Basic.
For an HTTPS target your client first sends CONNECT host:443 to the proxy. The 407 answers that CONNECT, so no tunnel opened and nothing reached the site. Most tools turn it into an exception with 407 buried in the message, and hand you no response object at all.
For a plain http:// target the 407 comes back as an ordinary response instead. Scrapy then drops it as an unhandled status, and a script that only prints the body sees an empty page and no error.
Your credentials, the proxy, or the target?
Almost always yours: the credentials, or the way your tool passes them. The target never saw the request.
Your credentials
The usual cause. A typo, an old password, a special character that broke the URL, or a trailing newline read in from a
.envfile.The proxy
Worth suspecting only once the same credentials fail in a plain curl command too. That is the point to ask in Discord.
The target site
Not involved. Websites answer with 401 or 403. A 407 comes from whatever is speaking proxy.
How to tell
- The status is 407 and the response carries
Proxy-Authenticate. Only a proxy sends that pair. - In curl,
-w '%{http_connect}'prints 407: the proxy refused the tunnel before the site was contacted. - The same URL loads without the proxy. That rules the site out.
- One curl command with the same USER:PASS works while your script fails. The credentials are fine and your tool is passing them wrong.
Fixes, in the order to try them
- Costs nothing
Copy the credentials again
Take USER and PASS fresh from the dashboard and paste them into a single curl command before touching your code. If curl gets through, the login is good.
- Costs nothing
Percent-encode special characters
A password with
@,:,/or#breaks auser:pass@hostURL. Encode them (@becomes%40), or pass the credentials separately:--proxy-userin curl, theusernameandpasswordfields in Playwright. - Costs nothing
Strip whitespace from files and env vars
Credentials read from
.envor a secrets file often end in a newline. Use.strip()in Python or.trim()in Node, then print the proxy URL with the password masked to check it. - Costs some time
Make sure credentials go with the CONNECT
Some setups attach credentials to plain HTTP requests and forget the CONNECT used for HTTPS. Chrome ignores credentials written into
--proxy-server, which is why Puppeteer needspage.authenticateand Selenium needs an extension. The setup guides cover each tool. - Costs some time
Match the proxy scheme to the port
SOCKS5 has its own login handshake and never answers with 407. A 407 means your tool is speaking HTTP to the proxy, so check that the scheme in the proxy URL is the one the dashboard lists for that host and port.
See the real status and headers
Each sample makes the 407 visible and shows whether it came back on the CONNECT (the proxy talking) or as a response.
curl
# -v prints the proxy's own reply to CONNECT
curl -sv -o /dev/null -x http://USER:[email protected]:8000 https://httpbin.org/ip 2>&1 \
| grep -iE '^< (HTTP|proxy-authenticate)'
# < HTTP/1.1 407 Proxy Authentication Required
# a password with @ : / or # goes in --proxy-user, outside the URL
curl -x http://resi.proxymonkey.io:8000 --proxy-user 'USER:PASS' https://httpbin.org/ipPython Requests
import os
from urllib.parse import quote
import requests
user = os.environ["PM_USER"].strip()
password = quote(os.environ["PM_PASS"].strip(), safe="")
proxy = f"http://{user}:{password}@resi.proxymonkey.io:8000"
try:
r = requests.get("https://httpbin.org/ip", proxies={"http": proxy, "https": proxy}, timeout=30)
print("the site answered", r.status_code)
except requests.exceptions.ProxyError as err:
print("the proxy refused the tunnel:", err)Scrapy
import scrapy
from scrapy.core.downloader.handlers.http11 import TunnelError
RESIDENTIAL = "http://USER:[email protected]:8000"
class CheckSpider(scrapy.Spider):
name = "check"
handle_httpstatus_list = [407]
def start_requests(self):
for url in ["http://httpbin.org/ip", "https://httpbin.org/ip"]:
yield scrapy.Request(url, meta={"proxy": RESIDENTIAL}, errback=self.failed)
def parse(self, response):
self.logger.info("%s %s", response.status, response.headers.get("Proxy-Authenticate"))
def failed(self, failure):
if failure.check(TunnelError):
self.logger.error("CONNECT refused: %s", failure.value)The http:// URL shows the 407 as a response; the https:// one shows it as a TunnelError.
Playwright
from playwright.sync_api import Error, sync_playwright
PROXY = {"server": "http://resi.proxymonkey.io:8000", "username": "USER", "password": "PASS"}
with sync_playwright() as p:
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
try:
response = page.goto("https://httpbin.org/ip")
print(response.status, page.inner_text("body"))
except Error as err:
print(err.message.splitlines()[0])
browser.close()Credentials go in username and password. Written into server, Chromium ignores them.
Node.js
import { fetch, ProxyAgent } from "undici";
const user = process.env.PM_USER.trim();
const pass = process.env.PM_PASS.trim();
const dispatcher = new ProxyAgent({
uri: "http://resi.proxymonkey.io:8000",
token: "Basic " + Buffer.from(user + ":" + pass).toString("base64"),
});
try {
const res = await fetch("https://httpbin.org/ip", { dispatcher });
console.log("the site answered", res.status);
} catch (err) {
console.error(err.cause?.message ?? err);
}
// Proxy response (407) !== 200 when HTTP TunnelingSending the credentials as a token sidesteps URL encoding altogether.
The samples use the residential gateway, resi.proxymonkey.io:8000. For an ISP or datacenter IP, use USER:PASS@IP:PORT for the address you rented. Your dashboard lists the host and port for every order, and where it differs from this page, the dashboard is right.
Will a different proxy line fix it?
When switching helps
No line fixes a 407. It is about the login, and the login is checked before your request goes anywhere.
When it will not
Buying a different line to get away from a 407 spends money on a typo. Get one curl command through first, then pick the line on what the target needs.
Is a failed request billed?
We bill for request bytes and response bytes, including headers and protocol overhead on the tunnelled connection. Connections that fail before transferring data are not billed. Retries that you initiate are billed and appear as separate entries in your usage log.
The terms do not single out a rejected login. They do say that a connection which fails before transferring data is not billed. If you find rows for 407s in your usage log, ask in Discord.
Residential is billed per GB of that traffic. ISP and datacenter addresses are charged per IP for their term, and where a plan includes a traffic allowance, traffic past it is billed per GB under the same rule. The usage log in your dashboard has one row per request with bytes in, bytes out and cost, so you can look up the failed request yourself.
Questions people ask about a 407
Why do I get a 407 when my password is right?
Because the password did not reach the proxy intact. A special character in the URL, a newline from a file, or a tool that only sends credentials on plain HTTP requests will each produce a 407 with a correct password.
Test the same pair with curl --proxy-user. If that works, the problem is in how your code passes it.
Is a 407 the same as a 401?
They are cousins. A 401 comes from the website and means it wants you to sign in to it. A 407 comes from the proxy and means it wants proxy credentials. A 401 has nothing to do with your proxy account.
Does a 407 mean my account is blocked or out of balance?
The status code alone cannot tell you that. Check the account in the dashboard. If everything there looks right and one curl command still gets a 407, paste the curl output into the Discord with the password removed.
Why does Chrome pop up a sign-in box for the proxy?
That box is how a browser shows a 407: it got the challenge and nobody answered it. In automation, pass the credentials through the tool: the proxy username and password options in Playwright, page.authenticate in Puppeteer, or an extension in Selenium.
Errors that travel with this one
- ECONNREFUSED
Connection refused
Something said no before any HTTP happened. Usually the host or port you gave for the proxy.
Work it out → - 403
403 Forbidden
The website refused you. Through a proxy that is usually about the IP range, your headers or your pace.
Work it out → - TLS
SSL certificate errors
Certificate checks fail between you and the site. The proxy passes TLS through untouched, so look at the site and your CA bundle.
Work it out →
Still stuck on a 407?
Paste your error in the Discord: the full message plus the command or the few lines that set up the proxy, with the password taken out. Someone there has seen it before.
Join the Discord4,200+monkeys in the Discord
Help from humans
Post your error, get an answer. Usually in minutes, usually from someone who has hit the same wall.
A status bot that tells on us
Pool health, incidents and maintenance posted automatically. Including the bad days.
Deals and free traffic
Bonus GB drops, early access to new pools, and the occasional giveaway for a good bug report.