305 Use Proxy
Redirection response defined by RFC 9110 §15.4.6.
Last updated July 27, 2026
Common causes at a glance
- A very old server or appliance still implementing RFC 2616 proxy semantics
- A hand-rolled status table picking 305 because the name sounded right
- A test fixture or mock enumerating every 3xx code
- A misconfigured corporate gateway attempting in-band proxy discovery
Reproduce it
curl -sSI https://legacy.example.com/resource | grep -iE '^(HTTP|location)'What 305 Use Proxy means
The idea was reasonable: a server behind a mandatory proxy could answer a direct request by naming the proxy the client should use instead. The implementation was not. Nothing authenticated the instruction, so any origin — or anything able to inject a response — could tell a browser to route subsequent traffic through a host of its choosing. That is a man-in-the-middle primitive delivered by a status code.
Browsers removed support in response, and the code was deprecated in the HTTP/1.1 revision on exactly those grounds. Today a 305 arrives as an opaque response with a target no client will act on.
What the spec says
RFC 9110 §15.4.6 is one sentence: the code was defined in a previous version of the specification and is now deprecated, pointing at Appendix B of RFC 7231 for the change. RFC 7231 had already deprecated it over security concerns about in-band configuration of a proxy. The original definition lives in the obsolete RFC 2616. Note that deprecation does not remove the registration: 305 is still listed in the IANA HTTP Status Code Registry, because registrations survive the specifications that retire them. A conforming client today has no defined behaviour to implement.
Why it is dead
The reason 305 failed is worth understanding, because the same temptation recurs. In-band proxy configuration means an unauthenticated response gets to decide where a client’s future traffic goes, and there is no way to make that safe in a protocol where any hop can write a response. Everything that replaced it pushes the configuration out of band: a proxy auto-config file fetched from somewhere the client already trusts, an OS-level proxy setting, or 407 Proxy Authentication Required from a proxy the client is already talking to. If you are tempted to emit 305, you want one of those instead.
How to debug it
There is not much to diagnose: no client will follow it, so a 305 shows up
as a request that simply stopped. curl reports the status and the
Location, and a browser shows a blank page or an error.
curl -sSI https://legacy.example.com/resource | grep -iE '^(HTTP|location)'
If you own the server, the fix is to stop sending it. Route through the proxy on the
server side, return 502 if you cannot reach it, and configure
clients out of band. If something in your path emits 305, set the proxy explicitly with
curl -x or the HTTPS_PROXY environment variable and confirm
the resource is reachable that way.
Working examples
curl
# curl reports the status but will not act on it.
curl -sSI https://legacy.example.com/resource | grep -iE '^(HTTP|location)'
# The out-of-band equivalent: name the proxy yourself.
curl -sSI -x http://proxy.internal:3128 https://legacy.example.com/resource
# Or via the environment, which curl and most tooling respect:
# export HTTPS_PROXY=http://proxy.internal:3128
Python (requests)
import requests
# requests does not treat 305 as a redirect; you get the raw response.
resp = requests.get("https://legacy.example.com/resource", timeout=10)
print(resp.status_code, resp.headers.get("Location"))
# Configure the proxy explicitly instead of trusting the response.
resp = requests.get(
"https://legacy.example.com/resource",
proxies={"https": "http://proxy.internal:3128"},
timeout=10,
)
print(resp.status_code)
Node (fetch)
// fetch() ignores 305 entirely: it is not in the redirect status set.
const resp = await fetch("https://legacy.example.com/resource");
console.log(resp.status, resp.headers.get("location"));
// 305 arrives as an ordinary response with resp.ok === false.
// Node honours an explicit dispatcher rather than any in-band hint:
// import { ProxyAgent, setGlobalDispatcher } from "undici";
// setGlobalDispatcher(new ProxyAgent("http://proxy.internal:3128"));Reference and tooling
The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.