๐ŸŽ‰ Premium Proxies ยท 24-Hour Free TrialClaim Now
Guides

How to Fix 407 Proxy Authentication Required

Fix 407 proxy authentication required in six checks, cheapest first: typos, URL encoding, whitelist mode, 401 vs 407, HTTPS CONNECT and account state.

S SparkProxy 11 16 min read
Share

A 407 proxy authentication required response means the proxy refused to forward your request because it could not match you to an account. It never comes from the website you were trying to reach. The frustrating part is that six different mistakes produce the identical status line, so this guide gives you a fixed order to test them in, cheapest first, with a single curl command per step that proves or rules out each cause.

What the 407 Looks Like on Your Screen

The exact text depends on where you hit it. These are the strings we captured on 2026-10-30 by sending deliberately wrong credentials to SparkProxy's gateway (gateway.sparkproxy.io:11000) with curl 8.15.0 and Python requests 2.32.5. Match yours against them before doing anything else.

curl, plain HTTP target. The proxy answers the request directly:

< HTTP/1.1 407 Proxy Authentication Required
< X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
< Proxy-Authenticate: Basic realm="SparkProxy"

curl, HTTPS target. The status never reaches the page request because the tunnel itself is refused. With -w "%{http_code}" curl prints 000, which sends a lot of people hunting in the wrong place:

* CONNECT tunnel failed, response 407

curl exits with code 56 here ("Failure in receiving network data" in the curl manual), not with a 4xx of any kind.

Python requests, HTTPS target:

requests.exceptions.ProxyError: HTTPSConnectionPool(host='www.sparkproxy.io', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Unable to connect to proxy',
OSError('Tunnel connection failed: 407 Proxy Authentication Required')))

Python requests, HTTP target: no exception at all. You get a normal Response object with status_code == 407 and reason == 'Proxy Authentication Required', which code that only checks for exceptions will happily treat as success.

A browser: a username and password prompt that names the proxy rather than the site, appearing on every page you open. If it keeps coming back after you type the credentials, you are in Step 1 or Step 6.

The Proxy-Authenticate: Basic realm="SparkProxy" header is the reliable fingerprint. If you see it, the refusal came from the proxy, and nothing about the target site, its cookies or its anti-bot system is involved yet. For a quick tour of where 407 sits among the other proxy status codes, see proxy error codes explained.

Who Can Fix a 407: You or the Provider

Most 407s are fixed on your side. Changing proxy provider won't help with any of the first five causes below, because they are all about what your client sends. Only the last one needs the provider.

CauseWhere the fault isWho fixes it
Typo or stale credentialsYour configYou
Unencoded special charactersYour proxy URLYou
Whitelist vs user:pass mismatchDashboard setting vs your clientYou, in the dashboard
Wrong header nameYour codeYou
Credentials missing on CONNECTYour HTTP client configYou
Expired plan, ended trial, disabled accountProvider sideProvider billing or support

And some failures look like a 407 problem but are not one. A 401 Unauthorized means the proxy already let you through and the target site wants a login (Step 4). A SOCKS5 failure never produces a 407 at all, because SOCKS has no HTTP status codes. On SparkProxy's SOCKS5 port 13000 we got curl exit 97, cannot complete SOCKS5 connection to httpbin.org. (2), both with wrong credentials and with none. Reply code 2 is "connection not allowed by ruleset" in RFC 1928. If that is your error, go to the SOCKS5 setup guide instead of this ladder.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

The Six-Step Ladder at a Glance

Run the steps in order. Each one costs less than the next, and each test isolates exactly one variable. Put your real credentials in environment variables first so nothing below leaks into shell history in plain text:

export SP_USER='your_username'
export SP_PASS='your_password'
StepWhat it isolatesOne-line testA pass looks like
1Credentials themselves`curl -s -o /dev/null -w "%{http_code}\n" -x http://gateway.sparkproxy.io:11000 -U "$SP_USER:$SP_PASS" http://www.sparkproxy.io/`Anything except 407
2URL parsing of the passwordSame request with credentials inside the `-x` URLSame code as Step 1
3Auth mode`curl -s -o /dev/null -w "%{http_code}\n" -x http://gateway.sparkproxy.io:11000 http://www.sparkproxy.io/` from the whitelisted boxAnything except 407
4Header name`curl -v ... 2>&1 \grep -i authorization`Line starts `> Proxy-Authorization:`
5CONNECT tunnel`curl -s -o /dev/null -w "%{http_connect}\n" -x ... https://www.sparkproxy.io/``200`
6Account stateStep 1 with freshly copied credentialsAnything except 407

"Anything except 407" matters. A 200, a 301 redirect or even a 403 from the target all mean the proxy accepted you. Only a 407 means it didn't.

Step 1: A Credentials Typo

Start here because it takes ten seconds. curl's -U (--proxy-user) flag sends the username and password exactly as typed, with no URL parsing in between, so it tests the credentials and nothing else:

curl -s -o /dev/null -w "%{http_code}\n" \
  -x http://gateway.sparkproxy.io:11000 \
  -U "$SP_USER:$SP_PASS" \
  http://www.sparkproxy.io/

A 407 here means the proxy received precisely what you typed and rejected it. The usual culprits, roughly in order of how often they bite:

  • A trailing space or newline copied from the dashboard or a .env file. Run printf '%s' "$SP_PASS" | od -c | tail -3 and look for a \n or a stray space at the end.
  • Username and password swapped, or the account email used as the username.
  • A password changed in the dashboard while an old copy sits in a config file, a CI secret or a Docker image layer.
  • Credentials from one product pasted into another. A Scraping API key goes in the X-API-Key header and is not a proxy password.

If Step 1 passes but your application still gets 407, the credentials are fine and the problem is how your application sends them. Move to Step 2.

Step 2: Special Characters and Percent-Encoding

Put the same credentials inside the proxy URL, which is how most libraries take them:

curl -s -o /dev/null -w "%{http_code}\n" \
  -x "http://$SP_USER:$SP_PASS@gateway.sparkproxy.io:11000" \
  http://www.sparkproxy.io/

If Step 1 passed and this fails, your password contains a character that means something in a URL. RFC 3986 only allows unreserved characters, percent-escapes and a few delimiters in the userinfo part, so @, /, #, ? and % have to be escaped. The proxy authentication explainer covers the basic @ to %40 rule. What it doesn't cover is that clients disagree about broken URLs, and we measured how much:

Password in URLcurl 8.15.0requests 2.32.5
`p@ss` unescapedRefuses: `Unsupported proxy syntax ... Bad hostname`, exit 5Accepts it, splits on the last `@`, sends `p@ss`
`pa#ss` unescapedRefuses, exit 5`InvalidURL: Failed to parse`
`p/ss` unescapedRefuses`InvalidURL: Failed to parse`
`Ab%41x9` (literal percent)Sends `AbAx9`Sends `AbAx9`

That last row is the nasty one. If your provider generated a password that happens to contain a % followed by two hex digits, both clients silently decode it, send a different password, and the proxy returns a clean 407 with no hint of why. We confirmed it by decoding the Proxy-Authorization header curl actually sent: dXNlcjpBYkF4OQ== is base64 for user:AbAx9.

The fix is to escape the password once, programmatically, instead of by hand:

from urllib.parse import quote

password = "p@ss:w/rd#%1"
print(quote(password, safe=""))  # p%40ss%3Aw%2Frd%23%251

Or build the proxy URL in code so it can't be wrong:

import os
from urllib.parse import quote

user = quote(os.environ["SP_USER"], safe="")
password = quote(os.environ["SP_PASS"], safe="")
proxy_url = f"http://{user}:{password}@gateway.sparkproxy.io:11000"
proxies = {"http": proxy_url, "https": proxy_url}

Escape the username too. Some providers put session or country parameters in the username, and those can include characters that need the same treatment.

Step 3: The Wrong Auth Mode for Your Account

SparkProxy supports two ways to authenticate: username and password, or IP whitelisting, where the gateway recognises your server's public IP and asks for nothing. A 407 is what you get when the client and the account disagree about which one is in use.

From the machine that should be whitelisted, send a request with no credentials at all:

curl -s -o /dev/null -w "%{http_code}\n" \
  -x http://gateway.sparkproxy.io:11000 \
  http://www.sparkproxy.io/

A 407 here means the gateway doesn't see this machine's IP on your whitelist. Check which IP it actually sees, without the proxy:

curl -s https://httpbin.org/ip

Compare that address with the one in your dashboard. The common mismatches are a home connection whose IP changed overnight, a cloud VM that got a new public IP after a stop and start, a laptop that was on a VPN when you added the IP, and a container whose outbound traffic leaves through a NAT gateway with a different address from the host. Also check that your server isn't egressing over IPv6 while the whitelist holds an IPv4 address.

The reverse mismatch exists too: you whitelisted an IP, then also hardcoded old credentials in the client. If the account side has changed, those credentials can be the thing failing. The IP whitelisting guide explains the trade-offs in depth. For this ladder, one rule is enough: test each mode on its own, never both at once.

Step 4: Authorization Sent Instead of Proxy-Authorization

HTTP has two separate auth conversations. RFC 9110 defines 401 Unauthorized with the Authorization header for the origin server, and 407 Proxy Authentication Required with Proxy-Authorization for the proxy (sections 15.5.2, 15.5.8 and 11.7.2). Put your proxy credentials in the wrong header and the proxy never sees them.

Look at what your client really sends:

curl -v -o /dev/null -x http://gateway.sparkproxy.io:11000 \
  -U "$SP_USER:$SP_PASS" http://www.sparkproxy.io/ 2>&1 | grep -i authorization

You want to see > Proxy-Authorization: Basic .... We tested the wrong version, adding -H "Authorization: Basic ..." without -U, and the gateway returned 407 as expected: that header is meant for the target site, not the proxy.

In application code the same mistake shows up as:

import requests

# Wrong: auth= goes to the TARGET as an Authorization header
r = requests.get("http://www.sparkproxy.io/",
                 proxies={"http": "http://gateway.sparkproxy.io:11000"},
                 auth=("SP_USER", "SP_PASS"), timeout=30)
print(r.status_code)  # 407 from the proxy

We logged that request at a local listener: requests 2.32.5 sent Authorization: Basic ... and no Proxy-Authorization line at all.

Use the code in the status to decide where to look next. 407 means fix your proxy credentials. 401 means the proxy accepted you and the website wants its own login, which no proxy setting will fix. If you flip from 407 to 401 while working through this ladder, you have solved the proxy side.

Step 5: HTTP Works but HTTPS Returns 407

For an HTTPS target, the client first sends CONNECT www.sparkproxy.io:443 to the proxy and only starts TLS once the tunnel is open. Proxy credentials must ride on that CONNECT request. Anything set inside the encrypted request arrives too late for the proxy to read. The CONNECT method explainer walks through the tunnel itself.

Test the tunnel status directly. %{http_connect} reports the proxy's answer to CONNECT, which %{http_code} hides:

curl -s -o /dev/null -w "page=%{http_code} tunnel=%{http_connect}\n" \
  -x "http://$SP_USER:$SP_PASS@gateway.sparkproxy.io:11000" \
  https://www.sparkproxy.io/

tunnel=407 with page=000 is a tunnel auth failure. Run the same command against http://www.sparkproxy.io/. If plain HTTP passes and HTTPS fails, your client is attaching credentials to normal requests but not to CONNECT.

We reproduced exactly that with requests 2.32.5 by pointing it at a local socket listener and logging what arrived. A Proxy-Authorization header passed through headers= was sent on the plain HTTP request and missing from the CONNECT:

CONNECT www.sparkproxy.io:443 HTTP/1.1 | Host: www.sparkproxy.io:443

The same credentials embedded in the proxy URL were present on CONNECT:

CONNECT www.sparkproxy.io:443 HTTP/1.1 | Proxy-Authorization: Basic dTpw | Host: www.sparkproxy.io:443

So the fix in requests is to keep credentials in the proxy URL and give both schemes an entry:

import os
import requests
from urllib.parse import quote

proxy = "http://{}:{}@gateway.sparkproxy.io:11000".format(
    quote(os.environ["SP_USER"], safe=""), quote(os.environ["SP_PASS"], safe=""))

r = requests.get("https://www.sparkproxy.io/",
                 proxies={"http": proxy, "https": proxy}, timeout=30)
print(r.status_code)

Two related traps. Leaving out the "https" key doesn't cause a 407; it sends HTTPS traffic straight out of your own IP with no proxy at all, which is worse because nothing fails. And writing the proxy as https://gateway... asks for TLS to the proxy itself; requests answers with Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP. The proxy URL scheme is http:// even when the target is HTTPS.

Step 6: Account State

If Steps 1 to 5 all point at the credentials and you have pasted them fresh from the dashboard, the account itself may not be active. From the client side, a disabled account and a wrong password look identical: the gateway returns the same ERR_CACHE_ACCESS_DENIED 407 for both, so no local test can tell them apart. We could only reproduce the wrong-credentials case, so treat the following as things to check rather than documented gateway behaviour:

  • Did the 24-hour free trial end? Credentials that worked yesterday afternoon and fail this morning fit that pattern exactly.
  • Did the monthly plan renew? SparkProxy plans are packs billed per plan, starting from $75/month, with unlimited bandwidth, so the account doesn't run out of traffic mid-month. A lapsed renewal is the thing to rule out.
  • Was the password regenerated, or a sub-user removed, by someone else on your team?

Log into the dashboard and confirm the plan is active, then copy the credentials again and rerun Step 1. If it still returns 407, email support@sparkproxy.io with the full curl -v output from Step 1 (delete the base64 after Proxy-Authorization:, since it decodes straight to your password) and the public IP from Step 3.

Applying the Fix in Your Own Tool

Once a step fails, fix it in the tool you actually use. Each guide below shows the working proxy configuration for one stack; keep this ladder as the test and use those posts for the setup:

  • Python requests and aiohttp: credentials in the proxy URL, both keys set. See using proxies with Python requests and aiohttp.
  • Playwright: pass username and password as separate fields in the proxy option rather than inside server. Playwright's network docs say you can specify them "for HTTP(S) proxy", and SparkProxy's Scraping API docs note that Playwright connects to a SOCKS5 proxy but silently ignores the username and password, so use IP whitelisting there. Setup is in web scraping with Playwright and proxies.
  • Puppeteer: Chromium's documented --proxy-server syntax is a scheme, host and port with no credentials field, so answer the proxy's challenge with page.authenticate(), which Puppeteer documents as providing "credentials for HTTP authentication". Full walkthrough in using proxies with Puppeteer.
  • Scrapy: see using proxies with Scrapy.
  • Chrome and Firefox by hand: configuring a proxy in Chrome and Firefox. If the login prompt reappears on every page, rerun Step 1 in curl from the same machine before blaming the browser.
  • curl in scripts: prefer -U over credentials in the URL, for the encoding reasons in Step 2. More patterns in web scraping with curl and proxies.

One more route: if you are fetching pages rather than running a browser fleet, the SparkProxy Scraping API takes an X-API-Key header instead of proxy credentials, so there is no proxy handshake to get wrong. render_js defaults to true and costs 5 credits, so set it to false for a 1-credit plain fetch, and proxy_type=premium selects the premium pool:

import os
import requests

r = requests.get(
    "https://scrape.sparkproxy.io/api/v1",
    headers={"X-API-Key": os.environ["SPARK_API_KEY"]},
    params={"url": "https://www.sparkproxy.io/", "render_js": "false",
            "proxy_type": "premium"},
    timeout=60,
)
print(r.status_code, len(r.text))

New accounts get 1,000 free credits, enough to confirm the target works before you return to debugging proxy credentials.

Frequently asked questions

FAQ

It means the proxy server refused to forward your request because it couldn't authenticate you. The target website never saw the request. The proxy signals this with a Proxy-Authenticate header, and your client has to resend with valid credentials in Proxy-Authorization.

Usually because the password isn't reaching the proxy unchanged. Special characters such as @, #, / or a literal % get mangled when the password sits inside a proxy URL without percent-encoding. Test with curl -U "user:pass", which bypasses URL parsing; if that works and the URL form doesn't, encode the password.

A 407 comes from the proxy and means your proxy credentials failed. A 401 comes from the target website and means the proxy accepted you but the site wants its own login. Moving from 407 to 401 means your proxy setup is fixed.

Your client attaches proxy credentials to ordinary requests but not to the CONNECT request that opens the HTTPS tunnel. Put the credentials in the proxy URL, or use the client's proxy-auth option, instead of adding a Proxy-Authorization header manually.

Yes. With whitelist auth, the proxy returns 407 as soon as your public IP stops matching the whitelisted one, which happens after a home IP change, a VM restart, a VPN connection or traffic leaving through a NAT gateway. Check your real egress IP and update the dashboard entry.

No. A 407 is deterministic, so the same credentials will fail the same way, and a retry loop only hammers the gateway. Fix the cause using the six-step ladder, then resend.

Special Discount ยท 20% off

Get 20% off your first month

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Save up to 15% more on quarterly, half-yearly and yearly plans

Claim Discount

About the Author

The SparkProxy Technical Team builds and operates SparkProxy's datacenter proxy network and the SparkProxy Scraping API, and supports the developers who route production traffic through them. The plans cover 1M+ premium IPs in 80+ countries over HTTP, HTTPS and SOCKS5, with both IP whitelist and user:pass authentication. Every error string in this guide was captured on 2026-10-30 against our own gateway or a local test listener, not copied from documentation.

Keep reading

Related articles

SSL WRONG_VERSION_NUMBER With a Proxy: The Fix

Getting SSL WRONG_VERSION_NUMBER through a proxy in Python? It's almost always https:// in the proxy URL. The one-character fix, plus every SSLEOFError cause.

SparkProxyยทGuides