Nginx 400: "The Plain HTTP Request Was Sent to HTTPS Port"

The port speaks TLS; the client spoke HTTP. Either the client hit the wrong port/scheme, or your nginx has ssl on a listener without an error-tolerant fallback. Both diagnosis and fix are one line each.

What you'll see

Root causes

Client mismatch: wrong scheme or port

http://host:443, or a proxy/monitoring tool configured with http against your TLS listener. The nginx error is literally accurate: the client must use https:// on that port.

Mixed traffic on one listener

A 443 listener receiving both TLS and plain (some legacy clients, TCP checks). nginx detects the plain bytes against the ssl listener and emits the 400 explanation page.

Fix it

  1. Reproduce the client call correctly
    curl -v https://host:443/   # if this works, the failing caller is using http:// — fix it there
  2. Fix the misconfigured caller
    # health checks, proxy_pass http://backend references, and upstream URLs against the TLS port must use https://
  3. Separate listeners instead of error pages
    # listen 80;  server with 301 to https  +  listen 443 ssl;  — each port speaks exactly one protocol
  4. Legacy plain-on-443 needs: use the error_page 497 trick deliberately
    # error_page 497 =301 https://$host$request_uri;   # nginx code 497 = this exact mismatch, redirect it to HTTPS properly

Field note

497 is nginx's internal code for plain-HTTP-on-TLS-port: mapping it to a 301 gives old clients/bookmarks a self-healing path to the right scheme. This error is nginx protecting the protocol contract — a better description than a failure. The only question is which side is wrong: usually the caller.

Common questions

Why did this start after I added ssl to the listener?

The listener now requires TLS for everything. Any caller still using http:// against that port gets the 400. Audit the callers: health checks and internal proxies are the usual stragglers.

What is error 497?

nginx's internal status for exactly this mismatch (plain bytes on an ssl listener). It's why error_page 497 works as a targeted fix: it fires only for this case, never for real 400s.

Ship it right the first time

Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.

Browse the template store →

One-time. Yours to modify. Instant download from the NinjaOps template store.