Learning objective. Understand how QUIC absorbs an involuntary address change (a NAT rebinding the client never intended) without dropping the connection: why it looks like a migration to the server, the heuristics and port-only exception that keep it cheap, and the anti-flapping rule that keeps reordering from thrashing the path.
Not every address change is a migration #
§10.1 and §10.2 covered deliberate migration: the client choosing to move networks. But most address changes are not chosen by anyone. A NAT rebinding is "a change of address due to a middlebox, usually a NAT, allocating a new outgoing port or even a new outgoing IP address for a flow" ([RFC 9000 §9]). The client did nothing; its NAT simply expired the old mapping (UDP mappings are often reclaimed after tens of seconds of idle) and issued a new port. From the client's side the connection ID never changed and it has no idea anything happened. But the server suddenly sees packets arriving from a new address.
Crucially, the server cannot tell the difference at first: "Receiving a packet from a new peer address could be the result of a NAT rebinding at the peer" ([RFC 9000 §9.3]) or a genuine migration. So it treats both the same way (validate the path), and QUIC's job is to make that transparent to the application.
The same mechanism, made cheap #
The server validates the new address with the PATH_CHALLENGE/PATH_RESPONSE exchange of §10.1. An involuntary rebinding still has to prove reachability, since a NAT rebinding is indistinguishable from a spoofing attempt. What keeps a rebinding cheap are two refinements:
- The port-only exception to the reset. A full migration resets congestion control and RTT to initial values (§10.1), which briefly throttles throughput. But a bare port change is almost always a NAT rebinding on the same physical path, so the estimates still hold. QUIC allows skipping the reset in exactly this case: the reset is required "unless the only change in the peer's address is its port number... Because port-only changes are commonly the result of NAT rebinding... the endpoint MAY instead retain its congestion control state and round-trip estimate" ([RFC 9000 §9.4]). So the common rebinding costs a path validation but not a throughput stall.
- Heuristics that guess rebinding vs. migration. The two are not perfectly separable, but signals help: "NAT rebinding is improbable if packets were recently received on the old path; similarly, rebinding is rare on IPv6 paths... Conversely, a change in connection ID is more likely to indicate an intentional migration" ([RFC 9000 §9.3.3]). A deliberate migration changes the connection ID (§10.2); a NAT rebinding does not, because the client is unaware. That single difference is the strongest tell.
The anti-flapping rule #
Address changes plus packet reordering could make a server oscillate: follow the peer to a new address, then receive a delayed packet from the old address and follow it back, and so on. QUIC forbids this with a simple rule ([RFC 9000 §9.3]):
"An endpoint only changes the address to which it sends packets in response to the highest-numbered non-probing packet. This ensures that an endpoint does not send packets to an old peer address in the case that it receives reordered packets."
Because QUIC packet numbers strictly increase (§3.1), a stale packet from the old address carries a lower number than the packets already seen from the new one, so it is ignored for address-selection purposes. The server settles on the newest path and does not thrash.
When validation fails, and other hazards #
The same machinery guards against attacks that look like rebinding:
- Revert on failure. If validating the new address fails, "an endpoint MUST revert to using the last validated peer address" ([RFC 9000 §9.3.2]). A spurious or malicious address change cannot strand the connection. And "if an endpoint has no state about the last validated peer address, it MUST close the connection silently" ([RFC 9000 §9.3.2]).
- Off-path forwarding. An attacker copying genuine packets to the server can make the real packet look like a duplicate and its copy like a rebinding ([RFC 9000 §9.3.3]); the defense is to revalidate the original path too: "endpoints MUST validate the previously active path using a PATH_CHALLENGE" ([RFC 9000 §9.3.3]). So a non-probing packet that later arrives on the old path moves the connection back to it.
- Zero-length connection IDs. A NAT rebinding is one of the failure modes that make zero-length connection IDs risky: with nothing to distinguish connections on the same address and port, a rebinding can misroute packets ([RFC 9000 §5.1]). This is a reason servers that expect mobile clients issue non-zero connection IDs.
Worked example: an idle mapping expires #
Trace Figure 10.3-1. A client behind a home NAT is in a lull on a long-lived connection; the NAT reclaims
the idle UDP mapping and, on the next packet, assigns a new external port p2. The client, unaware, sends
its next STREAM packet with the same connection ID: it simply emerges from X:p2 now. The server sees a
known connection ID from a new address. Its heuristics — same connection ID, only the port changed — point
to a NAT rebinding rather than an intentional migration, but it validates X:p2 regardless with a
PATH_CHALLENGE, which the genuine client echoes. Because only the port changed, the server keeps its
congestion window and RTT estimate, so there is no throughput dip. A moment later a reordered packet from
the old X:p1 arrives, but it carries a lower packet number than what the server has already seen from
p2, so the server ignores it for path selection and does not flap. The application never noticed.
This is why keepalives (§7.3) and NAT rebinding are two sides of one coin. A NAT expires an idle UDP mapping after tens of seconds; a keepalive PING sent under that horizon prevents the mapping from being reclaimed at all, avoiding the rebinding. But if a rebinding does happen — the client was fully idle, or the NAT is aggressive — connection migration catches it and the connection survives anyway. Keepalive avoids the rebinding; migration survives it. A robust mobile client uses both.
editorial You usually get NAT-rebinding resilience for free by enabling connection migration and issuing non-zero connection IDs. But verify it, because the failure is silent until a real rebinding drops a connection in the field. Test it deliberately: on a QUIC client behind a NAT, force a rebinding (some NATs rebind on idle; you can also change the client's source port) and confirm the connection survives with no application-visible break. Watch for two anti-patterns: a server that resets congestion control on a port-only change (needless throughput dips on every rebinding), and zero-length connection IDs on a server fronting mobile users (rebindings can misroute). Both are cheap to get right and painful to diagnose later.
Takeaways #
A NAT rebinding is an involuntary address change that looks identical to a migration at the server, so QUIC handles it with the same path validation, but keeps it cheap by skipping the congestion/RTT reset on a port-only change and by using heuristics (an unchanged connection ID strongly implies rebinding). The anti-flapping rule (only follow the highest-numbered non-probing packet) keeps reordering from thrashing the path, and revert-on-failure plus old-path revalidation guard against spoofing. Together these let a connection ride out a NAT change with no application-visible disruption. How resumption and 0-RTT let even a fully dropped connection restart cheaply is §10.4.