497 HTTP Request Sent to HTTPS Port
Client error response defined by nginx.org.
Last updated July 27, 2026
Common causes at a glance
- A health check or probe configured with http:// against port 443
- A load balancer forwarding to the backend with the wrong scheme
- A hardcoded http:// URL that still carries an explicit :443 port
- A service mesh sidecar terminating TLS then re-sending cleartext to a TLS listener
Reproduce it
curl -s -o /dev/null -w '%{http_code}\n' http://www.example.com:443/healthzWhat 497 HTTP Request Sent to HTTPS Port means
A TLS listener expects a ClientHello. If the first bytes are
GET / HTTP/1.1 instead, nginx could drop the connection, but it parses the
request and returns an error the human on the other end can read. That is why the
response arrives unencrypted — there is no session to encrypt it with.
The scheme mismatch is the whole story: something is speaking http:// to a
port that only speaks https://. Since the request is fully parsed by then,
an error_page 497 handler can redirect to HTTPS.
There is no RFC
497 appears only in nginx’s SSL module documentation, in the
error processing section,
described as a regular request sent to the HTTPS port and offered for use with
error_page. There is no IANA registration and no standards text, so RFC 9110
§15’s rule that an unrecognised 4xx be treated as 400 is all a client
has to go on. Two consequences follow. The response is cleartext, so never render anything
sensitive in a 497 error page. And because it comes back on a port monitoring labels
HTTPS, a spike shows up as HTTPS errors even though none of those requests was
encrypted.
What actually causes it
Machines, not people. A human who mistypes the scheme lands on port 80
and gets redirected; producing 497 takes an explicit :443 alongside
http://, the signature of a configuration file. The classic source is a
health check block where the port was updated to 443 but the scheme stayed HTTP. Close
behind is a load balancer that terminates TLS at the edge and re-originates plaintext to
a backend still listening with ssl, which happens when someone enables TLS
on an origin without changing the upstream’s protocol setting.
How to debug it
Confirm the mismatch by forcing cleartext against the TLS port:
curl -s -o /dev/null -w '%{http_code}\n' http://www.example.com:443/healthz
A 400 or 497 there, with 200 for the same path over https://, settles it.
The access log shows 497 with the request line intact, and the User-Agent
usually identifies the offending component — a probe agent, a load balancer, a mesh
proxy.
Fix the caller, not the server. An
error_page 497 301 https://$host$request_uri; rule turns the symptom into a
redirect, but every mistaken request then pays a round trip in the clear, and a health
check that follows redirects reports healthy while still misconfigured. Check the chain
with the status checker.
Working examples
curl
# Cleartext against the TLS port. The explicit :443 is what makes it happen.
curl -s -o /dev/null -w '%{http_code}\n' http://www.example.com:443/healthz
# The same path spoken correctly.
curl -s -o /dev/null -w '%{http_code}\n' https://www.example.com/healthz
# See the raw cleartext response nginx sends back.
printf 'GET /healthz HTTP/1.1\r\nHost: www.example.com\r\n\r\n' \
| nc www.example.com 443 | head -5
Python (requests)
import requests
# Note the scheme/port mismatch: http:// with an explicit :443.
resp = requests.get("http://www.example.com:443/healthz", timeout=10)
print(resp.status_code) # 497, delivered in cleartext
# The body is nginx's error page, not your application's.
print(resp.text[:200])
# Correct call for comparison.
print(requests.get("https://www.example.com/healthz", timeout=10).status_code)
Node (fetch)
// Health checks written like the first line are the usual source of 497.
const bad = await fetch("http://www.example.com:443/healthz");
console.log(bad.status, bad.headers.get("server")); // 497 from nginx, unencrypted
const good = await fetch("https://www.example.com/healthz");
console.log(good.status);
// Catch the mismatch in config validation rather than at runtime:
const url = new URL("http://www.example.com:443/healthz");
if (url.protocol === "http:" && url.port === "443") {
throw new Error("cleartext request aimed at a TLS port");
}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 4xx codes
- 400 Bad Request
- 401 Unauthorized
- 402 Payment Required
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 406 Not Acceptable
- 407 Proxy Authentication Required
- 408 Request Timeout
- 409 Conflict
- 410 Gone
- 411 Length Required
- 412 Precondition Failed
- 413 Content Too Large
- 414 URI Too Long
- 415 Unsupported Media Type
- 416 Range Not Satisfiable
- 417 Expectation Failed
- 418 (Unused)
- 419 Page Expired
- 421 Misdirected Request
- 422 Unprocessable Content
- 423 Locked
- 424 Failed Dependency
- 425 Too Early
- 426 Upgrade Required
- 428 Precondition Required
- 429 Too Many Requests
- 431 Request Header Fields Too Large
- 444 No Response
- 451 Unavailable For Legal Reasons
- 494 Request Header Too Large
- 495 SSL Certificate Error
- 496 SSL Certificate Required
- 499 Client Closed Request