Happy Eyeballs v2: how dual-stack clients race IPv6 and IPv4

How RFC 8305 clients query AAAA and A, sort addresses and stagger connection attempts to use whichever connects first when DNS returns IPv6 and IPv4, and what its recommended defaults mean.

networkingIPv6DNSTCP

The DNS lookup article ended on the point that once A and AAAA records come back, the client decides which address to try first. This article picks up from there. When one hostname resolves to several IPv6 and IPv4 addresses, Happy Eyeballs is what defines the order a dual-stack client tries them in and when it moves on to the next one. The current specification is RFC 8305, Happy Eyeballs Version 2.

The problem with waiting on one address at a time

RFC 6724 describes the usual pattern: an API such as getaddrinfo() returns a sorted list of addresses, and the application tries the first one, looping through the list until it finds a working address. With that approach, if an address near the front doesn’t respond, the client can’t move on until that attempt has failed.

RFC 6555 described the situation as of 2012. When IPv6 connectivity was impaired, IPv6-capable applications incurred many seconds of delay before falling back to IPv4, and as a result IPv6 was frequently disabled entirely on end systems. An AAAA record in DNS doesn’t guarantee that the IPv6 path actually works.

RFC 6724 says applications SHOULD NOT simply use the first address and give up if it fails, and notes that for some applications it may be appropriate to try multiple addresses in parallel, with a small delay in between, and use the first one to succeed. Happy Eyeballs turns that idea into a concrete procedure.

What RFC 8305 does and doesn’t define

RFC 8305 is a Standards Track document published in December 2017. It obsoletes RFC 6555 (April 2012), which described the original algorithm. By its own description it contains two things: requirements for algorithms that reduce user-visible delay, and an example algorithm.

So numbers like 50 ms and 250 ms in this article are configurable defaults the RFC recommends. They are not values that every browser or operating system uses as-is. Section 8 of RFC 8305 says these delays were determined by measuring connection timing on a very wide set of production devices, that they are expected to change over time, and that implementers are welcome to use different values without changing the specification. To know which values a particular client uses, you have to check that implementation’s documentation or code.

RFC 8305 is written assuming a host policy that prefers IPv6 over IPv4, and says the recommendations can be adapted if the host is configured differently. The procedure has four phases:

  1. Start asynchronous DNS queries (Section 3)
  2. Sort the resolved destination addresses (Section 4)
  3. Start asynchronous connection attempts (Section 5)
  4. Once one connection is established, cancel the other attempts (Section 5)

Step 1: send AAAA and A, and don’t wait for both

A client with both IPv4 and IPv6 connectivity needs to send both AAAA and A queries. The RFC says the two SHOULD be sent as close together as possible, with the AAAA query first and the A query immediately after.

Implementations SHOULD NOT wait for both answers before starting to connect, and SHOULD treat DNS resolution as asynchronous. If one query doesn’t return or takes much longer, waiting for it would delay the connection on the other family too. If the platform has no asynchronous DNS API, the RFC notes you can get the same behavior by running two synchronous queries on separate threads, one per address family.

What happens next depends on which answer arrives first.

  • If a positive AAAA response (at least one valid AAAA record) arrives first, the first IPv6 connection attempt starts immediately.
  • If a positive A response arrives first, the client SHOULD wait briefly for the AAAA response so that IPv6 gets preference. This wait is the Resolution Delay, and its recommended value is 50 ms. The RFC explains that it is common for the AAAA response to follow the A response by a few milliseconds.
  • If a positive AAAA response arrives within that window, the IPv6 attempt starts immediately. If a negative AAAA response (no error, no data) arrives, or no AAAA response has arrived by the end of the window, the client SHOULD go ahead with sorting and connection attempts using the IPv4 addresses received so far.
  • If the AAAA response arrives while attempts are in progress but before any connection is established, the new IPv6 addresses are added to the candidate list and the attempts continue.

In other words, 50 ms is about how long to wait for AAAA after A has already arrived. It is not a delay added to DNS resolution as a whole.

Step 2: sort with RFC 6724, then interleave address families

Before connecting, the client decides the order of attempts. The list should include every address from both families received up to that point. That way the racing effect applies to the whole list, not just the first IPv6 and first IPv4 address.

First, the client MUST sort the addresses using Destination Address Selection from Section 6 of RFC 6724. That algorithm sorts IPv6 and IPv4 addresses together in one list and applies ten pairwise comparison rules in order. They include a rule to avoid unusable destinations and a rule comparing precedence values from the policy table. RFC 8305 adds that a client with historical RTT data for each route SHOULD insert a rule preferring lower-RTT addresses between RFC 6724’s rules 8 and 9. That historical data MUST NOT be used across different network interfaces.

Next, the client SHOULD modify the list so the address families alternate. If the first sorted address is IPv6, the first IPv4 address is moved up to second place. This avoids waiting through a long list of addresses from one family when that family is broken. The number of addresses from the first family to try before switching is the First Address Family Count. The recommended value is 1, and 2 can be used to favor one family more strongly.

Step 3: staggered starts, overlapping attempts

Connection attempts SHOULD NOT be started simultaneously. To avoid unreasonable network load, the client starts one attempt to the first address, then starts the others in the list one at a time. Starting a new attempt doesn’t stop the earlier ones, so several attempts can be in progress at once. Rather than firing everything at the same moment, the client adds attempts one by one at an interval while still waiting on the earlier ones.

The interval before the next attempt is the Connection Attempt Delay.

  • Without RTT data, the recommended default is 250 ms. The RFC says a more nuanced implementation should time it to when the previous attempt would send its second TCP SYN, meaning the first SYN retransmission.
  • There MUST be a lower bound, and a new connection attempt MUST NOT start within 10 ms of the previous one. The recommended minimum (Minimum Connection Attempt Delay) is 100 ms.
  • There SHOULD be an upper bound. The recommended maximum (Maximum Connection Attempt Delay) is 2 seconds.

So the hard requirements are having a lower bound and never starting attempts less than 10 ms apart. The 50 ms, the count of 1, 250 ms, 100 ms and 2 seconds are recommended values.

Step 4: the first successful connection wins

When one attempt succeeds (generally when the TCP handshake completes), all other attempts that haven’t succeeded yet SHOULD be canceled, and any address not yet attempted SHOULD be ignored. The asynchronous DNS query MAY be canceled at this point. The DNS resolver SHOULD still process replies from the network for a short period (recommended to be 1 second), because they populate the DNS cache and can be used for later connections.

A hypothetical timeline using the defaults

The example below is hypothetical and assumes the client uses RFC 8305’s recommended defaults as-is (Resolution Delay 50 ms, First Address Family Count 1, Connection Attempt Delay 250 ms). The times are not measurements, and the addresses are documentation examples. It assumes RFC 6724 sorting put the IPv6 addresses first.

(Hypothetical example based on RFC 8305 recommended defaults)
  0 ms  Send AAAA query, then A query immediately after
 20 ms  A response arrives: 192.0.2.10
        → wait up to 50 ms for AAAA (until 70 ms)
 35 ms  AAAA response arrives: 2001:db8::10, 2001:db8::20
        → sort, then interleave families:
          [2001:db8::10, 192.0.2.10, 2001:db8::20]
        → attempt 1 starts: 2001:db8::10 (no reply)
285 ms  attempt 1 not yet established → attempt 2 starts: 192.0.2.10
        (attempt 1 is not canceled and keeps running)
325 ms  attempt 2 completes its TCP handshake → use this connection
        → cancel attempt 1; 2001:db8::20 is never tried

If no AAAA response had arrived by 70 ms, the client would start sorting and connecting with only the IPv4 address at that point. If AAAA then arrived before any connection was established, its IPv6 addresses would be sorted into the list of addresses not yet attempted.

What changed from v1 (RFC 6555)

According to Appendix A of RFC 8305, RFC 6555 mostly concentrated on how to stagger connections to a hostname that has a AAAA and an A record. RFC 8305 additionally covers how to perform the DNS queries, how to handle multiple addresses from each family, how to handle DNS updates while connections are being raced, how to use historical information, and how to support IPv6-only networks with NAT64 and DNS64. It also notes that a simple implementation of RFC 8305’s algorithm is still compliant with RFC 6555.

Things to keep in mind when debugging

  • Happy Eyeballs handles initial connection failures at the TCP/IP layer. Other failures or performance problems can still affect the chosen connection, and because it only operates during the initial handshake, MTU-related problems can go unnoticed (Sections 9 and 9.1).
  • If DNS returns addresses for different application servers, the application itself may not be working on all of them. The RFC gives TLS and HTTP as common examples (Section 9.2).
  • If a misconfiguration makes IPv6 fail consistently while IPv4 still works, Happy Eyeballs can make it harder for the operator to notice. The RFC recommends that network operators use external monitoring for every address family (Section 9.3). A connection that works doesn’t mean IPv6 is healthy.
  • Don’t assume that connections to the same hostname always go to the same IP address. RFC 8305 notes that Happy Eyeballs may make it more likely that subsequent connections use different IP addresses, and says applications should not rely on a stable hostname-to-address mapping for security properties (Section 10).

Sources

Checked on 2026-10-11. This article describes RFC 8305. On the same date, the IETF Datatracker listed RFC 8305 as a Proposed Standard, with no RFC shown as obsoleting it.

Back to the blog