Skip to content

305 Use Proxy (Deprecated)

What is HTTP 305 Use Proxy (Deprecated)?

305 Use Proxy (Deprecated)
A long time ago, websites could send a '305 Use Proxy' message that said 'Don't ...
HTTP 305 Use Proxy (Deprecated) status code illustration

Explain Like I’m 3

This is an old rule that we don’t use anymore because it wasn’t safe. It’s like an old toy that broke, so we don’t play with it. We have better, safer toys now!

Example: Imagine a sign that said ‘Go ask that person to help you find your toy’ - but we can’t be sure that person is nice! So we stopped using that sign and now ask safe people instead.

Explain Like I’m 5

A long time ago, websites could send a ‘305 Use Proxy’ message that said ‘Don’t talk to me directly - talk to this other computer first, and it will help you!’ The problem was that bad people could trick your computer into talking to a bad ‘helper’ computer that would spy on you or change what you see. Because it wasn’t safe, everyone stopped using it. Modern web browsers completely ignore this message now. It’s like an old rule from a game that we realized was dangerous, so we made new, better rules instead.

Example: Imagine if a website could force you to go through a stranger’s house to get to the website. The stranger could listen to everything you say! That’s not safe, so we don’t allow that anymore. Instead, if you want to use a helper (proxy), you set it up yourself in your computer settings where you control it.

Jr. Developer

HTTP 305 Use Proxy is a deprecated status code from early HTTP/1.1 (RFC 2616) that instructed clients to access the requested resource through a proxy specified in the Location header. It was deprecated in RFC 7231 and completely removed in RFC 9110 due to severe security concerns. The fundamental problem: allowing origin servers to dictate proxy usage enables man-in-the-middle attacks. A malicious server could return 305 with a Location pointing to an attacker-controlled proxy, forcing all client traffic through that proxy where it could be intercepted, logged, or modified. Modern browsers (Chrome, Firefox, Safari) completely ignore 305 responses and treat them as errors. Never use this status code. For legitimate proxy needs: configure proxies at the system/browser level (manual settings, PAC files), use transparent proxies at the network layer, or use VPNs. For redirects, use 307 Temporary Redirect or 308 Permanent Redirect instead. 305 exists only for historical context and should never appear in production systems.

Example: Legacy server returns: ‘HTTP/1.1 305 Use Proxy’ with ‘Location: http://proxy.example.com’. Expected behavior: client should re-request through that proxy. Actual modern behavior: browsers ignore it, display error page. Why: proxy.example.com could be malicious, intercepting HTTPS traffic, stealing credentials, injecting malware.

Code Example

// DO NOT USE - 305 is deprecated and insecure!
// This code is for educational purposes only
// WRONG: Never implement this
app.get('/resource', (req, res) => {
// This is dangerous and won't work in modern browsers
res.status(305)
.set('Location', 'http://proxy.example.com')
.send('Use proxy'); // Browsers will ignore this
});
// RIGHT: Modern approaches for proxy usage
// 1. Client-side proxy configuration (browser settings)
// Users configure proxy manually or via PAC file:
// Proxy: http://proxy.example.com:8080
// 2. System-level proxy (environment variables)
// HTTP_PROXY=http://proxy.example.com:8080
// HTTPS_PROXY=http://proxy.example.com:8080
// 3. Transparent proxy (network level, invisible to client)
// Network infrastructure routes traffic through proxy
// 4. For redirects, use 307/308 instead:
app.get('/old-resource', (req, res) => {
// Temporary redirect, preserves method
res.redirect(307, '/new-resource');
});
app.get('/moved-permanently', (req, res) => {
// Permanent redirect, preserves method
res.redirect(308, 'https://newdomain.com/resource');
});

Crash Course

305 Use Proxy, originally defined in RFC 2616 Section 10.3.6, indicated the requested resource must be accessed through the proxy specified in the Location header, with all future requests to that resource also using the proxy. RFC 7231 deprecated it in Appendix B, and RFC 9110 (current HTTP Semantics spec) completely removed it. Deprecation rationale: security concerns regarding in-band configuration of proxies. The attack vector: malicious origin servers could return 305 with Location pointing to attacker-controlled proxies, creating forced man-in-the-middle attacks. Even with HTTPS, the initial HTTP request could be intercepted and responded to with 305, redirecting subsequent requests (including sensitive ones) through the malicious proxy. Browser vendors recognized this risk and never fully implemented automatic 305 handling - Firefox, Chrome, Safari, and Edge all ignore 305 responses. The intended use case (dynamic proxy configuration) is better served by: Proxy Auto-Configuration (PAC) files for browser-level proxy selection, Web Proxy Auto-Discovery (WPAD) for automatic proxy discovery via DHCP/DNS, manual proxy settings controlled by users/admins, and transparent proxies at network infrastructure level. For HTTP redirects not involving proxy semantics, use 307 (temporary, preserves method) or 308 (permanent, preserves method) instead.

Example: Historical 305 scenario: Client GETs http://cdn.example.com/file. Server returns ‘305 Use Proxy’ with Location: http://cache-proxy.example.com. Intent: client should GET http://cdn.example.com/file via cache-proxy.example.com. Attack scenario: Attacker intercepts initial request, returns 305 pointing to evil-proxy.com. Client routes all subsequent traffic through evil-proxy.com, enabling credential theft, session hijacking, content injection. Modern reality: browsers display error, never automatically use the specified proxy.

Code Example

// Historical context: How 305 was intended to work
// (DO NOT implement - for educational understanding only)
const express = require('express');
const app = express();
// DEPRECATED: 305 Use Proxy (never use this!)
app.get('/legacy-api/resource', (req, res) => {
// Original intent: force clients through caching proxy
res.status(305)
.set('Location', 'http://cache.example.com')
.send();
// What should happen (but doesn't in modern browsers):
// 1. Client receives 305
// 2. Client re-requests http://example.com/legacy-api/resource
// but routes it through http://cache.example.com
// 3. All future requests to this resource use the proxy
// What actually happens:
// Modern browsers ignore 305, display error page
});
// MODERN ALTERNATIVES:
// Alternative 1: Use 307/308 for actual redirects
app.get('/api/resource', (req, res) => {
// Temporary redirect, preserves POST/PUT/DELETE method
res.redirect(307, 'https://cdn.example.com/api/resource');
});
// Alternative 2: PAC file for proxy configuration
app.get('/proxy.pac', (req, res) => {
// Proxy Auto-Configuration
const pacFile = `
function FindProxyForURL(url, host) {
// Use proxy for specific domains
if (shExpMatch(host, "*.internal.example.com")) {
return "PROXY proxy.example.com:8080";
}
// Direct connection for everything else
return "DIRECT";
}
`;
res.set('Content-Type', 'application/x-ns-proxy-autoconfig');
res.send(pacFile);
});
// Alternative 3: Reverse proxy pattern (server-side)
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/api', createProxyMiddleware({
target: 'http://backend.example.com',
changeOrigin: true,
pathRewrite: {
'^/api': '/v1/api'
}
}));
// Alternative 4: Client-configured proxy (Node.js example)
const https = require('https');
const { HttpsProxyAgent } = require('https-proxy-agent');
// User/admin configures proxy via environment or settings
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
const agent = new HttpsProxyAgent(proxyUrl);
https.get('https://api.example.com/data', { agent }, (res) => {
console.log('Request made through configured proxy');
});
} else {
https.get('https://api.example.com/data', (res) => {
console.log('Direct request, no proxy');
});
}

Deep Dive

The 305 Use Proxy status code represents a failed experiment in HTTP protocol design. Defined in RFC 2616 Section 10.3.6 (HTTP/1.1, June 1999), it specified that the requested resource MUST be accessed through the proxy given by the Location field, and the recipient is expected to repeat this single request via the proxy; it MUST NOT be cached by a shared cache. The intent was to enable dynamic, server-driven proxy configuration for scenarios like content filtering, geographic routing, or caching optimization. However, the specification contained fundamental security flaws that made safe implementation impossible, leading to its deprecation in RFC 7231 (2014) and complete removal in RFC 9110 (2022).

Technical Details

Security analysis of 305: The core vulnerability is that it enables forced man-in-the-middle attacks via HTTP-level proxy injection. Attack scenario: (1) Victim requests http://bank.com/login over HTTP (initial request, before HTTPS redirect), (2) Attacker intercepts request and responds with ‘305 Use Proxy, Location: http://attacker-proxy.com’, (3) If client honors 305, subsequent requests to bank.com route through attacker-proxy.com, (4) Attacker proxy can downgrade HTTPS to HTTP, steal credentials, inject JavaScript, or harvest session tokens. Even if the original request was HTTPS, certain edge cases (HTTP Strict Transport Security not set, user typo, or legacy clients) could allow initial HTTP interception. The specification required clients to “repeat this single request via the proxy” but didn’t specify cryptographic validation of proxy authenticity or user confirmation, making exploitation trivial.\n\nHistorical context and implementation: RFC 2616 (1999) introduced 305 with little consideration for security implications. Browser vendors immediately recognized the risk: Netscape Navigator, Internet Explorer, and early Mozilla Firefox never implemented automatic 305 handling. By 2004, most browsers treated 305 as an error condition. RFC 2616 itself acknowledged the security concern with the prohibition “MUST NOT be cached by a shared cache” - caching would amplify the attack surface by persisting malicious proxy assignments. RFC 7231 (2014) formally deprecated 305 in Appendix B with the note: “Defined in a previous version of this specification and is now deprecated, due to security concerns regarding in-band configuration of a proxy.” RFC 9110 (2022) completely removed 305 from the status code registry, effectively declaring it a historical artifact.\n\nModern proxy configuration mechanisms that replaced 305’s intended use cases: Proxy Auto-Configuration (PAC) files are JavaScript-based proxy selection logic served via HTTP/HTTPS, allowing complex routing rules (by domain, subnet, URL pattern). PAC files are user/admin-controlled and cryptographically verifiable via HTTPS. Web Proxy Auto-Discovery Protocol (WPAD) uses DHCP option 252 or DNS to automatically discover PAC file location, but has its own security issues (deprecated by some browsers due to WPAD spoofing attacks). Manual proxy settings in browsers/OS allow user-controlled proxy configuration via GUI or config files. Transparent proxies operate at network layer (OSI layer 4/7), intercepting traffic without client configuration - used by ISPs, corporations, and CDNs. SOCKS proxies provide circuit-level proxying for arbitrary protocols. HTTP CONNECT method (tunneling) enables proxy traversal for HTTPS traffic while preserving end-to-end encryption.\n\nComparison with related status codes: 300 Multiple Choices offers client-driven selection among representations (not proxies), deprecated due to poor browser support but less dangerous. 306 (Switch Proxy) was reserved in RFC 26…

Code Example

// Comprehensive guide: 305 Use Proxy (deprecated) and modern alternatives\nconst express = require('express');\nconst { createProxyMiddleware } = require('http-proxy-middleware');\nconst app = express();\n\n// === DO NOT USE: 305 Use Proxy (educational only) ===\n\napp.get('/INSECURE-305-EXAMPLE', (req, res) => {\n // This is WRONG and DANGEROUS - for documentation only!\n res.status(305)\n .set('Location', 'http://proxy.example.com')\n .send('Use specified proxy');\n \n // Why this is insecure:\n // 1. Attacker-controlled server can force clients through malicious proxy\n // 2. Man-in-the-middle attack vector for credential theft\n // 3. HTTPS downgrade attacks possible\n // 4. No cryptographic validation of proxy authenticity\n // 5. No user confirmation or control\n \n // Modern browsers will ignore this and show error page\n});\n\n// === CORRECT MODERN ALTERNATIVES ===\n\n// Alternative 1: Proxy Auto-Configuration (PAC) file\napp.get('/proxy.pac', (req, res) => {\n // Serve PAC file over HTTPS for security\n const pacContent = `\n function FindProxyForURL(url, host) {\n // Internal domains use corporate proxy\n if (shExpMatch(host, \\\"*.internal.corp.com\\\")) {\n return \\\"PROXY proxy.corp.com:8080; DIRECT\\\";\n }\n \n // CDN domains go direct\n if (shExpMatch(host, \\\"*.cdn.example.com\\\")) {\n return \\\"DIRECT\\\";\n }\n \n // Geographic routing based on domain\n if (shExpMatch(host, \\\"*.eu.example.com\\\")) {\n return \\\"PROXY eu-proxy.example.com:8080; DIRECT\\\";\n }\n \n if (shExpMatch(host, \\\"*.us.example.com\\\")) {\n return \\\"PROXY us-proxy.example.com:8080; DIRECT\\\";\n }\n \n // Default: direct connection\n return \\\"DIRECT\\\";\n }\n `;\n \n res.set('Content-Type', 'application/x-ns-proxy-autoconfig');\n res.set('Cache-Control', 'public, max-age=3600');\n res.send(pacContent);\n \n // User configures browser to use this PAC file:\n // Network Settings -> Automatic proxy configuration URL:\n // https://example.com/proxy.pac\n});\n\n// Alternative 2: Reverse proxy pattern (server-side, transparent to clients)\napp.use('/api/backend', createProxyMiddleware({\n target: 'http://backend-service.internal:3000',\n changeOrigin: true,\n pathRewrite: {\n '^/api/backend': ''\n },\n onProxyReq: (proxyReq, req, res) => {\n // Add authentication headers\n proxyReq.setHeader('X-Forwarded-For', req.ip);\n proxyReq.setHeader('X-Forwarded-Proto', req.protocol);\n },\n onError: (err, req, res) => {\n console.error('Proxy error:', err);\n res.status(502).json({\n error: 'Bad Gateway',\n message: 'Backend service unavailable'\n });\n }\n}));\n\n// Alternative 3: Use 307/308 for actual redirects (not proxy)\napp.get('/api/v1/users', (req, res) => {\n // Temporary redirect to new API version, preserves POST/PUT/DELETE\n res.redirect(307, '/api/v2/users...

Frequently Asked Questions

Why was HTTP 305 Use Proxy deprecated?

305 was deprecated due to severe security concerns. It allowed malicious servers to force clients through attacker-controlled proxies, enabling man-in-the-middle attacks, credential theft, and traffic interception. Allowing servers to dictate proxy usage violated security principles - proxy configuration should be user/admin-controlled, not server-controlled. RFC 7231 deprecated it in 2014, and RFC 9110 removed it entirely in 2022.

Do modern browsers support HTTP 305?

No. All modern browsers (Chrome, Firefox, Safari, Edge) completely ignore 305 responses and treat them as errors. This has been the case since the early 2000s - browser vendors recognized the security risk and never implemented automatic 305 handling. If a server returns 305 today, users will see error pages.

What should I use instead of 305 Use Proxy?

For proxy configuration: use PAC (Proxy Auto-Configuration) files, manual browser/system proxy settings, or transparent proxies at the network level. For redirects: use 307 Temporary Redirect or 308 Permanent Redirect. For network authentication (captive portals): use 511 Network Authentication Required. For server-side routing: use reverse proxies like nginx or load balancers.

Can 305 Use Proxy be used safely in any scenario?

No. There is no safe use case for 305. The fundamental design - allowing origin servers to force client proxy configuration via HTTP responses - is inherently insecure and enables trivial man-in-the-middle attacks. It should never be implemented in server code, and clients should never honor it. Even legacy systems supporting 305 are vulnerable and should be upgraded.

Is 305 Use Proxy related to HTTP CONNECT proxying?

No. HTTP CONNECT (RFC 9110) is a legitimate method for tunneling HTTPS through proxies while preserving end-to-end encryption. The client initiates CONNECT to a user-configured proxy. 305, by contrast, was a server-initiated attempt to force proxy usage, which is fundamentally insecure. CONNECT is widely used and secure; 305 is deprecated and dangerous.

Common Causes

  • Encountering legacy server from late 1990s/early 2000s (extremely rare)
  • Misconfigured server returning 305 by mistake (configuration error)
  • Security testing/penetration testing checking for outdated client software
  • Historical HTTP documentation or textbooks describing obsolete codes
  • Academic research into HTTP protocol evolution
  • Malicious server attempting man-in-the-middle attack (theoretical, won’t work)
  • Web archive/museum preserving historical HTTP implementations

Implementation Guidance

  • Never implement 305 responses in server code - use 307/308 for redirects instead
  • If you see 305 in legacy code, remove it immediately and use modern alternatives
  • For proxy configuration, use PAC files over HTTPS instead of HTTP-level enforcement
  • Implement reverse proxies (nginx, HAProxy) for server-side routing/load balancing
  • Use manual browser/system proxy settings controlled by users, not servers
  • For network authentication, use 511 Network Authentication Required, not 305
  • Upgrade legacy clients that honor 305 - they’re vulnerable to MITM attacks
  • Log any 305 responses as potential security incidents or misconfigurations
  • In testing environments, verify that clients properly ignore/reject 305 responses
  • Document in security policies that 305 is forbidden and why it’s dangerous
📚 Sources:
🕐 Last updated: January 5, 2026

Comments