Why architecture matters here
The architecture matters because migration is the feature that makes QUIC worth adopting for mobile and multi-homed clients, and getting it wrong turns a resilience feature into a security hole. On the mobile internet, address changes are not rare edge cases — they are routine. A user commuting on a train hands off between cell towers; a laptop moves from a coffee-shop WiFi to a tethered phone; a corporate NAT rebinds mappings on a timer. Each of these, on TCP, forces a full reconnect. Multiply that across millions of sessions and the reconnect storm becomes a real load and a real user-visible latency spike at exactly the moment the user is on a degraded network.
Migration turns those events into non-events. Because the connection is identified by a token the endpoints control rather than by addresses the network assigns, the session outlives the address. The application's streams — including half-finished uploads and in-flight request/response pairs — continue where they left off. There is no TLS renegotiation, no application-layer reconnect handshake, and no lost session state. For a bidirectional protocol that keeps server-pushed data flowing to the client, this is critical: a reconnect would create a gap during which pushes are dropped or must be replayed.
But the same property that makes migration useful — accepting packets from a new address on an existing connection — is exactly what an attacker would exploit if it were unguarded. Two threats dominate the design. The first is a redirection attack: an on-path attacker spoofs the client's address and tries to steer the server's response traffic somewhere else. The second is amplification: because QUIC packets can trigger larger responses, an attacker who forges a source address could turn the server into a reflector aimed at a victim. The architecture answers both with path validation and a strict anti-amplification limit, and every implementation has to honor them.
There is also a performance dimension. A new network path has different characteristics — different round-trip time, different available bandwidth, different loss behavior. Congestion-control state built up on the old path is not just irrelevant on the new one, it is dangerous: reusing an aggressive congestion window on a slower path causes a burst of loss. So migration is not merely a routing trick; it forces the transport to reset or carefully carry over its congestion estimators. The architecture couples identity, security validation, and congestion behavior into a single coordinated event, and understanding that coupling is what separates a robust deployment from one that either stalls or floods on every handoff.
The architecture: every piece explained
The Connection ID is the keystone. During the handshake each endpoint tells the other which CIDs it is willing to have used to address it. These are opaque octet strings — the peer treats them as a routing token and never interprets their contents. Crucially, endpoints issue multiple CIDs and rotate through them, because reusing a single CID across a migration would let a passive observer link the old path to the new one and track the user. New CIDs are delivered in NEW_CONNECTION_ID frames, each with a sequence number, so a peer always has a spare unlinkable CID to switch to when it moves.
Path validation is the security gate. When a server first sees packets arriving with a known CID but from a new address, it does not immediately trust the path. It sends a PATH_CHALLENGE frame containing an unpredictable random value; the peer must echo that exact value back in a PATH_RESPONSE. Because only an endpoint actually receiving packets at the new address can echo the challenge, a successful response proves the peer is really reachable there and is not merely a spoofed source address. Until validation completes, the path is 'unvalidated' and treated with suspicion.
The anti-amplification limit constrains that suspicion concretely. On an unvalidated path a server may send at most three times the number of bytes it has received from that address. This caps how much a forged-source attacker could reflect: even if they spoof a victim's address to trigger a challenge, the server will not blast megabytes at the victim before the path is confirmed. The limit lifts only when path validation succeeds, at which point normal sending resumes.
Congestion control and RTT reset handle the performance side. RFC 9000 requires that when an endpoint migrates to a new path it reset its congestion controller and round-trip-time estimator to their initial values, because the new path may have entirely different capacity. An implementation may keep the old path's state cached in case the connection migrates back, but it must not assume the new path can absorb the old path's window. This deliberate conservatism trades a brief ramp-up for safety against a self-inflicted loss burst.
Finally, the CID-aware load balancer makes migration work at scale. In a fleet where many servers sit behind a load balancer, the balancer normally hashes the four-tuple to pick a backend. After a migration the four-tuple changes, so a naive balancer would send the migrated packets to a different — and stateless — backend. QUIC deployments solve this by encoding routing information into the Connection ID itself (a technique standardized as QUIC-LB): the server chooses CIDs that let the balancer decode which backend owns the connection, so migrated packets still land on the machine holding the session state.
End-to-end flow
Follow a single handoff from the client's perspective. The client is streaming over WiFi, connection established, CID A in use, streams flowing in both directions. The user leaves the building and the WiFi drops; the OS switches the default route to the cellular interface, which has a fresh public IP. The client's QUIC stack notices the local address changed and, to avoid linkability, selects a previously-unused CID B from its pool of peer-issued CIDs to use when addressing the server going forward, and begins sending packets from the new source address.
The server receives a packet: known CID, unknown source address. It records the new candidate path, marks it unvalidated, and immediately sends a PATH_CHALLENGE with a random 64-bit value. Because the path is unvalidated, everything the server sends is now bounded by the anti-amplification limit — at most three times the bytes received on this new path. The client, receiving the challenge at its new address, replies with a PATH_RESPONSE echoing the value.
The server matches the echoed value to the challenge it sent, marks the path validated, and lifts the amplification limit. In parallel it resets its congestion window and RTT estimate for the new path to initial values and begins probing capacity from a conservative starting point. The client does the same on its side. Application streams that were in flight — a partially uploaded file, an outstanding request awaiting a response — simply continue; QUIC's stream layer is unaware that the underlying path changed at all.
Meanwhile the load balancer in front of the server fleet has seen packets arrive on a new four-tuple. Instead of hashing the tuple, it decodes the routing bits embedded in CID B, determines the owning backend, and forwards the packets there — the same machine that holds the session. Within a single round trip of path-validation exchange, the connection is fully live on cellular, congestion control is safely re-probing, and the user, who was mid-call or mid-sync, noticed at most a momentary pause rather than a dropped session and a spinner.