I’ve run into a problem on both of my web apps where the browser seems to hang on requests. My business partner wasn’t seeing this from his network. I also was seeing different results depending on what network my device was connected to.
Debugging using chrome network logging showed a consistent 10 second delay on those requests. I was able to narrow this down to something that only happens when I’m connecting to the app via IPv6. The log showed that the connection stops passing traffic somewhere between 30 and 45 seconds idle. No FIN, RST or GOAWAY ever arrives. The TCP keepalive probe goes unanswered, and the socket eventually fails with ETIMEDOUT.
I had claude write some test scripts to help diagnose. It recreates the problem using plain nodejs http/2 connections. Here’s an example of what I’m seeing:
% node idle-test.mjs 30 40 50 90 https://[myapp].fly.dev/healthz
Target https://[myapp].fly.dev/healthz -> IPv4 66.241.XXX.XXX, IPv6 [2a09:8280:1::xxx:xxxx:0]
Testing IPv4 + IPv6 with idle times 30, 40, 50, 90s (takes about 115s)...
IPv6 idle 30s | first: 200 in 604ms | after idle: ping ok 25ms; request 200 in 31ms
IPv4 idle 30s | first: 200 in 607ms | after idle: ping ok 31ms; request 200 in 29ms
IPv4 idle 40s | first: 200 in 606ms | after idle: ping ok 30ms; request 200 in 32ms
IPv6 idle 40s | first: 200 in 605ms | after idle: ping ok 33ms; request 200 in 35ms
IPv4 idle 50s | first: 200 in 606ms | after idle: ping ok 32ms; request 200 in 31ms
IPv6 idle 50s | first: 200 in 601ms | after idle: NO PING REPLY in 10s; request error ETIMEDOUT after 12601ms | error ETIMEDOUT at 73.2s
IPv6 idle 90s | first: 200 in 605ms | after idle: n/a (connection already closed); request n/a (connection already closed) | error ETIMEDOUT at 73.9s
IPv4 idle 90s | first: 200 in 606ms | after idle: ping ok 23ms; request 200 in 32ms
I should note incase it’s relevant that my ISP is Starlink. I haven’t been able to test on a real IPv6 connection on another provider yet, and things that go through a NAT before my router work fine. Also using an IPv4 mapped v6 address [::: ffff: …] works ok.