Every IPsec engineer eventually hits the same wall. The tunnel worked perfectly in the lab, you shipped the branch router, and now the site is behind a broadband modem doing PAT. Phase 1 comes up. Phase 2 does not. Nothing crosses. This is the single most recognisable failure in the whole of IPsec VPN, and the reason for it is embarrassingly simple: ESP has no port numbers, and PAT is a machine that rewrites port numbers.
NAT Traversal (NAT-T) is the fix. This article shows you exactly why raw IPsec dies behind a NAT, what NAT-T actually changes on the wire, and what the failure and the fix look like in real CLI output from a live CML lab, including the capture almost nobody publishes: the NAT device's own translation table.
Why ESP and PAT Cannot Coexist
Start with the protocol numbers, because the whole story is in them.
PAT (what Cisco calls NAT overload) multiplexes many inside hosts onto one public address by giving each flow a unique source port. That is its entire mechanism. Hand it an ESP packet and there is no port field to allocate, no port field to record, and no port field to reverse on the way back. Some NAT implementations bolt on an ESP passthrough hack that tracks the SPI instead, and it works for exactly one inside host, until a second one shows up. Cisco IOS PAT does not pretend. It simply cannot track the flow.
AH is worth a sentence of its own because people ask. AH authenticates the immutable fields of the outer IP header, and the source address is one of them. NAT rewrites that address by definition. The receiver recomputes the ICV, gets a different answer, and drops the packet. There is no NAT-T for AH and there never will be. NAT traversal is an ESP-only story, which is one more reason nobody deploys AH.
The Lab
Two site-to-site IPsec endpoints, EDGE1 at the head end on 203.0.113.1 and EDGE2 at the branch. For this test EDGE2 was moved behind NAT1, an IOS router doing plain PAT on its outside interface:
ip nat inside source list PLZ-PAT interface Ethernet0/0 overloadEDGE2's real address is now a private 10.30.99.2. To the rest of the world, including EDGE1, it appears as 198.51.100.6. EDGE2 itself has no idea any of this is happening, which turns out to matter a great deal.
The Failure: Phase 1 Up, Phase 2 Dead
Disable NAT-T on the branch router (IOS enables it by default, so you have to go out of your way to break it):
EDGE2(config)#no crypto ipsec nat-transparency udp-encapsulationNow ping across the tunnel:
EDGE2#ping 192.168.99.100 source Loopback10 repeat 5
.....
Success rate is 0 percent (0/5)Total failure. Now look at Phase 1:
EDGE2#show crypto isakmp sa
dst src state conn-id status
203.0.113.1 10.30.99.2 QM_IDLE 1008 ACTIVEQM_IDLE. That is the healthy, settled state of an IKEv1 Phase 1 SA. The tunnel's control plane is completely up. Authentication succeeded, DH completed, the ISAKMP SA is ACTIVE. If you stopped troubleshooting here you would swear the VPN was fine.
Now look at Phase 2:
EDGE2#show crypto ipsec sa | include #pkts
#pkts encaps: 0, #pkts encrypt: 0, #pkts digest: 0
#pkts decaps: 0, #pkts decrypt: 0, #pkts verify: 0Zero in every column. Not a single packet has been encapsulated or decapsulated. The data plane is stone dead.
Why the split happens exactly here
This is the part worth internalising, because once you see it you will diagnose this failure from across the room.
IKE negotiation rides on UDP port 500. UDP has ports. NAT1's PAT box takes EDGE2's 10.30.99.2:500 packets, allocates a translation, rewrites the source to 198.51.100.6 with some high port, and forwards them. Replies come back, PAT reverses the translation, EDGE2 receives them. Phase 1 completes normally. Phase 2 (Quick Mode) also runs inside IKE on UDP 500, so the IPsec SAs get negotiated too.
Then the router tries to send actual user data, and that data is ESP. IP protocol 50. It arrives at NAT1 with no ports at all. PAT has nothing to multiplex on, no way to build a translation entry, and no way to demultiplex the return traffic. The packets die at the NAT device.
So you get a control plane that works and a data plane that does not. Phase 1 up, Phase 2 at zero, with a NAT anywhere in the path, is the NAT-T signature. Learn those two show commands together and you will never spend an afternoon on this again.
The Fix: Wrap ESP in UDP 4500
Re-enable NAT transparency (or just leave the default alone) and the tunnel comes straight up. Here is what changed.
EDGE2#show crypto session detail
Session status: UP-ACTIVE
Peer: 203.0.113.1 port 4500 fvrf: (none) ivrf: (none)
Capabilities:N connid:1011 lifetime:00:57:40
IPSEC FLOW: permit ip 10.30.10.0/255.255.255.0 192.168.99.0/255.255.255.0
Inbound: #pkts dec'ed 9 drop 0 life (KB/Sec) 4607998/3597
Outbound: #pkts enc'ed 9 drop 0 life (KB/Sec) 4607999/3597Two things to read here. The peer is on port 4500, not 500. And Capabilities:N, where N means NAT-traversal was detected.
Notice what you did not do: you never configured port 4500 anywhere. IOS discovered the NAT by itself. During Main Mode, both peers exchange NAT-D (NAT Discovery) payloads containing hashes of the source and destination IP and port as each side believes them to be. If the hash a peer receives does not match what it computes from the packet it actually got, an address was rewritten in transit, so there is a NAT in the path. Both ends then agree to float the negotiation from UDP 500 to UDP 4500 and to UDP-encapsulate all subsequent ESP. The whole thing is automatic and it happens during Phase 1.
Now the IPsec SA:
EDGE2#show crypto ipsec sa
local crypto endpt.: 10.30.99.2, remote crypto endpt.: 203.0.113.1
#pkts encaps: 15, #pkts encrypt: 15, #pkts digest: 15
transform: esp-256-aes esp-sha256-hmac ,
in use settings ={Tunnel UDP-Encaps, }Two lines carry the whole lesson.
local crypto endpt.: 10.30.99.2 is the private address. EDGE2 has no clue it is being translated. It builds and signs its packets from 10.30.99.2 and hands them to the network. The NAT rewrite happens downstream, outside the router's awareness, which is precisely why the outer header must not be authenticated (and precisely why AH cannot play).
in use settings ={Tunnel UDP-Encaps, } is the clincher. In a normal tunnel you see {Tunnel, }. Here you see Tunnel UDP-Encaps: tunnel mode, with the ESP payload encapsulated in UDP. That string is the definitive proof that NAT-T is engaged. If you only remember one grep from this article, make it that one.
Traffic flows: 15 packets encapsulated, 9 decapsulated, pings at 100 percent.
The NAT Device's View
This is the capture that closes the argument. Log into NAT1, the PAT router in the middle, and ask it what it is actually tracking:
NAT1#show ip nat translations
Pro Inside global Inside local Outside local Outside global
udp 198.51.100.6:4790 10.30.99.2:500 203.0.113.1:500 203.0.113.1:500
udp 198.51.100.6:4791 10.30.99.2:4500 203.0.113.1:4500 203.0.113.1:4500
NAT1#show ip nat statistics
Total active translations: 2 (0 static, 2 dynamic; 2 extended)
Hits: 79 Misses: 0Two entries. Both say udp. One for IKE on 500, one for ESP-in-UDP on 4500. As far as NAT1 is concerned there is no IPsec here at all, just two ordinary UDP conversations between an inside host and an outside host. It allocates ports (4790 and 4791), it builds extended translations, it counts 79 hits and zero misses. This is PAT doing the most boring thing it knows how to do, and that is the entire point.
Compare that with what NAT1 could have done with raw ESP: nothing. No line in the table, no port to allocate, no state to keep. The genius of NAT-T is not that it is clever. It is that it makes IPsec look mundane enough for a PAT box to handle.
The Costs and the Fine Print
8 bytes of overhead, and your MTU
UDP encapsulation adds a UDP header: 8 bytes on every single ESP packet, on top of the ESP overhead you already pay. That is 8 bytes less room for user data before the packet needs fragmenting. If you were already close to the edge (GRE over IPsec over a 1500-byte path is a classic squeeze), NAT-T can be the thing that tips you into fragmentation, and fragmented ESP is a well-known source of intermittent, size-dependent, maddening failures. Fix it at the source: set ip tcp adjust-mss on the LAN-facing interface and lower the tunnel MTU rather than letting the path fragment for you.
NAT keepalives
PAT translations age out. A NAT device holds a UDP entry for a while after the last packet (often 30 to 300 seconds) and then reclaims the port. If your VPN goes quiet for longer than that, the entry vanishes, and the far end's next inbound packet arrives at a NAT device that no longer knows where to send it. The tunnel appears up on both routers and traffic silently dies until the branch happens to send something first. IPsec NAT keepalives exist purely to prevent this: a tiny periodic packet on UDP 4500 whose only job is to keep the translation warm. On IOS that is crypto isakmp nat keepalive 20. Set it. It is cheap, and the failure it prevents is nasty and intermittent.
Firewall rules: 500 AND 4500
The classic operational mistake. Someone opens UDP 500 on the perimeter firewall because "IPsec uses 500", the tunnel negotiates beautifully, and then no data flows because ESP-in-UDP on 4500 is being dropped. It looks exactly like the failure at the top of this article, and the symptom is identical: Phase 1 up, Phase 2 at zero. Permit UDP 500 and UDP 4500 in both directions. If any peer might sit behind a NAT, you need both, always. (And if no NAT is involved, you also need IP protocol 50 permitted for raw ESP.)
Aggressive Mode, IKEv2, and the rest
NAT-D is exchanged in messages 3 and 4 of Main Mode, which is why the float to 4500 happens mid-Phase-1. IKEv2 does the same job with NAT_DETECTION_SOURCE_IP and NAT_DETECTION_DESTINATION_IP notify payloads in the initial exchange, and the resulting behaviour is the same: float to 4500, UDP-encapsulate ESP. The mechanism differs, the concept does not.
Diagnosing It in the Field
Means: Phase 1 is fine. IKE crossed the NAT on UDP 500. Do not stop here.
Means: Phase 2 negotiated but nothing crosses. With a NAT in path, suspect NAT-T immediately.
port 4500 and Capabilities:N.Means: NAT detected and NAT-T active. Port 500 plus a NAT in path means it is not.
in use settings ={Tunnel UDP-Encaps, }.Means: ESP is inside UDP. This is the definitive proof.
udp entries, :500 and :4500.Means: PAT is tracking both flows. No 4500 entry means ESP never got wrapped.
Means: the router is behind a NAT and does not know it. Normal, and exactly why NAT-T exists.
One more warning from the same lab. When we moved EDGE2 behind PAT, the head end kept failing Phase 2 with error 64 and peer address 198.51.100.6 not found, because EDGE1 still had a static crypto map entry pointing at EDGE2's old public address. IOS matched the static entry on proxy ID, found the peer did not match, and rejected the proposal instead of falling through to the dynamic map. If you migrate a peer behind a NAT, clean up the stale static entry. There is more on reading those error codes in our guide to troubleshooting IPsec VPN tunnels.
Key Takeaways
- ESP is IP protocol 50 and has no port numbers. PAT works by rewriting ports. It therefore has nothing to multiplex on and cannot track an ESP flow. That single fact is the whole problem.
- The signature is Phase 1 up, Phase 2 at zero.
show crypto isakmp sareads QM_IDLE whileshow crypto ipsec sashows#pkts encaps: 0, #pkts decaps: 0. IKE on UDP 500 survives PAT; raw ESP does not. - NAT-T wraps ESP in UDP port 4500, which PAT translates like any other UDP flow. On the NAT box you see two ordinary udp translations, :500 and :4500, and that is the entire trick.
- You do not configure the port. IOS auto-detects the NAT during IKE using NAT-D payloads and floats to 4500 on its own. Confirm it with
Peer: ... port 4500andCapabilities:N. - The proof string is
in use settings ={Tunnel UDP-Encaps, }. Also expectlocal crypto endpt.to show the private address, because the router genuinely does not know it is being translated. - Mind the 8 bytes. The extra UDP header eats MTU. Adjust TCP MSS and tunnel MTU rather than letting the path fragment.
- Enable NAT keepalives so idle PAT entries do not age out and blackhole the tunnel.
- Permit UDP 500 AND UDP 4500. Opening only 500 produces the identical Phase-1-up, Phase-2-dead symptom and wastes hours.
- AH is out. It authenticates the outer IP header that NAT rewrites, so it can never traverse a NAT. NAT-T is an ESP-only story.
NAT-T is one of the pieces that makes real-world IPsec deployable at all, alongside crypto maps, tunnel interfaces and the failure modes that come with them. For the full picture, start at the IPsec VPN pillar, build the baseline tunnel with site-to-site IPsec crypto maps, and keep the IPsec troubleshooting guide open next to the console.