Happy Eyeballs Test BETA
Test your browser's RFC 8305 implementation and detect broken IPv6 connectivity
About Happy Eyeballs
Happy Eyeballs (RFC 8305) is an algorithm that modern browsers use to reduce connection delays on dual-stack networks. When a website has both IPv4 and IPv6 addresses:
- IPv6 is tried first (preferred protocol)
- IPv4 races after ~250ms if IPv6 hasn't connected
- Fastest connection wins - whichever completes first is used
- Broken IPv6 fails fast - prevents long timeouts
⚠ Broken IPv6: If your IPv6 connectivity is broken (has an IPv6 address but can't actually connect), a browser without Happy Eyeballs will hang for 30+ seconds instead of falling back to IPv4 quickly.
ℹ️ Testing Limitations: JavaScript cannot directly observe which protocol (IPv4 or IPv6) the browser uses - this happens deep in the browser's network stack. These tests detect symptoms of broken IPv6 and Happy Eyeballs issues (slow connections, timeouts) rather than directly testing the RFC 8305 algorithm itself.
Test Status
1 Basic Connectivity Detection Active
Detects your IPv4 and IPv6 connectivity status using multiple detection services.
2 Connection Timing Analysis Active
Measures connection times to well-known dual-stack websites (Google, Cloudflare, Facebook, Netflix, Codeberg). Slow connections (>3 seconds) may indicate broken IPv6 where your browser waits for IPv6 timeout before falling back to IPv4. This tests the symptom of Happy Eyeballs issues, not the mechanism itself (which happens in the browser's network stack).
3 Dual-Stack Race Condition Test Active
Tests if your browser properly implements Happy Eyeballs racing behavior by loading resources from dual-stack and IPv4-only hosts simultaneously.
4 Passive Broken IPv6 Detection Passive
This test uses a technique similar to brokenv6.krautrouting.net. If you can see this result, your browser successfully fell back to IPv4 when IPv6 failed. This test is a bit of a guess with JavaScript due to the lack of ability to see into the protocol stack. See documentation below for more detailed information.
You are viewing this page, which means your browser either:
- Has working IPv4 connectivity, OR
- Successfully implemented Happy Eyeballs fallback from broken/slow IPv6 to IPv4
If this page loaded quickly (< 2 seconds), your Happy Eyeballs implementation is working correctly.
🔬 How These Tests Work (Technical Details)
Test 1: Connectivity Detection
What it does:
- Queries
api.ipify.org(IPv4-only service) to detect your IPv4 address - Queries
api64.ipify.org(IPv6-capable service) to detect your IPv6 address - Compares results to determine your connection type
How it works:
- If both APIs return different addresses → You have dual-stack (IPv4 + IPv6)
- If only IPv4 API responds → You have IPv4-only connectivity
- If only IPv6 API responds → You have IPv6-only connectivity
If you're on an IPv6-only network with NAT64/DNS64 - which, if you're single stacked IPv6 you probably are, it's the most common deployment of IPv6-only, by far - the test will [in]correctly report dual-stack connectivity. Here's why:
- What NAT64 does: Translates IPv6 packets to IPv4 on your carrier's network, allowing IPv6-only devices to reach IPv4-only services
- DNS64: Synthesizes AAAA records (IPv6 addresses) for IPv4-only domains by embedding the IPv4 address in an IPv6 prefix (like
64:ff9b::192.0.2.1) - Why the test fails: When you query
api.ipify.org, DNS64 returns a synthetic IPv6 address. Your device sends IPv6 packets to the NAT64 gateway, which translates them to IPv4. The service sees the NAT64 gateway's IPv4 address and returns it. - Result: Your device thinks it has both IPv4 and IPv6, but you actually have only IPv6 + NAT64 translation
Detection: If your detected "IPv4 address" and "IPv6 address" from api64.ipify.org represent the same endpoint (the NAT64 gateway's public IPv4), you likely have NAT64. True dual-stack would potentially show different upstream addresses, a NAT overload address, or your native public IPv4 address.
Test 2: Connection Timing Analysis
What it does:
- Loads
favicon.icofrom 5 well-known dual-stack websites (Google, Cloudflare, Facebook, Netflix, Codeberg) - Measures total time from request start to resource load using
performance.now() - Analyzes timing patterns to detect symptoms of broken IPv6
How it works:
- DNS Resolution: Browser queries for both A (IPv4) and AAAA (IPv6) records
- Connection Attempt: Browser tries to establish connection(s) based on Happy Eyeballs:
- IPv6 connection attempt starts first
- If IPv6 doesn't connect within ~250ms, IPv4 attempt starts (racing)
- First successful connection wins
- Resource Load: Once connected, browser requests
/favicon.ico - Timing Measurement: Total elapsed time is recorded
What the timing tells us:
- <1 second: Good connectivity, Happy Eyeballs working correctly (or not needed)
- 1-3 seconds: Moderate delay, possible network congestion or slow path selection
- >3 seconds: Likely indicates broken IPv6:
- IPv6 SYN packets are being dropped (firewall/routing issue)
- Browser waits for IPv6 connection timeout (~1-3 seconds per RFC)
- Only then falls back to IPv4, which succeeds
- Total time = IPv6 timeout + IPv4 connection time
JavaScript has no API to:
- Force IPv4-only or IPv6-only connections (browser controls this)
- Query which protocol was used for a connection
- Observe the 250ms race timer or connection attempts
- Access socket-level details (TCP SYN/ACK, connection state)
Instead, we infer behavior from observable symptoms: slow connections indicate the browser waited for a broken IPv6 timeout before trying IPv4.
Test 3: Dual-Stack Race Test
What it does:
- Loads
favicon.icofrom the same dual-stack hosts - Uses
Promise.race()to simulate racing behavior - Measures which "contestant" completes first
How it works:
- Creates two identical image load requests to the same URL (with different cache-busting parameters)
Promise.race()resolves when the first request completes- This mimics Happy Eyeballs at the application layer (though the browser has already done its own Happy Eyeballs internally)
Interpretation:
- Fast completion (<1s): At least one path (IPv4 or IPv6) is working well
- Consistent slow results: Both protocols may be experiencing issues, or broken IPv6 is affecting all attempts
- Mixed results: Network conditions vary, or different sites have different routing
Test 4: Passive Detection
What it does:
- Simply displays a success message if you can view the page
- Similar to brokenv6.krautrouting.net
How it works:
- If you have broken IPv6 (IPv6 address assigned, but no actual IPv6 routing), attempting to load this page would:
- Get DNS records (both A and AAAA)
- Try IPv6 first → hangs/times out
- Eventually fall back to IPv4 (if Happy Eyeballs works)
- Page loads, but slowly
- If the page loaded quickly, either:
- You don't have broken IPv6, OR
- Happy Eyeballs successfully fell back to IPv4 within ~250ms
Summary: What We Can and Can't Test
| Capability | Can Test? | Method |
|---|---|---|
| IPv4/IPv6 connectivity | ✅ Yes | Query protocol-specific APIs |
| Connection timing | ✅ Yes | performance.now() around requests |
| Symptoms of broken IPv6 | ✅ Yes | Slow connections indicate timeout delays |
| Which protocol was used | ❌ No | Browser network stack only |
| RFC 8305 250ms race delay | ❌ No | Internal browser timing only |
| NAT64 vs true dual-stack | ⚠️ Partial | Can detect, but requires inference |
| Browser Happy Eyeballs support | ⚠️ Indirect | Infer from connection success patterns |