How HTTP Works

Response header

< Proxy-Authenticate: Basic realm="corp-proxy"

Proxy-Authenticate Header

Learn how the Proxy-Authenticate header challenges clients for credentials when accessing resources through a proxy. Understand proxy authentication schemes.

Reviewed 5 min readadvanced2 sourcesTry itMarkdown
Direction
Response
Category
Authentication
Spec
RFC 9110 §11.7.1
Hop-by-hop
Yes: not forwarded by proxies
On this page

TL;DR: When a proxy wants credentials, it answers 407 Proxy Authentication Required and lists the schemes it accepts in Proxy-Authenticate. The client retries with Proxy-Authorization. It’s the proxy-side twin of 401 plus WWW-Authenticate, which come from the origin server and need separate credentials.

The header tells the client which authentication scheme and parameters the proxy accepts. Every 407 a proxy generates must include at least one challenge. See RFC 9110 §11.7.1.

How Proxy-Authenticate Works

For HTTPS through an HTTP/1.1 forward proxy, the client usually asks for a tunnel with CONNECT before any TLS handshake with the origin happens. The requests and responses on this page are trimmed examples.

CONNECT api.example.com:443 HTTP/1.1
Host: api.example.com:443

If the proxy wants credentials first, it rejects the request with a challenge:

HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Corporate Proxy"
Content-Length: 0

Once the client authenticates, a successful CONNECT opens the tunnel and the origin TLS traffic flows through it. Plain HTTP requests through a forward proxy work differently: they use an absolute-form target such as GET http://example.com/ HTTP/1.1. See RFC 9110 §9.3.6 and RFC 9112 §3.2.2.

Syntax

Proxy-Authenticate: scheme [scheme-specific parameters]

Each scheme defines its own parameters, so realm isn’t universal. If you parse the header yourself, remember that parameter values can contain commas, so splitting on commas won’t reliably separate challenges.

A Basic challenge:

Proxy-Authenticate: Basic realm="Corporate Proxy", charset="UTF-8"

The realm names a protection space so the client knows which credentials to use. Basic just base64-encodes the username and password, with no encryption at all. RFC 7617 defines its parameters.

Multiple challenges and Digest parameters

A proxy can offer several challenges on separate field lines. This one offers Negotiate and Basic:

Proxy-Authenticate: Negotiate
Proxy-Authenticate: Basic realm="Corporate Proxy"

The client picks a scheme it supports. Listing Negotiate first doesn’t force anyone to use it. In curl, --proxy-negotiate authenticates to the proxy, while --negotiate authenticates to the origin. To let curl choose, pass --proxy-anyauth with your proxy credentials. The curl proxy authentication options document each layer.

Digest challenges carry algorithm-specific parameters. This one advertises SHA-256 and qop=auth:

Proxy-Authenticate: Digest realm="proxy", nonce="proxy-generated-nonce", algorithm=SHA-256, qop="auth"

The client computes its response from this exact challenge and the request target. With qop, the nonce count and client nonce go into the hash too, so a response copied from another URI, or one that skips those inputs, will fail. If the proxy sends opaque, return it unchanged. RFC 7616 §§3.3–3.4 specifies these fields. Digest protects the password from casual sniffing, but it isn’t a substitute for TLS.

Testing Proxy-Authenticate

Point curl at your test proxy and look at the challenge without sending credentials:

curl --proxy http://proxy.example.com:3128 -v https://example.com/ -o /dev/null

Then let curl pick a supported scheme and prompt you for the password:

curl --proxy https://proxy.example.com:8443 \
  --proxy-anyauth --proxy-user proxyuser \
  https://example.com/ -o /dev/null

The https:// proxy URL encrypts the hop between client and proxy. Swap in your own proxy hostname, port and certificate. --proxy-anyauth may cost an extra round trip while curl discovers the scheme, and it only helps if curl and the proxy share one.

Getting Proxy-Authenticate right

Set proxy credentials in the client’s proxy configuration, not in application code. Browsers make this explicit: Fetch forbids request headers that start with Proxy-, and it has no proxy option for picking a forward proxy. See the Fetch Standard.

Watch the transport, too. If you talk to a proxy over plain HTTP, a Basic credential sent with CONNECT travels in the clear, even when the destination is HTTPS. It goes out before the destination TLS handshake starts.

Proxy-Authenticate vs WWW-Authenticate

Getting through the proxy doesn’t get you into the origin. A request inside an authenticated tunnel can still come back 401. In curl, --proxy-user holds your proxy credentials and --user holds your origin credentials. They’re separate sets.

Basic and Digest are IETF-specified. Other schemes work only when both proxy and client support them, and how secure they are depends on the transport, the algorithm and the implementation.

Server (Proxy) Implementation

Checking a password in an Express route doesn’t make a forward proxy. A real one also handles CONNECT, validates destinations and keeps proxy credentials from leaking upstream. Use an existing proxy whose authentication and tunnel behavior you can test.

Apache local forward proxy

For a local diagnostic proxy, bind to loopback and require credentials. You’ll need Apache’s proxy, proxy_http, proxy_connect, auth_basic, authn_file and authorization modules:

Listen 127.0.0.1:3128
<VirtualHost 127.0.0.1:3128>
    ProxyRequests On
    AllowCONNECT 443
    <Proxy "*">
        AuthType Basic
        AuthName "Local proxy"
        AuthBasicProvider file
        AuthUserFile /etc/apache2/proxy.htpasswd
        Require valid-user
    </Proxy>
</VirtualHost>

Create the credentials file:

htpasswd -c /etc/apache2/proxy.htpasswd proxyuser

Drop -c when adding users to an existing file. This listener speaks plain HTTP on loopback, so keep it local. Before serving remote users, add client-to-proxy TLS and network access restrictions. Apache’s proxy access documentation explains why your access rules must cover CONNECT as well: it carries a host and port, not an origin path.

Test the challenge, then the authenticated tunnel:

curl --noproxy '' --proxy http://127.0.0.1:3128 -v https://example.com/ -o /dev/null
curl --noproxy '' --proxy http://127.0.0.1:3128 \
  --proxy-basic --proxy-user proxyuser https://example.com/ -o /dev/null

The first request gets a 407 challenge. The second prompts for the password and goes through. The origin might still reject the tunneled request on its own terms, but you’ve proven the proxy boundary works without writing a credential parser by hand.

Debugging a persistent 407

Start with the advertised scheme and realm. If the proxy offers Digest and your client is forced to Basic, no password change will fix it. Let curl negotiate with --proxy-anyauth, or pick a scheme the proxy actually supports.

Next, check NO_PROXY. A request that bypasses the proxy never reaches the thing you’re debugging. The commands above pass --noproxy '' to override any bypass settings. curl documents this override.

With an HTTPS proxy there are two TLS peers: the proxy and the destination. A proxy certificate error happens before CONNECT authentication even starts, so treat it as a trust problem, not a credential one. Once CONNECT succeeds, the destination certificate gets verified separately. Do not disable both checks just to make a 407 easier to investigate.

For a plain HTTP destination, the client sends an absolute URI as the request target. Here the credentials are the example user:pass:

GET http://example.com/ HTTP/1.1
Host: example.com
Proxy-Authorization: Basic dXNlcjpwYXNz

That’s a different request form from HTTPS CONNECT. RFC 9112 §3.2.2 defines absolute-form for proxy requests.

Frequently asked questions

What is Proxy-Authenticate?

Proxy-Authenticate is sent with 407 responses when a proxy requires authentication. It works like WWW-Authenticate but for proxy servers instead of origin servers.

When is Proxy-Authenticate used?

A proxy requiring credentials sends 407 with Proxy-Authenticate. The client supplies credentials in Proxy-Authorization for the selected scheme.

What is the difference between 401 and 407?

401 means the origin server requires authentication (use WWW-Authenticate). 407 means a proxy requires authentication (use Proxy-Authenticate).

How do I authenticate with a proxy?

After receiving 407 with Proxy-Authenticate, send the request again with Proxy-Authorization header containing credentials in the specified scheme.

Sources

  1. MDN Web Docs: Proxy-Authenticatedeveloper.mozilla.org
  2. RFC 9110: Proxy-Authenticaterfc-editor.org

Keep going

Browse /search