I am setting up an Nginx server with an SSL certificate, but whenever I try to access my site via the standard domain, I get a 400 Bad Request error stating that a plain HTTP request was sent to an HTTPS port. It seems like Nginx is expecting encrypted traffic on port 443 but receiving unencrypted data. How should my server block be configured to automatically redirect users from port 80 to port 443 without triggering this specific error?
3 answers
This error occurs because you likely have ssl on; or the ssl parameter inside a listen directive that is receiving standard HTTP traffic. In modern Nginx versions, you should use listen 443 ssl; for your secure block. To fix the "plain HTTP" error, ensure you have a separate server block listening on port 80 that explicitly redirects to the HTTPS version of your site using return 301 https://$host$request_uri;. This separates the unencrypted traffic from the encrypted port, preventing the protocol mismatch that causes the 400 error.
Are you using a load balancer or a proxy like Cloudflare in front of your Nginx server? Sometimes the SSL termination happens at the proxy level, and if the proxy forwards traffic to Nginx over port 443 without using SSL, you will see this exact error message in your logs.
Make sure you haven't accidentally combined your port 80 and port 443 configurations into a single server block without proper conditional checks, as this is the most common cause.
I agree with Sarah. I once had both listen 80; and listen 443 ssl; in one block, and it caused total chaos with the request handling. Splitting them into two distinct server blocks in the Nginx config file is the cleanest and most reliable solution.
Joshua, that is a vital distinction to make. If the load balancer is handling the certificate, the internal communication between the balancer and Nginx usually happens over port 80. If you have Nginx configured to "listen 443 ssl" but the balancer sends plain traffic to that port, it breaks. You should check your 'X-Forwarded-Proto' headers to see exactly what protocol is reaching your web server.