Happy Eyeballs: Dual-Stack Connection Preference Explained

Most dual-stack clients don’t “pick IPv6 or IPv4” once and for all—they race them. The idea, nicknamed Happy Eyeballs, is simple: start with a bias toward IPv6, introduce small delays so you don’t overload networks, and keep whichever connection completes first. Done right, this hides broken paths and shaves visible wait time without flooding servers with duplicate attempts.
Two IETF documents define how this works in practice. The 2012 algorithm (often called v1) introduced fast fallback from IPv6 to IPv4 so users don’t stare at a spinner when IPv6 is down, and the 2017 update (v2) tightened the steps: send both AAAA and A queries, sort and interleave addresses, and stagger connection attempts with specific timers.
If you run an app, CDN, or proxy, a handful of knobs determine whether Happy Eyeballs helps you or hurts you. The big ones are the DNS resolution behavior (including a short “resolution delay” to give AAAA a chance), the connection attempt spacing, and how you order and reuse destination addresses. Getting those right keeps latency low while still preferring IPv6.
How Happy Eyeballs Decides Between IPv6 and IPv4
The decision starts at name resolution. A dual-stack client fires a AAAA query first, immediately followed by an A query. Resolution is asynchronous; whichever answer arrives first can trigger connection setup. If an A answer returns first, v2 recommends waiting a tiny resolution delay—recommended 50 ms—to give AAAA a chance, so IPv6 doesn’t lose purely due to reply ordering. Late AAAA answers can still be folded into the candidate list if a connection hasn’t been established yet.
Timers That Matter
Three timer concepts shape behavior. First, the resolution delay (50 ms recommended) prevents IPv4 from always winning when DNS reorders answers. Second, the connection attempt delay is the gap between starting one connect() and the next; the recommended default is 250 ms, and a nuanced stack times the next attempt to when the prior one would send its second TCP SYN based on the retransmission timer. Third, guardrails: the delay must not be less than 10 ms, should be at least 100 ms, and should have an upper bound (recommended 2 s).
Sorting, Interleaving, and Address Selection
After DNS answers arrive, the client sorts destinations using the default destination address selection rules (RFC 6724), optionally seasoning the list with historical RTTs and previously used addresses. Then it interleaves families: if the first entry is IPv6, the next one is pulled from IPv4, and so on. Interleaving avoids waiting through a long run of one family if that family is impaired.
Connection Racing and Cancellation
The client starts one connection attempt, then—after the connection attempt delay—starts the next candidate while keeping previous attempts alive. When any attempt finishes the handshake, all others are canceled, and outstanding DNS queries may be canceled after a short grace period to let caches fill. If new DNS answers arrive mid-race, they’re merged into the not-yet-tried portion so the race stays fair.
Tuning Behavior in Real Implementations
Implementations differ in defaults and exposed knobs. cURL (libcurl) defaults its “happy eyeballs timeout” to 200 ms and lets you tune it via CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS or the command line; historically several browsers have used around 300 ms for the family switch, while Safari is notable for actually implementing the RFC’s 50 ms resolution delay. These differences explain why the same hostname can feel faster in one client than another on the same network.
When to Nudge the Timers
Shorten the connection attempt delay if you consistently see slow first SYN completion on one family and you’d rather not wait for it to time out; lengthen it if your backends suffer from too many speculative connections or you observe churn. Keep the resolution delay small; stretch it only if your AAAA answers are known to trail A by more than a few tens of milliseconds and you truly want to favor IPv6 at the cost of occasional extra wait.
State, Caches, and Per-Network Memory
A well-behaved engine tracks historical RTT and “addresses used” to inform ordering, but it must scope this state to the current network and flush it when the interface changes. That keeps heuristics from penalizing a healthy network for problems seen on a different one.
Operational Edge Cases You Should Expect
IPv6-only segments with NAT64/DNS64 are common now. If apps use IPv4 literals, the stack may need to synthesize IPv6 endpoints for them or provide 464XLAT, so v4-only inputs still work in an IPv6-only environment. v2 details how to keep these networks usable without breaking the bias toward IPv6.
DNS Behavior Can Make or Break Perceived Speed
Slow or reordered answers often explain “why did it pick IPv4” reports. If A consistently arrives well before AAAA at your resolver, the resolution delay is the only thing standing between you and an IPv4-first bias. Where multiple DNS server addresses exist on an interface, v2 recommends sending queries over the IPv6-reachable resolver first; penalize unresponsive resolvers and rotate as needed. Measure both DNS response timings and connect handshake RTTs before changing defaults.
Server and Network Hygiene Still Matters
Happy Eyeballs isn’t a band-aid for broken routing. Ensure IPv6 prefixes are fully reachable, that anycast DNS returns consistent A/AAAA sets across vantage points, and that path MTU discovery works so post-handshake data doesn’t stall. If you terminate TLS, keep certificate chains and ALPN offerings consistent across families so the “first to connect” is also “first to handshake.”
Checklist: Shipping a Good Happy Eyeballs Experience
Send AAAA then A without waiting; apply a 50 ms resolution delay if A wins the race; sort with RFC 6724, interleave families with a first-family count of 1; start the next connect after ~250 ms (or at the second-SYN point) with a hard lower bound (≥100 ms, never <10 ms) and an upper bound (~2 s); cancel losers fast; cache per-network outcomes; and validate your choices with real RTT and DNS traces before rolling to users.