SSL_ERROR_RX_RECORD_TOO_LONG is Firefox reporting that it opened a TLS connection and received something that is not TLS. The name describes the symptom literally: the first bytes of a plaintext HTTP response, read as a TLS record header, declare an absurd record length. Translation: your server is speaking plain HTTP on port 443. Chrome shows the same misconfiguration as ERR_SSL_PROTOCOL_ERROR; Firefox's version is more specific and more useful.
This one is purely a server problem. No amount of cache clearing on the visitor's side fixes it.
The Usual Suspects#
- nginx:
listen 443;withoutssl. The listener accepts connections on 443 but treats them as HTTP. The single missing word is the whole bug. - Apache: vhost on 443 without
SSLEngine on. Same disease, different config file. - A proxy or port-forward sending 443 to the app's HTTP port. Docker port mappings (
443:8080to a plaintext container port), kubectl port-forwards, and load balancer target groups all reproduce it. - The app server bound to 443 directly. Node, Flask, or Spring serving HTTP on 443 because someone set
PORT=443without enabling TLS in the app. - Wrong URL scheme during development.
https://localhost:3000against a plain HTTP dev server triggers it locally; that one is the URL, not the server.
Confirm It in One Command#
curl -v https://example.com# curl: (35) error:0A00010B:SSL routines::wrong version number
curl -v https://example.com# curl: (35) error:0A00010B:SSL routines::wrong version number
wrong version number from curl on port 443 is the same plaintext-where-TLS-expected signature. To see the smoking gun directly, fetch the port as HTTP:
curl -s http://example.com:443 | head -3
curl -s http://example.com:443 | head -3
If that returns HTML, port 443 is serving plaintext, case closed.
The Fix#
nginx, the canonical version:
server {listen 443 ssl;listen [::]:443 ssl;server_name example.com;ssl_certificate /etc/ssl/fullchain.pem;ssl_certificate_key /etc/ssl/privkey.pem;}
server {listen 443 ssl;listen [::]:443 ssl;server_name example.com;ssl_certificate /etc/ssl/fullchain.pem;ssl_certificate_key /etc/ssl/privkey.pem;}
Apache:
<VirtualHost *:443>ServerName example.comSSLEngine onSSLCertificateFile /etc/ssl/cert.pemSSLCertificateKeyFile /etc/ssl/privkey.pem</VirtualHost>
<VirtualHost *:443>ServerName example.comSSLEngine onSSLCertificateFile /etc/ssl/cert.pemSSLCertificateKeyFile /etc/ssl/privkey.pem</VirtualHost>
For proxy and container setups, the rule is: terminate TLS somewhere deliberate, and make sure whatever listens on 443 is the thing doing the terminating. Map 443 to the proxy's TLS port, not the app's HTTP port.
Limitations to Know#
- The error proves plaintext, not where it comes from. In a chain of proxy, container, and app, any hop can be the one answering 443 with HTTP; check them in order.
- Intermittent versions exist. If only some requests fail, suspect a load balancer pool where one backend has TLS misconfigured and the others are fine.
Conclusion#
SSL_ERROR_RX_RECORD_TOO_LONG has one meaning: port 443 is serving HTTP instead of TLS, usually one missing ssl keyword or one wrong port mapping away from correct. The curl check confirms it in seconds and the fix is a single config line.
It also tends to appear at the worst time, right after an infra change, when nobody is manually re-checking the site. A scheduled ObserveOne HTTPS check fails the moment the listener config goes wrong, so the bad deploy gets caught while it is still one commit deep.