# SSL_ERROR_RX_RECORD_TOO_LONG: why Firefox says it and how to fix it

> SSL_ERROR_RX_RECORD_TOO_LONG is Firefox's error when a server answers an HTTPS request in plain HTTP. Here is the visitor fix, the bytes that make Firefox say "record too long", one curl test that proves it, and the fix for nginx, Apache, app servers and Cloudflare.

Updated 2026-11-11 · Security · HTML version: https://getreport.app/guides/ssl-error-rx-record-too-long

SSL_ERROR_RX_RECORD_TOO_LONG means Firefox asked a server for an encrypted HTTPS connection and got plain, unencrypted HTTP back, so the first bytes of the reply looked like a TLS record far longer than TLS allows. Firefox shows it on a "Secure Connection Failed" page with the line "SSL received a record that exceeded the maximum permissible length". If you are a visitor, a proxy, VPN or antivirus that scans HTTPS can cause it; if you own the site, the usual cause is port 443 serving HTTP without TLS, which one `curl` command proves. It is one of the codes in [SSL errors, browser by browser](https://getreport.app/guides/ssl-errors-by-browser), and Chrome shows the same server state as [ERR_SSL_PROTOCOL_ERROR](https://getreport.app/guides/err-ssl-protocol-error).

![Firefox's hello goes to port 443, the server replies in HTTP, and the first five bytes of the HTML reply read as a TLS header give a length of 20,291 bytes, above the 18,432-byte TLS 1.2 limit and the 16,640-byte TLS 1.3 limit](https://getreport.app/guides/img/ssl-error-rx-record-too-long/diagram.svg "The error name is literal: an HTML or HTTP reply decodes to a record length TLS forbids.")

## Quick answer

- **Visitor**: open the site in a private window and on mobile data. If it works there, check Firefox's proxy setting, pause your VPN and turn off HTTPS scanning in your antivirus. If it fails everywhere, only the site owner can fix it.
- **Site owner**: run `curl -v http://yourdomain:443/` from your computer. If your normal page or a redirect comes back over plain HTTP, port 443 has no TLS. That is the whole diagnosis.
- **nginx**: the listen line needs `ssl`: `listen 443 ssl;`, not `listen 443;`, plus a certificate and key.
- **Apache**: the `<VirtualHost *:443>` block needs `SSLEngine on` and certificate lines, and `mod_ssl` must be loaded.
- **Not the certificate**: an expired or outdated certificate gives a different error. Firefox never got as far as a certificate here.
- After the fix, run the [SSL checker](https://getreport.app/tools/ssl-check) to confirm the handshake, the certificate and the redirect.

## What "record too long" actually means

Everything sent over TLS travels in records. Each record starts with a 5-byte header: 1 byte for the content type (handshake, alert, application data), 2 bytes for the protocol version and 2 bytes for the length of what follows. A browser reads those 5 bytes first and then waits for that many bytes of data.

TLS caps that length. RFC 5246 (TLS 1.2), section 6.2.3, says an encrypted record must not exceed 2^14 + 2048 = 18,432 bytes. RFC 8446 (TLS 1.3), section 5.2, lowers it to 2^14 + 256 = 16,640 bytes. A header that announces more than that is invalid, and Firefox rejects it with this error.

Now send that browser an HTML page or an HTTP error instead. Firefox still reads the first 5 bytes as a record header, because that is all it expects on an `https://` connection. Bytes 4 and 5 are now two text characters, and read as one number they come out large: for the two cases below, above 20,000 bytes and over the limit. The record Firefox thinks it received really is too long.

### The bytes from our lab

We pointed a real TLS client at a port that serves plain HTTP (port 8446 in our lab, `python3 -m http.server 8446` serving the example-shop.hr fixture) and decoded the reply as a TLS record header with a short script, `raw.py`. Run on the lab machine:

```text
$ python3 raw.py 8446
first 5 bytes: 3c 21 44 4f 43 b'<!DOC'
as a TLS record: type=60 version=0x2144 length=20291 bytes
first line: <!DOCTYPE HTML>
```

The healthy port in the same lab, 8443, replied `16 03 03 00 7a`: type 22 (handshake), version 0x0303 (the TLS 1.2 value), length 122 bytes. Here is the arithmetic side by side:

| Reply on port 443 | First 5 bytes | Type | Version | Length read | Under the limit? |
| --- | --- | --- | --- | --- | --- |
| Healthy TLS server (lab, 8443) | `16 03 03 00 7a` | 22, handshake | 0x0303 | 122 bytes | Yes |
| HTML page (lab, 8446) | `3c 21 44 4f 43` (`<!DOC`) | 60, not a TLS type | 0x2144 | 0x4F43 = 20,291 bytes | No: above 18,432 and 16,640 |
| nginx or Apache status line (computed) | `48 54 54 50 2f` (`HTTP/`) | 72, not a TLS type | 0x5454 | 0x502F = 20,527 bytes | No: above 18,432 and 16,640 |

The last row is computed, not captured: nginx and Apache were not in our lab. A web server that receives a binary TLS hello on a plain HTTP port usually answers with `HTTP/1.1 400 Bad Request`, and the five characters `HTTP/` decode to the length shown.

### The same server state in other clients

The same broken port, in other clients:

| Client | What it shows for plain HTTP on the TLS port | Source |
| --- | --- | --- |
| Firefox | Secure Connection Failed, `SSL_ERROR_RX_RECORD_TOO_LONG` | Mozilla's error text; Firefox was not run in our lab |
| Chrome, Edge (Chromium) | This site can't provide a secure connection, `ERR_SSL_PROTOCOL_ERROR` | Our lab: `net::ERR_SSL_PROTOCOL_ERROR` on port 8446 |
| curl | `curl: (35) ... wrong version number` | Our lab, port 8446 |
| OpenSSL `s_client` | `wrong version number`, no peer certificate | Our lab, port 8446 |

curl and OpenSSL complain about the version bytes (0x2144 is no TLS version), Chromium reports a generic protocol error, and Firefox's TLS library names the length. They are three readings of one header.

## If you're a visitor: the quick fix

If the site fails on every device and network, the server is broken and only its owner can fix it. If it fails only on your computer, something between Firefox and the internet is changing the traffic. Mozilla's help page for secure connection errors names security software, proxies and VPNs as common causes. Try these in order:

1. **Open the site in a private window, then on your phone over mobile data.** If it works on the phone, the problem is on your computer or your network.
2. **Check Firefox's proxy setting.** Settings, General, Network Settings: choose "No proxy" or "Use system proxy settings" unless your workplace needs a specific proxy. A proxy entered in the wrong field (an HTTP-only proxy used for HTTPS) can hand Firefox plain HTTP.
3. **Pause your VPN and turn off HTTPS scanning in your antivirus.** Products that inspect encrypted traffic sit in the middle of the connection. If the site loads afterwards, update the product or add an exception for the site.
4. **Try Firefox's Troubleshoot Mode** (Help, Troubleshoot Mode) to rule out an add-on.
5. **Try another network.** Hotel, school and office networks sometimes intercept web traffic until you sign in.

Don't look for a setting that lowers Firefox's security to get past this. There is no certificate to accept: the server never spoke TLS.

## If you own the site: prove it in one command

Run this on your own computer (macOS, Linux, or Windows 10 and later, which include curl). Note the `http://` with port `:443`: you are asking port 443 for plain HTTP on purpose.

```bash
curl -v http://example.com:443/
```

- **Your page or a redirect comes back** (`HTTP/1.1 200 OK`, `301 Moved Permanently`): port 443 speaks plain HTTP. This is the cause of SSL_ERROR_RX_RECORD_TOO_LONG.
- **`400 Bad Request` with "The plain HTTP request was sent to HTTPS port"**: this is nginx saying port 443 *is* set up for TLS and you sent it HTTP. Port 443 is healthy; the problem is elsewhere.
- **`Empty reply from server` or the connection is reset**: the port expected TLS and dropped your plain request. 443 is probably fine; test from the visitor's network instead.
- **`Connection refused`**: nothing listens on 443. Browsers show [ERR_CONNECTION_REFUSED](https://getreport.app/guides/err-connection-refused) for that, not this error.

This is what a plain HTTP answer looks like, from our lab's port 8446 (same machine, same day, with `--resolve` mapping the fixture name to 127.0.0.1; output trimmed):

```text
$ curl -sv -o /dev/null --resolve example-shop.hr:8446:127.0.0.1 http://example-shop.hr:8446/
> GET / HTTP/1.1
> Host: example-shop.hr:8446
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.11.17
< Content-type: text/html
```

The same port asked for HTTPS, as a browser does:

```text
curl: (35) OpenSSL/3.0.13: error:0A00010B:SSL routines::wrong version number
```

Then run a full check from outside your network, so a local cache or VPN does not hide the result:

> **Free tool:** [SSL checker: free SSL certificate and TLS test](https://getreport.app/tools/ssl-check): Free SSL checker: test certificate expiry and chain, TLS versions, HTTPS redirects, HSTS and mixed content on any site, with the fix for each. No sign-up.

The SSL checker connects like a strict client. While port 443 speaks plain HTTP, it cannot load the page over `https://` at all. Once TLS works, these findings should pass:

> **Check: The site is served over HTTPS.** Browsers label HTTP pages "Not secure" and Google uses HTTPS as a small ranking signal. Anyone on the network can read or alter what visitors see.
>
> 1. Get a certificate (Let's Encrypt is free) and enable HTTPS on your host.
> 2. Redirect every http:// URL to https:// with a 301.

> **Check: http:// redirects to https://.** Visitors who type your address without https, or follow an old link, land on the unencrypted page. Every one of those visits can be read or altered on the network.
>
> 1. Redirect every http:// URL to the same path on https:// with a 301.
> 2. nginx: return 301 https://$host$request_uri; Apache: RewriteRule in .htaccess; most hosts and Cloudflare have a "Always use HTTPS" switch.

## The causes, most likely first

| Cause | Sign | Fix |
| --- | --- | --- |
| nginx `listen 443;` without `ssl` | `curl -v http://host:443/` gets a normal page | Add `ssl` to the listen line, plus certificate and key |
| Apache `*:443` virtual host without `SSLEngine on`, or `mod_ssl` not loaded | Same curl result | Enable `mod_ssl`, add `SSLEngine on` and certificate lines |
| An app or container speaking HTTP on 443 | Same, with your app's `Server` header | Put the reverse proxy back in front, or enable TLS in the app |
| `https://` on a plain HTTP port, such as `:80` or `:8080` | The URL has a port number | Remove the port, or serve TLS on it |
| Proxy, VPN or antivirus on the visitor's side | Works on another device or network | The visitor fixes in the list above |

### nginx: `listen 443;` without `ssl`

This is the classic cause, and the [nginx guide to HTTPS servers](https://nginx.org/en/docs/http/configuring_https_servers.html) explains why: the `ssl` parameter on the `listen` line is what turns TLS on for that socket. Without it, nginx listens on 443 and serves plain HTTP. The snippet below is from the nginx docs, not run in our lab. It goes in your site's file, often `/etc/nginx/sites-available/example.com` or `/etc/nginx/conf.d/example.com.conf`:

```nginx
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
}
```

Check both the IPv4 and IPv6 lines: a missing `ssl` on `[::]:443` breaks only visitors on IPv6, which looks random. Search every file, because another site's block can define the same port: `grep -rn "listen.*443" /etc/nginx/`. Test and reload with `sudo nginx -t && sudo systemctl reload nginx`. If you have no certificate yet, [get a free one from Let's Encrypt](https://getreport.app/guides/free-ssl-certificate-lets-encrypt) first.

### Apache: a `*:443` virtual host without TLS

Apache serves plain HTTP on any port it listens on unless `mod_ssl` is loaded and the virtual host turns it on. The usual slip is a `<VirtualHost *:443>` copied from the port 80 block. The lines below are from the Apache mod_ssl docs, not run in our lab; they go in the site's file, for example `/etc/apache2/sites-available/example.com-ssl.conf` on Debian and Ubuntu:

```apache
<VirtualHost *:443>
    ServerName example.com
    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>
```

On Debian and Ubuntu, enable the module and the site with `sudo a2enmod ssl` and `sudo a2ensite example.com-ssl`, then `sudo apachectl configtest && sudo systemctl reload apache2`. On RHEL-family systems, install the `mod_ssl` package.

### An app server or container on port 443

Node.js, Python and Go apps, and Docker containers, usually speak plain HTTP and expect a reverse proxy (nginx, Caddy, a load balancer) to handle TLS in front of them. The error appears when the app ends up on 443 directly: a `docker run -p 443:3000` mapping, an app told to listen on 443, or a proxy removed during a migration. Either put the proxy back so it terminates TLS on 443 and forwards to the app's HTTP port, or start the app with TLS (in Node, `https.createServer` with a certificate, not `http.createServer`).

### `https://` on a port that serves HTTP

A link or redirect such as `https://example.com:80/` or `https://example.com:8080/` sends a TLS hello to a plain HTTP port, and the browser gets exactly the reply in our lab. Look for the port number in the address bar, in the site's base URL setting (WordPress "Site Address", an app's `BASE_URL`) and in redirect rules. Remove the port, or serve TLS on it.

### Cloudflare: where the error can and can't come from

With the orange cloud on (proxied), the visitor's TLS ends at Cloudflare, so a broken origin does not show this error. If your origin's port 443 speaks plain HTTP, the SSL mode decides what visitors see:

| SSL/TLS mode | How Cloudflare reaches your origin | Origin 443 speaking plain HTTP gives |
| --- | --- | --- |
| Flexible | Plain HTTP on port 80 | No error (port 443 is never used), but the origin link is unencrypted |
| Full | HTTPS on port 443 | Error 525 for visitors |
| Full (strict) | HTTPS on port 443, certificate validated | Error 525 for visitors |

Fix the origin as above and set Full (strict); [Cloudflare errors 525 and 526](https://getreport.app/guides/cloudflare-ssl-errors-525-526) walks through it. Two cases do show SSL_ERROR_RX_RECORD_TOO_LONG with Cloudflare. First, a grey-cloud (DNS only) record: visitors connect straight to your origin, so its port 443 must serve TLS itself. Second, `https://` on one of Cloudflare's HTTP ports: Cloudflare's network ports page lists 80, 8080, 8880, 2052, 2082, 2086 and 2095 as HTTP and 443, 2053, 2083, 2087, 2096 and 8443 as HTTPS. That second case follows from the port list; we did not test it.

### Managed and WordPress hosts

On managed hosting you can't edit the server block, but the cause is the same: the host's port 443 serves your domain without TLS, often because a newly added domain or subdomain never got its certificate. In cPanel, Plesk or your host's dashboard, look for the SSL/TLS or "HTTPS" setting for that exact domain and turn it on. Where that option is missing, send support the `curl -v http://yourdomain:443/` output: it shows them the problem in one line. Panel names vary by host.

## After the fix

1. `curl -v http://example.com:443/` no longer returns a normal page: nginx answers "The plain HTTP request was sent to HTTPS port", or the connection closes.
2. `openssl s_client -connect example.com:443 -servername example.com </dev/null` (on your computer) shows your certificate and `Verify return code: 0 (ok)`.
3. Repeat for every name visitors use, such as `www` and subdomains, over IPv4 and IPv6.
4. Open the site in a Firefox private window and on a phone over mobile data.
5. Run the [SSL checker](https://getreport.app/tools/ssl-check) again. Check that only TLS 1.2 and 1.3 are accepted, and add HSTS only once HTTPS works on every name:

> **Check: The server only accepts TLS 1.2 or newer.** TLS 1.0 and 1.1 have known weaknesses and every major browser dropped them in 2020. A server that still accepts them lets an attacker downgrade a connection to one they can break.
>
> 1. Set the minimum protocol to TLS 1.2 (nginx: ssl_protocols TLSv1.2 TLSv1.3; Apache: SSLProtocol -all +TLSv1.2 +TLSv1.3).
> 2. Behind Cloudflare or another CDN, set "Minimum TLS Version" to 1.2 in its dashboard.

> **Check: Strict-Transport-Security header is set.** HSTS tells browsers to always use HTTPS for your site, so after the first visit a typed address or an old http:// link never starts on an unencrypted connection. Only the preload list covers the very first visit.
>
> 1. Send the header: Strict-Transport-Security: max-age=31536000; includeSubDomains.
> 2. Start with a shorter max-age (e.g. 86400) if you are not sure every subdomain supports HTTPS.

If Firefox now shows a different code, the next problem is in line behind this one. Our sibling guide on [ERR_SSL_VERSION_OR_CIPHER_MISMATCH](https://getreport.app/guides/err-ssl-version-or-cipher-mismatch) covers servers that speak TLS but only old versions, and [the SSL handshake explained](https://getreport.app/guides/ssl-handshake) shows the full exchange.

## How we tested

The getReport team ran these tests on 30 October 2026 on a Linux machine with OpenSSL 3.0.13, curl 8.5.0, Python 3.11 and headless Chromium 153.0.8010.12 (Playwright). The example-shop.hr fixture homepage was served by `openssl s_server` on healthy ports and by `python3 -m http.server 8446` on the plain-HTTP port. A Python script sent a real TLS hello and decoded the first 5 bytes of the reply; curl, OpenSSL and Chromium connected to the same port.

Not tested: Firefox itself (its message comes from Mozilla's error text, and the "record too long" verdict follows from the bytes and the RFC limits), Safari, real nginx, Apache or IIS (the config snippets are from their docs, and the `HTTP/` length of 20,527 bytes is computed), Cloudflare, antivirus HTTPS scanning, VPNs and proxies.

## Questions people ask

### What does SSL_ERROR_RX_RECORD_TOO_LONG mean in Firefox?

It means the server replied to Firefox's HTTPS request with plain HTTP. Firefox reads the first 5 bytes of every reply as a TLS record header, and text such as `<!DOC` or `HTTP/` decodes to a length above 20,000 bytes, while TLS allows at most 18,432 (TLS 1.2) or 16,640 (TLS 1.3). Almost always the site's port 443 has no TLS configured, so the owner has to fix the server.

### Is SSL_ERROR_RX_RECORD_TOO_LONG caused by an outdated certificate?

No. An expired, outdated or wrong-name certificate produces a certificate warning, such as "Warning: Potential Security Risk Ahead", not this error. SSL_ERROR_RX_RECORD_TOO_LONG happens before any certificate is sent, because the server never started speaking TLS. Renewing the certificate alone will not help; the server has to serve TLS on port 443 first, and then its certificate matters.

### Why does Firefox show this error when Chrome loads the site fine?

Because something affects Firefox only, usually its own proxy setting. Firefox can use a proxy separate from your system's, and a wrong one returns plain HTTP. If the server's port 443 were really broken, Chrome would fail too, with ERR_SSL_PROTOCOL_ERROR. Check Settings, General, Network Settings, try Troubleshoot Mode to rule out add-ons, and check antivirus software that treats each browser differently.

### Can antivirus HTTPS scanning cause SSL_ERROR_RX_RECORD_TOO_LONG?

Yes, Mozilla lists security software that inspects encrypted traffic among the causes of secure connection errors. Such software sits between Firefox and the site, and when it fails it can hand Firefox an unencrypted reply. Turn off the HTTPS or "encrypted connection" scanning option briefly and reload. If the site works, update the product or exclude the site, then turn protection back on.

### Why do I get SSL_ERROR_RX_RECORD_TOO_LONG on localhost?

Because you opened `https://localhost` and your development server only speaks HTTP. Open `http://localhost:3000` (or your port) instead, or start the dev server with HTTPS enabled. If Firefox keeps upgrading to HTTPS, check for a redirect in your app, an earlier HSTS header on that name, or Firefox's HTTPS-Only Mode, which you can switch off for one site.

### Does turning on TLS 1.3 fix SSL_ERROR_RX_RECORD_TOO_LONG?

No. The server isn't using TLS at all on that port, so no TLS version setting is involved. Changing `ssl_protocols` or similar lines does nothing while the `listen` line lacks `ssl` or `SSLEngine on` is missing. Turn TLS on for port 443 first; once the handshake works, allow TLS 1.2 and 1.3 and refuse older versions.

## Sources

- [RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3, section 5.2](https://www.rfc-editor.org/rfc/rfc8446) (accessed October 2026)
- [RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2, section 6.2.3](https://www.rfc-editor.org/rfc/rfc5246) (accessed October 2026)
- [Mozilla: Secure connection and security warning error pages in Firefox](https://support.mozilla.org/en-US/kb/secure-connection-failed-firefox-did-not-connect) (accessed October 2026)
- [Chromium net error list](https://source.chromium.org/chromium/chromium/src/+/main:net/base/net_error_list.h) (accessed October 2026)
- [nginx: Configuring HTTPS servers](https://nginx.org/en/docs/http/configuring_https_servers.html) (accessed October 2026)
- [Apache HTTP Server: mod_ssl](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html) (accessed October 2026)
- [Cloudflare: Network ports](https://developers.cloudflare.com/fundamentals/reference/network-ports/) (accessed October 2026)
- [Cloudflare: SSL troubleshooting](https://developers.cloudflare.com/ssl/troubleshooting/) (accessed October 2026)
