A detailed root cause analysis and fix for ERR_CONNECTION_RESET errors in Google Chrome caused by ISP deep packet inspection (DPI) middleboxes that cannot handle large TLS 1.3 ClientHello messages and TCP segments generated by Chrome's post-quantum cryptography (ML-KEM/Kyber) implementation.
- Chrome shows
ERR_CONNECTION_RESETon certain websites (especially those behind Cloudflare CDN) curlto the same URLs works perfectly fine- Other browsers (Firefox, Safari) may also work
- The issue is network-specific — the same Chrome instance works on a different network (e.g., mobile hotspot)
- Incognito mode may temporarily work but fails after a few page loads as cookies accumulate
- Google Chrome 124+ (post-quantum key exchange enabled by default since Chrome 124, ML-KEM since Chrome 131)
- ISPs with upstream DPI middleboxes that inspect or filter TCP segments during TLS sessions
- Known affected ISPs: CenturyLink/Lumen, and potentially others using similar DPI equipment
- Network topology: OPNsense/pfSense firewall behind an ISP modem in bridge mode — the firewall passes traffic cleanly, but the ISP's upstream infrastructure does not
Starting with Chrome 124, Google enabled hybrid post-quantum key exchange using X25519Kyber768. In Chrome 131+, this was switched to X25519MLKEM768 (NIST FIPS 203).
This adds approximately 1,100 bytes of post-quantum key material to the TLS ClientHello key_share extension, inflating the ClientHello from ~300 bytes to ~1,500+ bytes. At this size, the ClientHello is fragmented across multiple TCP segments.
Many ISP middleboxes performing deep packet inspection (DPI) expect the ClientHello to fit within a single TCP segment. When it spans multiple segments, these middleboxes either:
- Send a TCP RST (connection reset)
- Silently drop the connection
- Fail to reassemble the handshake correctly
Even after reducing the ClientHello size (by disabling ML-KEM), Chrome's encrypted HTTP requests can also trigger the same middlebox behavior. Chrome sends significantly more data than curl:
- 8+ cookies per request (tracking cookies, consent cookies, session cookies)
- Dozens of HTTP headers (sec-ch-ua, sec-fetch-*, etc.)
- Total encrypted payload: 1,400–1,500+ bytes
When these encrypted payloads hit the TCP Maximum Segment Size (MSS) boundary (~1,460 bytes with standard 1,500 MTU), the ISP middlebox RSTs the connection.
curl works because:
- Its ClientHello is ~300 bytes (no post-quantum extensions)
- Its HTTP requests are minimal (no cookies, fewer headers)
- All segments stay well under the middlebox's threshold
If your firewall is running in a typical routing configuration behind a bridged ISP modem:
- Firewall logs show all connections to the affected IPs as passed
- IDS/IPS (Suricata) is either disabled or not triggering on these connections
- No TLS inspection or transparent proxy is configured
The RST originates from upstream ISP infrastructure, not the local firewall.
Capture a net-export log from chrome://net-export/ and analyze the TLS handshake:
SSL_CONNECT_JOB_SSL_CONNECT: {"ech_config_list": "", "ech_enabled": true}
SOCKET_BYTES_SENT: {"byte_count": 1726} ← Oversized ClientHello
SOCKET_READ_ERROR: {"net_error": -101, "os_error": 54} ← ECONNRESET
SSL_HANDSHAKE_ERROR: {"error_lib": 35, "error_reason": 101,
"file": "net/socket/socket_bio_adapter.cc", "line": 157,
"net_error": -101, "ssl_error": 1}
Key indicators:
net_error: -101=ERR_CONNECTION_RESETos_error: 54= macOSECONNRESET- TCP connects successfully, RST occurs after ClientHello is sent
ech_enabled: trueand largebyte_countinSOCKET_BYTES_SENT
# Capture Chrome's TLS handshake
sudo tcpdump -i en0 -c 20 'net <server_ip_range> and tcp port 443' -n
# Compare with curl's ClientHello size
curl -v --resolve www.example.com:443:<server_ip> https://www.example.com 2>&1 | grep -A2 "Client hello"Chrome's ClientHello: ~1,536 bytes (split across 1,448 + 88 byte segments)
curl's ClientHello: ~321 bytes (single segment)
# Test on mobile hotspot (bypasses ISP middlebox)
# If Chrome works on hotspot → ISP middlebox is the culprit
# Temporarily reduce MTU to force smaller segments
sudo ifconfig en0 mtu 1400
# If Chrome works with lower MTU → confirms large segment issue
sudo ifconfig en0 mtu 1500 # revert after testingIn the tcpdump hex output, look for the ML-KEM key share codepoint:
0x11EC → ML-KEM768+X25519 (Chrome 131+)
0x6399 → X25519Kyber768 (Chrome 124-130)
If present in the key_share extension, the ClientHello is inflated with post-quantum key material.
The correct fix is MSS clamping on the WAN interface, which forces all TCP connections to negotiate a smaller Maximum Segment Size. This ensures no individual TCP segment exceeds the threshold that triggers the ISP middlebox.
- Navigate to Firewall → Settings → Normalization
- Click + Add
- Configure:
- Interface: WAN
- Direction: Any
- Protocol: TCP
- Max MSS:
1400 - Description:
MSS clamping - ISP middlebox large segment fix
- Save → Apply Changes
This works because:
- MSS clamping modifies the TCP SYN/SYN-ACK to advertise a smaller MSS
- All subsequent data segments respect this smaller size
- Both the TLS handshake and encrypted HTTP data stay within bounds
- No Chrome-side changes are required
- All browsers and applications benefit automatically
- Navigate to Firewall → Scrub
- Add a scrub rule:
- Interface: WAN
- Protocol: TCP
- Max MSS:
1400
- Save → Apply
These are temporary workarounds that only fix Chrome and don't address the underlying network issue:
macOS (configuration profile):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.google.Chrome</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.custom.chrome.tlsfix.settings</string>
<key>PayloadUUID</key>
<string>A1B2C3D4-E5F6-7890-ABCD-EF1234567890</string>
<key>PostQuantumKeyAgreementEnabled</key>
<false/>
<key>EncryptedClientHelloEnabled</key>
<false/>
</dict>
</array>
<key>PayloadDescription</key>
<string>Disables Chrome PQ key exchange and ECH</string>
<key>PayloadDisplayName</key>
<string>Chrome TLS Compatibility Fix</string>
<key>PayloadIdentifier</key>
<string>com.custom.chrome.tlsfix</string>
<key>PayloadOrganization</key>
<string>Local Admin</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>F1E2D3C4-B5A6-7890-FEDC-BA0987654321</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>Windows (Registry):
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome
PostQuantumKeyAgreementEnabled = 0 (DWORD)
EncryptedClientHelloEnabled = 0 (DWORD)
Linux:
sudo mkdir -p /etc/opt/chrome/policies/managed/
echo '{"PostQuantumKeyAgreementEnabled": false, "EncryptedClientHelloEnabled": false}' | \
sudo tee /etc/opt/chrome/policies/managed/disable-pqc.json
⚠️ Note: Google has stated thatPostQuantumKeyAgreementEnabledis a temporary policy. Chrome-side workarounds only reduce the ClientHello size — they don't fix the large encrypted HTTP request problem (cookies/headers), which can also trigger the middlebox RST. MSS clamping is the only complete fix.
These flags have been removed in recent Chrome versions:
chrome://flags/#encrypted-client-hello— removed around Chrome 130+chrome://flags/#post-quantum-key-agreement— removed in Chrome 138+chrome://flags/#enable-tls13-kyber— removed in Chrome 131+chrome://flags/#use-ml-kem— removed in Chrome 138+
| Field | curl (~321 bytes) | Chrome 144 (~1,536 bytes) |
|---|---|---|
| TLS version | TLS 1.3 | TLS 1.3 |
| Cipher suites | ~5 | ~15 |
| Key share | X25519 (32 bytes) | X25519MLKEM768 (~1,100 bytes) |
| ECH | Not present | GREASE ECH (~200 bytes) |
| Extensions | ~8 | ~20+ (including sec-ch-ua, ALPS) |
With standard MTU 1500 and MSS 1460:
Chrome ClientHello (1,536 bytes):
Segment 1: [TCP header][TLS record header][ClientHello bytes 1-1,448] ← 1,448 bytes
Segment 2: [TCP header][ClientHello bytes 1,449-1,536] ← 88 bytes
→ ISP middlebox sees fragmented ClientHello → RST
With MSS clamped to 1400:
Segment 1: [TCP header][TLS record header][ClientHello bytes 1-1,348] ← 1,348 bytes
Segment 2: [TCP header][ClientHello bytes 1,349-1,536] ← 188 bytes
→ Segments within middlebox tolerance → PASS
More importantly, MSS clamping also keeps all subsequent encrypted segments under the threshold, preventing RSTs on large HTTP requests with cookies.
- Google: A new path for Kyber on the web
- Chrome: Post-quantum cryptography breaks TLS connections
- Chrome switching to NIST-approved ML-KEM
- Cloudflare: PQC support documentation
- FortiGate: ERR_SSL_PROTOCOL_ERROR with ML-KEM
- tldr.fail — How large post-quantum ClientHello breaks connections
- Disabling ECH in Google Chrome
- Chrome Enterprise: PostQuantumKeyAgreementEnabled policy
- Chrome Enterprise: EncryptedClientHelloEnabled policy
If you've encountered this issue with a different ISP or network equipment, please open an issue with:
- Your ISP name
- The modem/gateway model (if known)
- Whether MSS clamping resolved the issue
- The MSS value that worked for you