- Home
- HTTP Headers
- Proxy-Authenticate Header
Header
Proxy-Authenticate Header
Learn how the Proxy-Authenticate header challenges clients for credentials when accessing resources through a proxy. Understand proxy authentication schemes.
What is Proxy-Authenticate?
TL;DR: Challenges clients for authentication credentials when accessing resources through a proxy server. Sent with 407 status code when proxy requires login.
The Proxy-Authenticate header is sent by a proxy server to challenge the client for authentication credentials. It’s like a security checkpoint saying “You need to prove who you are before I’ll let you through to the destination server.”
This is similar to WWW-Authenticate but specifically for proxy server authentication rather than origin server authentication.
How Proxy-Authenticate Works
Client makes request through proxy:
GET https://api.example.com/data HTTP/1.1
Host: api.example.com
```text
**Proxy challenges for credentials:**
```http
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Corporate Proxy"
Proxy authentication required
Client sends credentials:
GET https://api.example.com/data HTTP/1.1
Host: api.example.com
Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==
```text
**Proxy forwards request:**
```http
HTTP/1.1 200 OK
Content-Type: application/json
{"data": "success"}
Syntax
Proxy-Authenticate: <auth-scheme> realm="<realm>"
Proxy-Authenticate: <auth-scheme> realm="<realm>", <directives>
```text
### Common Auth Schemes
- **Basic** - Username and password (base64 encoded)
- **Digest** - More secure than Basic
- **Bearer** - Token-based authentication
- **Negotiate** - SPNEGO/Kerberos authentication
## Common Examples
### Basic Authentication
```http
Proxy-Authenticate: Basic realm="Corporate Network"
Simple username/password authentication.
Digest Authentication
Proxy-Authenticate: Digest realm="proxy", qop="auth",
nonce="abc123xyz", opaque="def456"
```text
More secure challenge-response authentication.
### Multiple Options
```http
Proxy-Authenticate: Negotiate
Proxy-Authenticate: Basic realm="Proxy Server"
Offer multiple authentication methods.
Bearer Token
Proxy-Authenticate: Bearer realm="proxy", error="invalid_token"
```text
OAuth 2.0 style token authentication.
## Real-World Scenarios
### Corporate Proxy
```http
# Employee tries to access internet
GET https://news.example.com HTTP/1.1
# Corporate proxy requires authentication
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="CorpProxy"
Proxy-Authenticate: Negotiate
# Client sends Windows credentials
GET https://news.example.com HTTP/1.1
Proxy-Authorization: Negotiate YIIBvwYJKoZIhvcSAQICAQBu...
# Proxy authenticates and forwards
HTTP/1.1 200 OK
Digest Proxy Authentication
# Initial request
GET https://api.example.com/data HTTP/1.1
# Proxy challenge
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Digest realm="proxy",
qop="auth",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
# Client response
GET https://api.example.com/data HTTP/1.1
Proxy-Authorization: Digest username="user",
realm="proxy",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="https://api.example.com/data",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
# Success
HTTP/1.1 200 OK
```text
### Failed Authentication
```http
# Invalid credentials
GET https://api.example.com HTTP/1.1
Proxy-Authorization: Basic aW52YWxpZDpjcmVkcw==
# Proxy rejects
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Proxy", error="invalid_credentials"
Invalid proxy credentials
Server (Proxy) Implementation
Express Proxy with Authentication
const express = require('express')
const httpProxy = require('http-proxy')
const app = express()
const proxy = httpProxy.createProxyServer({})
// Simple credential store
const validCredentials = {
user1: 'password1',
user2: 'password2'
}
// Check Basic auth
function checkProxyAuth(req, res, next) {
const proxyAuth = req.headers['proxy-authorization']
if (!proxyAuth) {
res.setHeader('Proxy-Authenticate', 'Basic realm="Corporate Proxy"')
return res.status(407).send('Proxy Authentication Required')
}
// Parse Basic auth
const authHeader = proxyAuth.replace('Basic ', '')
const decoded = Buffer.from(authHeader, 'base64').toString()
const [username, password] = decoded.split(':')
// Validate credentials
if (validCredentials[username] !== password) {
res.setHeader('Proxy-Authenticate', 'Basic realm="Corporate Proxy"')
return res.status(407).send('Invalid proxy credentials')
}
// Store authenticated user
req.proxyUser = username
next()
}
// Apply auth to all requests
app.use(checkProxyAuth)
// Proxy requests
app.use('/', (req, res) => {
console.log(`Proxying request for user: ${req.proxyUser}`)
proxy.web(req, res, {
target: `${req.protocol}://${req.get('host')}`,
changeOrigin: true
})
})
app.listen(3128, () => {
console.log('Proxy server listening on port 3128')
})
```javascript
### Digest Authentication Proxy
```javascript
const crypto = require('crypto')
function generateNonce() {
return crypto.randomBytes(16).toString('hex')
}
function digestProxyAuth(req, res, next) {
const proxyAuth = req.headers['proxy-authorization']
if (!proxyAuth) {
const nonce = generateNonce()
const opaque = crypto.randomBytes(16).toString('hex')
res.setHeader(
'Proxy-Authenticate',
`Digest realm="proxy", qop="auth", nonce="${nonce}", opaque="${opaque}"`
)
return res.status(407).send('Proxy Authentication Required')
}
// Parse Digest auth
const params = {}
proxyAuth
.replace('Digest ', '')
.split(',')
.forEach((part) => {
const [key, value] = part.trim().split('=')
params[key] = value.replace(/"/g, '')
})
// Validate digest
const username = params.username
const password = validCredentials[username]
if (!password) {
res.setHeader('Proxy-Authenticate', 'Digest realm="proxy"')
return res.status(407).send('Invalid credentials')
}
// Calculate expected response
const ha1 = crypto.createHash('md5').update(`${username}:proxy:${password}`).digest('hex')
const ha2 = crypto.createHash('md5').update(`${req.method}:${params.uri}`).digest('hex')
const expected = crypto.createHash('md5').update(`${ha1}:${params.nonce}:${ha2}`).digest('hex')
if (params.response !== expected) {
res.setHeader('Proxy-Authenticate', 'Digest realm="proxy"')
return res.status(407).send('Invalid digest')
}
req.proxyUser = username
next()
}
Python Proxy with Authentication
from http.server import HTTPServer, BaseHTTPRequestHandler
import base64
VALID_CREDENTIALS = {
'user1': 'password1',
'user2': 'password2'
}
class ProxyHandler(BaseHTTPRequestHandler):
def do_GET(self):
# Check Proxy-Authorization header
proxy_auth = self.headers.get('Proxy-Authorization')
if not proxy_auth:
self.send_response(407)
self.send_header('Proxy-Authenticate', 'Basic realm="Proxy"')
self.end_headers()
self.wfile.write(b'Proxy Authentication Required')
return
# Parse Basic auth
try:
auth_type, credentials = proxy_auth.split(' ', 1)
if auth_type.lower() != 'basic':
raise ValueError('Unsupported auth type')
decoded = base64.b64decode(credentials).decode('utf-8')
username, password = decoded.split(':', 1)
# Validate
if VALID_CREDENTIALS.get(username) != password:
self.send_response(407)
self.send_header('Proxy-Authenticate', 'Basic realm="Proxy"')
self.end_headers()
self.wfile.write(b'Invalid credentials')
return
# Forward request to destination
# ... proxy logic here ...
self.send_response(200)
self.end_headers()
self.wfile.write(b'Success')
except Exception as e:
self.send_response(407)
self.send_header('Proxy-Authenticate', 'Basic realm="Proxy"')
self.end_headers()
self.wfile.write(b'Authentication failed')
server = HTTPServer(('', 3128), ProxyHandler)
server.serve_forever()
```text
## Best Practices
### For Proxy Servers
**1. Always use 407 status code**
```http
# ✅ Correct proxy auth challenge
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Proxy"
2. Prefer secure authentication methods
# ✅ Offer secure options first
Proxy-Authenticate: Digest realm="proxy", qop="auth", nonce="..."
Proxy-Authenticate: Basic realm="proxy"
# ❌ Only Basic is less secure
Proxy-Authenticate: Basic realm="proxy"
```javascript
**3. Use HTTPS for proxy connections**
```javascript
// ✅ Secure proxy (HTTPS)
const proxy = {
host: 'proxy.example.com',
port: 443,
protocol: 'https'
}
// ❌ Unencrypted Basic auth is vulnerable
4. Implement rate limiting
const attempts = new Map()
function checkAuthAttempts(clientIp) {
const count = attempts.get(clientIp) || 0
if (count > 5) {
return res.status(429).send('Too many authentication attempts')
}
attempts.set(clientIp, count + 1)
}
```javascript
**5. Log authentication attempts**
```javascript
function logProxyAuth(username, success, clientIp) {
console.log({
timestamp: new Date(),
user: username,
success,
ip: clientIp
})
}
For Clients
1. Store proxy credentials securely
// ✅ Use secure credential storage
const credentials = await getSecureCredentials('proxy')
// ❌ Don't hardcode
const credentials = { user: 'user', pass: 'password' }
```javascript
**2. Handle 407 responses**
```javascript
const response = await fetch(url, {
proxy: proxyUrl
})
if (response.status === 407) {
// Prompt for proxy credentials
const credentials = await promptForProxyCredentials()
// Retry with credentials
return fetch(url, {
proxy: proxyUrl,
headers: {
'Proxy-Authorization': `Basic ${btoa(credentials)}`
}
})
}
3. Reuse proxy authentication
// ✅ Cache proxy auth for session
const proxyAuth = `Basic ${btoa(`${username}:${password}`)}`
fetch(url, {
headers: {
'Proxy-Authorization': proxyAuth
}
})
```text
## Proxy-Authenticate vs WWW-Authenticate
### Proxy-Authenticate (Proxy Layer)
```http
# For proxy server authentication
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Proxy"
# Client responds with
Proxy-Authorization: Basic credentials
WWW-Authenticate (Origin Server)
# For origin server authentication
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="API"
# Client responds with
Authorization: Basic credentials
```text
### Both Can Occur Together
```http
# Proxy auth required first
HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="Proxy"
# After proxy auth, origin might also require auth
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="API"
# Client sends both
Proxy-Authorization: Basic proxy-credentials
Authorization: Basic api-credentials
Common Proxy Authentication Schemes
Basic
Proxy-Authenticate: Basic realm="Corporate Proxy"
```text
Simple but should only be used over HTTPS.
### Digest
```http
Proxy-Authenticate: Digest realm="proxy", qop="auth", nonce="..."
More secure than Basic, challenge-response mechanism.
Negotiate (SPNEGO)
Proxy-Authenticate: Negotiate
```text
Kerberos/NTLM for Windows environments.
### Bearer
```http
Proxy-Authenticate: Bearer realm="proxy"
Token-based authentication (OAuth 2.0 style).
Testing Proxy-Authenticate
Using curl
# Test proxy without credentials (should get 407)
curl -x http://proxy.example.com:3128 https://api.example.com
# Test with proxy credentials
curl -x http://proxy.example.com:3128 \
--proxy-user username:password \
https://api.example.com
# View proxy auth challenge
curl -x http://proxy.example.com:3128 -v https://api.example.com 2>&1 | \
grep -i "proxy-authenticate"
```javascript
### Using Node.js
```javascript
const https = require('https')
const options = {
host: 'api.example.com',
path: '/data',
headers: {
'Proxy-Authorization': 'Basic ' + Buffer.from('username:password').toString('base64')
},
proxy: {
host: 'proxy.example.com',
port: 3128
}
}
https.get(options, (res) => {
if (res.statusCode === 407) {
console.log('Proxy auth required:', res.headers['proxy-authenticate'])
} else {
console.log('Success:', res.statusCode)
}
})
Using fetch with proxy
// Configure proxy (Node.js with proxy agent)
const ProxyAgent = require('proxy-agent')
const proxyAuth = Buffer.from('username:password').toString('base64')
const agent = new ProxyAgent({
protocol: 'http',
host: 'proxy.example.com',
port: 3128,
headers: {
'Proxy-Authorization': `Basic ${proxyAuth}`
}
})
fetch('https://api.example.com/data', { agent })
.then((res) => console.log('Response:', res.status))
.catch((err) => console.error('Error:', err))
Related Headers
- Proxy-Authorization - Client sends proxy credentials
- WWW-Authenticate - Origin server auth challenge
- Authorization - Client sends origin server credentials
- Authentication-Info - Additional auth info in response
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?
Corporate proxies often require authentication to access the internet. The proxy sends 407 with Proxy-Authenticate, and clients respond with Proxy-Authorization.
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.