Skip to content
Control Plane Labs

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/healthz

What 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