HTTP 497 Status Code

Plain HTTP sent to an HTTPS port is a small nginx edge case. The fix is tiny, but the habit is bigger: make protocol mistakes converge.

August 12, 2018 · 3 min read · 561 words
.md

Deploying SSL is not enough if a client or integration still builds the URL with the wrong scheme. A request to http://example.com:443 reaches the HTTPS listener as plain HTTP, and nginx can answer with status 497.

The site is not missing. The certificate is not necessarily broken. The server is telling you that the protocol and the port do not agree.

wrong-scheme.sh
curl -I http://example.com:443

That sounds like a small detail, but small edge failures create real noise. A browser might recover after a user retries. A script might keep failing. A monitoring check might report the wrong class of problem. Support sees "the site is down" while nginx is quietly saying "wrong scheme."

flowchart LR A[client sends http://host:443] --> B[nginx HTTPS listener] B --> C{plain HTTP on TLS port} C -->|yes| D[497 from nginx] D --> E[rewrite to https://host:443/path] E --> F[client retries with the right scheme]

The useful response is to normalize the mistake into a correct URL when it is safe to do so.

The nginx rule is tiny:

nginx-497.conf
error_page 497 https://$host:$server_port$request_uri;

It tells nginx to turn the protocol mismatch into a redirect. The browser or client gets pointed to the HTTPS version of the same host, port, and path. The failure still exists in the logs, but the user path becomes much less surprising.

There are a few details worth making explicit.

First, I would scope this to the server blocks where preserving the port is correct. If a proxy, load balancer, or CDN terminates TLS before nginx, $server_port may not be the public port the client should see. In that setup, the redirect should use the canonical public URL instead of blindly echoing internal listener shape.

Second, I would make the behavior observable. A tiny counter or log field for 497_redirected helps separate harmless bad-scheme traffic from a real client integration bug. If the same customer, mobile version, or internal caller keeps hitting it, that is no longer random internet noise. It is feedback from the system.

Third, I would test it as configuration, not as folklore. The test does not need to be fancy. A single deploy check that sends http://domain:443/path and expects a redirect to https://... is enough to protect the rule from being lost during nginx cleanup.

release-check.sh
curl -I --max-time 5 http://example.com:443/health \
  | grep -i '^location: https://example.com'

Production reliability is made of these small agreements. Scheme, port, redirect, health check, log line. None of them look impressive alone. Together they stop the team from spending time on avoidable confusion.

Name the failure precisely, make the common mistake safe, and write the check so the fix stays in place.

Was this useful?

React if it helped; comment if you have a concrete question, correction, or field note.

-

Discussion (0)

Practical notes, bug stories, and disagreement with receipts are welcome.

No comments yet. A useful first comment is usually a field note: what failed, what held, or what you would check before shipping this idea.
Start the discussion
Markdown is supported. Keep it concrete and useful.