# 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.

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.

```bash title="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."

```mermaid
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 title="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.

<Decision title="Normalize the common edge">
  When a failure is caused by a common protocol mistake, make the server response converge toward
  the correct path. Do not make every client rediscover the same operational detail.
</Decision>

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.

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

<Principle title="Config deserves regression checks too">
  Runtime behavior often lives outside application code. If a one-line nginx rule protects a real
  user path, it deserves the same review and verification habit as a handler or worker.
</Principle>

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.
