Nginx is trying to serve plain HTTP requests on the port configured for HTTPS, and the client’s browser is getting confused.
Common Causes and Fixes
-
Incorrect
listendirective in Nginx configuration.- Diagnosis: Check your Nginx configuration files (e.g.,
/etc/nginx/nginx.confand files in/etc/nginx/sites-available/or/etc/nginx/conf.d/). Look forlistendirectives within yourserverblocks. - Cause: You might have accidentally configured a
serverblock to listen on port443(the standard HTTPS port) without specifyingsslorhttp2, or you might have a separateserverblock listening on port80(HTTP) that’s catching requests intended for your HTTPS site. The most common mistake is having a default server block on port 443 that isn’t set up for SSL. - Fix: Ensure your HTTPS
serverblock explicitly listslisten 443 ssl http2;(orlisten 443 ssl;if not using HTTP/2). For your HTTPserverblock (if you have one for redirects or plain HTTP access), ensure it listens onlisten 80;and redirects to HTTPS.server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # ... other SSL settings } - Why it works: Nginx uses the
listendirective to bind to specific IP addresses and ports. By explicitly defininglisten 443 ssl http2;, you tell Nginx to expect SSL/TLS encrypted traffic on that port. Withoutssl, it defaults to plain HTTP, leading to the mismatch. The HTTP block withreturn 301ensures all HTTP traffic is correctly redirected to HTTPS.
- Diagnosis: Check your Nginx configuration files (e.g.,
-
Missing or incorrect SSL certificate configuration.
- Diagnosis: In your Nginx configuration for the HTTPS
serverblock, verify the paths tossl_certificateandssl_certificate_key. - Cause: If Nginx is configured to listen on
443 sslbut the certificate files are missing, inaccessible, or misconfigured, it can fail to establish an SSL connection. This can manifest as the browser receiving an unexpected HTTP response on the HTTPS port. - Fix: Ensure the
ssl_certificateandssl_certificate_keydirectives point to valid, readable certificate files. For example:
If using Certbot, these paths are typically managed automatically. Runssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;sudo certbot --nginxto reconfigure or renew. - Why it works: Nginx needs valid SSL certificates to perform the TLS handshake required for HTTPS. If these are not found or are invalid, the handshake fails, and Nginx might fall back to serving whatever content it has configured for that port as plain HTTP, confusing the browser which expects encryption.
- Diagnosis: In your Nginx configuration for the HTTPS
-
Conflicting
server_namedirectives or duplicateserverblocks.- Diagnosis: Review all
serverblocks in your Nginx configuration for the relevant domain name. - Cause: If you have multiple
serverblocks that could potentially match a request (e.g., one on port 80 and one on port 443 for the sameserver_name), or if you have duplicateserverblocks with the sameserver_nameandlistendirectives, Nginx might pick the wrong one. This is especially true if a default server is not clearly defined. - Fix: Ensure each
server_nameis uniquely handled by a specificserverblock. Use_for a default server block if necessary. For example, a strong default server for HTTP could be:
And ensure your specificserver { listen 80 default_server; listen [::]:80 default_server; server_name _; # Catches all other hostnames on port 80 return 301 https://$host$request_uri; }server_name example.com;blocks are correctly configured for their respective ports. - Why it works: Nginx matches incoming requests to
serverblocks based onlistendirectives andserver_namevalues. Ambiguity can lead to Nginx selecting a block that doesn’t have SSL enabled when it should, or serving the wrong content entirely. Explicitly defining default servers and uniqueserver_namematches resolves these conflicts.
- Diagnosis: Review all
-
HTTP/2 not enabled or incorrectly configured.
- Diagnosis: Check the
listendirective in your HTTPSserverblock. - Cause: Browsers often expect HTTP/2 for HTTPS connections to improve performance. If you have
listen 443 ssl;but nothttp2, or if Nginx wasn’t compiled with HTTP/2 support, some clients might misinterpret the connection. While less common as a direct cause of plain HTTP on HTTPS port, it can contribute to connection issues. - Fix: Ensure your
listendirective includeshttp2if you intend to use it:
If Nginx was not compiled with thelisten 443 ssl http2;--with-http_v2_moduleflag, you’ll need to recompile Nginx or install a pre-compiled version that supports it. Most modern Nginx packages do. - Why it works: HTTP/2 is a protocol negotiation that happens during the TLS handshake. Explicitly enabling it tells Nginx to advertise and support HTTP/2 capabilities, ensuring compatibility with clients that expect it on HTTPS connections.
- Diagnosis: Check the
-
Firewall blocking or misconfigured Nginx worker processes.
- Diagnosis: Check firewall rules (e.g.,
sudo ufw status,sudo iptables -L). Also, check Nginx worker process status. - Cause: A firewall might be interfering with Nginx’s ability to bind to port 443, or it could be configured to allow HTTP traffic on port 443. Less likely, but possible, are issues with Nginx worker processes not having the correct permissions to bind to privileged ports (<1024) if Nginx is not run as root (though typically Nginx starts as root and drops privileges).
- Fix: Ensure your firewall allows traffic on port 443:
Verify Nginx is running and its worker processes are healthy. If Nginx is not running as root, ensure it’s started via a process manager that handles initial port binding or that the user Nginx runs as hassudo ufw allow 443/tcp sudo ufw reloadCAP_NET_BIND_SERVICEcapabilities. - Why it works: Firewalls act as gatekeepers. Allowing traffic on port 443 ensures that incoming HTTPS requests can reach the Nginx process listening on that port. Correct process permissions ensure Nginx can actually bind to the required network socket.
- Diagnosis: Check firewall rules (e.g.,
-
Browser cache or local DNS issues.
- Diagnosis: Clear your browser’s cache, try an incognito/private browsing window, or test from a different device/network. Use
curl -v https://example.comto inspect the handshake. - Cause: Sometimes, the browser or local network caches an old, incorrect response or DNS record, leading it to believe it should be making a plain HTTP request or that the server is not responding correctly to HTTPS.
- Fix: Clear browser cache. Try
curl -v https://your_domain.com. Look forHTTP/1.1 200 OK(which is bad for HTTPS) or TLS handshake errors. Ifcurlalso fails, the issue is server-side. Ifcurlworks but the browser doesn’t, it’s likely a browser or local cache problem. - Why it works: Eliminating local caching layers ensures you are testing against the live server configuration, helping to isolate whether the problem is with Nginx or the client’s environment.
- Diagnosis: Clear your browser’s cache, try an incognito/private browsing window, or test from a different device/network. Use
After fixing these, the next error you might encounter is a "SSL certificate verification failed" if your certificate is expired or not trusted by the client’s CA.