505 HTTP Version Not Supported
Server error response defined by RFC 9110 §15.6.6.
Last updated July 27, 2026
Common causes at a glance
- A client speaking cleartext HTTP to a TLS port, or TLS to a cleartext port
- A hand-written client emitting a malformed or invented version token
- An HTTP/2 prior-knowledge request sent to a server that only speaks HTTP/1.1
- A server or WAF deliberately rejecting a version it considers unsafe
Reproduce it
printf 'GET / HTTP/2.0\r\nHost: www.example.com\r\n\r\n' | nc www.example.com 80What 505 HTTP Version Not Supported means
Version negotiation in modern HTTP does not go through 505. HTTPS clients
choose between HTTP/1.1 and HTTP/2 with ALPN during the TLS handshake, and HTTP/3 is
advertised with Alt-Svc. By the time a request line is parsed, both ends
have already agreed. That leaves 505 for the pathological cases: a request line claiming
a major version the server will not speak, or bytes that merely look like one.
The most common real sighting is a client sending cleartext HTTP to a port expecting
TLS, or the reverse. A TLS ClientHello starts with the byte 0x16, and a
server parsing that as a request line produces garbage; some answer 400, some 505, some
close the connection. Old crawlers and hand-written scripts emitting
HTTP/1.2 or an empty version token land in the same bucket.
What the spec says
RFC 9110 §15.6.6 says the server is unable or unwilling to complete the request using the same major version as the client, as described in §2.5, and that it SHOULD generate a representation explaining why that version is not supported and what protocols are. That second half is almost universally ignored; most 505 bodies say nothing useful. Note the word major: refusing HTTP/1.1 while supporting HTTP/1.0 is not what 505 is for, since minor version handling falls under the parsing rules in RFC 9112 §2.3. HTTP/2 has no equivalent for a malformed connection preface — that is a connection error, not a status code.
What actually causes it
Port confusion accounts for most of it. Point a plain
http:// URL at 443 and the TLS listener sees a request line where it
expected a ClientHello; point https:// at 80 and the HTTP parser sees
binary. Either can surface as 505 depending on the server. The second source is
prior-knowledge HTTP/2, where curl --http2-prior-knowledge against
cleartext sends a preface an HTTP/1.1-only server cannot parse. Legacy embedded clients
and scanners produce the rest. If 505 comes from your own server for legitimate traffic,
look for a WAF rule pinning acceptable versions.
How to debug it
Pin the version explicitly and watch which combinations work, because curl will happily downgrade on its own and hide the problem:
curl -sS -o /dev/null -w '1.1 -> %{http_code}\n' --http1.1 https://www.example.com/
curl -sS -o /dev/null -w '2 -> %{http_code}\n' --http2 https://www.example.com/
If --http1.1 succeeds and --http2 does not, something is
mishandling negotiation. Check what ALPN actually agreed with
openssl s_client -alpn h2 -connect host:443, or the
TLS inspector. Then confirm the scheme matches the port: a 505 or
400 arriving instantly on a URL where you typed the port by hand is nearly always
cleartext meeting TLS.
Working examples
curl
# Force each version in turn; curl otherwise negotiates and hides the failure.
curl -sS -o /dev/null -w '1.0 -> %{http_code}\n' --http1.0 https://www.example.com/
curl -sS -o /dev/null -w '1.1 -> %{http_code}\n' --http1.1 https://www.example.com/
curl -sS -o /dev/null -w '2 -> %{http_code}\n' --http2 https://www.example.com/
# Send a bogus version token by hand and read the raw reply.
printf 'GET / HTTP/2.0\r\nHost: www.example.com\r\n\r\n' | nc www.example.com 80
Python (requests)
import socket
import requests
# requests speaks HTTP/1.1 only, so it cannot produce a version mismatch itself.
# Confirm the server is healthy over 1.1, then test other versions with a socket.
print(requests.get("https://www.example.com/", timeout=10).status_code)
s = socket.create_connection(("www.example.com", 80), timeout=5)
s.sendall(b"GET / HTTP/2.0\r\nHost: www.example.com\r\n\r\n")
print(s.recv(120).decode("latin-1").splitlines()[0])
s.close()
Node (fetch)
// undici (Node's fetch) negotiates the version for you; to observe a
// version rejection you need a raw socket.
import net from "node:net";
const sock = net.connect(80, "www.example.com", () => {
sock.write("GET / HTTP/2.0\r\nHost: www.example.com\r\n\r\n");
});
sock.once("data", (buf) => {
console.log(buf.toString("latin1").split("\r\n")[0]);
sock.destroy();
});
sock.on("error", (err) => console.error("socket error:", err.message));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.
Other 5xx codes
- 500 Internal Server Error
- 501 Not Implemented
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 506 Variant Also Negotiates
- 507 Insufficient Storage
- 508 Loop Detected
- 510 Not Extended
- 511 Network Authentication Required
- 520 Web Server Returns an Unknown Error
- 521 Web Server Is Down
- 522 Connection Timed Out
- 523 Origin Is Unreachable
- 524 A Timeout Occurred
- 525 SSL Handshake Failed
- 526 Invalid SSL Certificate
- 530 Unable to Resolve Origin Hostname