NAT policies on Palo Alto Networks firewalls are evaluated before security policy and operate exclusively on pre-NAT zones and pre-NAT IP addresses. That single architectural fact catches engineers off guard more than anything else on this platform. A NAT rule that looks perfectly configured in the GUI can silently fail because the source zone is wrong, because a more general rule higher in the list consumed the traffic first, or because the routing table has no path to the post-NAT destination. This runbook walks through every common failure mode with real CLI commands and sample output so you can resolve the issue without guesswork.
Symptoms
When a Palo Alto NAT rule is not working, you will typically observe one or more of the following:
- Internal hosts reach the internet using their original RFC 1918 addresses instead of the expected public IP — the remote host sees 10.0.1.50 instead of 203.0.113.10
- Inbound destination NAT does not forward traffic to the internal server — TCP connections time out at the public IP
- The CLI command
test nat-policy-match
returns no matching NAT rule found - Session table entries show the original source and destination with no translation applied
- Bi-directional NAT hosts can receive inbound sessions but cannot initiate outbound sessions, or the reverse
- Security policy denies traffic because the expected translated IP does not match any rule condition
- Traffic hits an unintended NAT rule earlier in the policy list and is translated to the wrong address
- Packet captures show packets leaving the firewall with untranslated addresses
Root Cause 1: Rule Order Wrong
Palo Alto evaluates NAT rules top-to-bottom and applies the first match. If a more general rule appears above your specific rule, traffic matches the general rule and your intended translation never fires. This is the single most common cause of unexpected NAT behavior and must always be the first thing checked.
Why It Happens
Engineers frequently add new NAT rules at the bottom of the policy and assume they will be reached, not realizing that a broad catch-all source NAT rule higher in the list is already consuming the traffic. A rule that translates any source on the trust zone to the outside interface IP will match all outbound traffic regardless of destination, leaving more-specific destination NAT rules below it unreachable for those flows. This problem is compounded when multiple administrators manage the firewall and no one owns the policy ordering.
How to Identify
Use the CLI test utility with the exact parameters of the failing traffic flow:
admin@PA-FW-01> test nat-policy-match source 10.0.1.50 destination 8.8.8.8 source-port 12345 destination-port 53 protocol 17 from trust to untrust
NAT Rule: catch-all-outbound-nat
Src Tran: Dynamic-IP-And-Port interface-address
Dst Tran: none
If the output shows a different rule name than what you intended, rule order is the problem. Review the full ordered policy with:
admin@PA-FW-01> show running nat-policy
"catch-all-outbound-nat" {
from trust;
to untrust;
source any;
destination any;
service any;
translate-to "src: interface-address";
index 1;
}
"web-server-dnat" {
from untrust;
to untrust;
source any;
destination 203.0.113.20;
service service-http;
translate-to "dst: 10.10.20.80 port 80";
index 4;
}
Note the index values — lower index means higher priority. A rule at index 1 is always evaluated before index 4.
How to Fix
Move specific rules above general ones. In the GUI, navigate to Policies > NAT, select the rule, and use the Move buttons. On the CLI:
admin@PA-FW-01# move rulebase nat rule web-server-dnat top
admin@PA-FW-01# commit description "Reorder: move web-server-dnat above catch-all"
Commit job 138 queued.
Configuration committed successfully.
After reordering, validate:
admin@PA-FW-01> test nat-policy-match source 203.0.113.5 destination 203.0.113.20 source-port 55000 destination-port 80 protocol 6 from untrust to untrust
NAT Rule: web-server-dnat
Src Tran: none
Dst Tran: 203.0.113.20 -> 10.10.20.80 port 80
Root Cause 2: Wrong Source Zone
NAT rules match on pre-NAT zones — the zones traffic arrives from and exits through before any translation occurs. If the source zone in the NAT rule does not match the actual ingress zone of the packet, the rule will never match regardless of how correct all other parameters appear.
Why It Happens
This is especially common in multi-zone deployments. An engineer building a NAT rule for traffic coming from a DMZ interface may accidentally select the trust zone in the rule, or may not realize that a sub-interface or VLAN is assigned to a different zone than expected. It also appears after firewall reconfiguration when a zone was renamed or an interface was reassigned to a new zone without updating existing NAT rules.
How to Identify
Confirm the actual zone of the source interface:
admin@PA-FW-01> show interface ethernet1/3
Name: ethernet1/3
Zone: dmz
IP: 172.16.10.1/24
Link status: up/up
Now check the NAT rule's configured source zone:
admin@PA-FW-01> show running nat-policy
"dmz-to-internet-snat" {
from trust; <-- configured as trust, should be dmz
to untrust;
source 172.16.10.0/24;
destination any;
service any;
translate-to "src: 203.0.113.10";
index 3;
}
Run the policy-match test specifying the actual zone and observe the failure:
admin@PA-FW-01> test nat-policy-match source 172.16.10.50 destination 1.1.1.1 source-port 45000 destination-port 443 protocol 6 from dmz to untrust
Result: no matching NAT rule found
Then test with the wrong zone (trust) to confirm the rule only matches incorrectly:
admin@PA-FW-01> test nat-policy-match source 172.16.10.50 destination 1.1.1.1 source-port 45000 destination-port 443 protocol 6 from trust to untrust
NAT Rule: dmz-to-internet-snat
Src Tran: Static-IP 203.0.113.10
How to Fix
Edit the NAT rule to reference the correct source zone:
admin@PA-FW-01# set rulebase nat rules dmz-to-internet-snat from dmz
admin@PA-FW-01# commit description "Fix source zone on dmz-to-internet-snat"
Configuration committed successfully.
Validate with the corrected zone:
admin@PA-FW-01> test nat-policy-match source 172.16.10.50 destination 1.1.1.1 source-port 45000 destination-port 443 protocol 6 from dmz to untrust
NAT Rule: dmz-to-internet-snat
Src Tran: Static-IP 203.0.113.10
Dst Tran: none
Root Cause 3: Destination NAT IP Conflict
When two or more destination NAT rules translate to overlapping conditions, or when a NAT virtual IP overlaps with an existing firewall interface address, traffic is silently mis-directed or dropped entirely. IP conflicts in NAT configurations are a subtle but impactful failure mode.
Why It Happens
In dense NAT configurations, multiple engineers may add port-forwarding rules that use the same public IP as the destination match condition. If two rules share the same destination IP and service object but translate to different internal hosts, only the first rule by index will ever match — the second rule is permanently shadowed. A second scenario occurs when a NAT pool IP or virtual IP happens to match one of the firewall's own interface addresses. The firewall treats those packets as self-traffic rather than forwarding them through the NAT rule, causing destination NAT to fail silently.
How to Identify
List NAT rules and look for duplicate destination IP and service combinations:
admin@PA-FW-01> show running nat-policy | match "destination|service|translate"
destination 203.0.113.20;
service service-http;
translate-to "dst: 10.10.20.80 port 80"; index 2
destination 203.0.113.20;
service service-http;
translate-to "dst: 10.10.20.85 port 80"; index 6 <-- permanently shadowed
Check whether the NAT virtual IP is assigned to a firewall interface:
admin@PA-FW-01> show interface all | match ip
ethernet1/1 203.0.113.20/28 L3 up untrust
ethernet1/2 10.0.1.1/24 L3 up trust
ethernet1/3 172.16.10.1/24 L3 up dmz
In this output, 203.0.113.20 is the firewall's own untrust interface IP. Any destination NAT rule with this as the translated destination means the firewall routes the post-NAT packet to itself — the internal server never receives it.
How to Fix
For shadowed rules with duplicate conditions, assign a distinct public IP to each internal server, or use different service objects (different destination ports) if a single public IP must be shared:
admin@PA-FW-01# set rulebase nat rules web-server-dnat-2 destination 203.0.113.21
admin@PA-FW-01# commit description "Resolve DNAT IP conflict: assign .21 to second server"
For interface IP conflicts, use a secondary IP or a dedicated IP block that does not overlap with any interface address. If using secondary IPs on the uplink for proxy-ARP based NAT, add them as secondary addresses on the interface, not as primary IPs, and confirm proxy-ARP is enabled on that interface.
Root Cause 4: Bi-directional NAT Misconfigured
Palo Alto's bi-directional NAT option on a static source NAT rule automatically creates a reverse destination NAT entry. When this feature is misconfigured or its zone and security policy requirements are not satisfied, one direction of traffic works while the other fails completely.
Why It Happens
Bi-directional NAT requires static IP translation — it has no meaning on a dynamic IP-and-port rule and is silently ignored if applied to one. Engineers also enable bi-directional NAT on a static rule but forget that the auto-generated reverse rule still requires a matching security policy to permit the inbound session. Because the reverse rule is auto-created and not visible as a separate entry in the NAT policy list, it is easy to miss. Additionally, the reverse rule uses the same zone definitions as the forward rule, which may not be correct for the inbound direction.
How to Identify
Check the rule type and bi-directional flag:
admin@PA-FW-01> show running nat-policy
"host-bidir-nat" {
from trust;
to untrust;
source 10.10.10.100/32;
destination any;
service any;
translate-to "src: static-ip 203.0.113.50 bi-directional yes";
index 5;
}
Test both the outbound direction and the inbound reverse direction:
admin@PA-FW-01> test nat-policy-match source 10.10.10.100 destination 8.8.8.8 source-port 50000 destination-port 443 protocol 6 from trust to untrust
NAT Rule: host-bidir-nat
Src Tran: Static-IP 203.0.113.50
Dst Tran: none
admin@PA-FW-01> test nat-policy-match source 45.33.22.11 destination 203.0.113.50 source-port 60001 destination-port 443 protocol 6 from untrust to untrust
Result: no matching NAT rule found
The reverse direction fails. Now test security policy for the reverse direction using the post-NAT internal IP:
admin@PA-FW-01> test security-policy-match source 45.33.22.11 destination 10.10.10.100 destination-port 443 protocol 6 from untrust to trust
Result: deny (implicit deny)
Both problems are present: the NAT reverse lookup fails (zone issue on the auto-generated rule) and security policy does not permit the inbound session.
How to Fix
First, verify the rule is using static IP translation and not dynamic. If the rule is DIPP, bi-directional cannot work — switch to static:
admin@PA-FW-01# set rulebase nat rules host-bidir-nat source-translation static-ip translated-address 203.0.113.50 bi-directional yes
Then add a security policy rule that permits inbound traffic to the post-NAT internal IP in the trust zone:
admin@PA-FW-01# set rulebase security rules allow-bidir-inbound-443 from untrust to trust source any destination 10.10.10.100 application ssl service service-https action allow log-setting default
admin@PA-FW-01# commit description "Fix bidir NAT: add security policy for inbound direction"
Configuration committed successfully.
Re-test both directions after the commit to confirm translations are working in both directions.
Root Cause 5: Route Missing for NAT IP
For destination NAT to deliver traffic to an internal server, the firewall must have a valid route to the translated (post-NAT) destination IP. If the routing table has no matching entry, the firewall drops the packet after the translation step with a route lookup failure — the NAT rule itself worked correctly, but the packet has nowhere to go.
Why It Happens
This is particularly common when destination NAT points to a server in a DMZ, server VLAN, or any subnet not directly connected to the firewall. The NAT rule correctly translates 203.0.113.20 to 10.10.20.80, but if the firewall's virtual router has no route to 10.10.20.0/24, the translated packet is dropped. It also occurs when a server is migrated to a new subnet, the NAT rule destination is updated, but nobody adds or updates the static route on the firewall.
How to Identify
After confirming the NAT rule matches correctly with
test nat-policy-match, test the routing table for the translated destination IP:
admin@PA-FW-01> test routing fib-lookup virtual-router default ip 10.10.20.80
Result: no route
Review the full routing table to understand what is and is not reachable:
admin@PA-FW-01> show routing route
FLAGS: A:active, ?:loose, C:connect, H:host, S:static, ~:recursive
DESTINATION NEXTHOP METRIC FLAGS AGE INTERFACE VIRTUAL-ROUTER
0.0.0.0/0 203.0.113.1 10 A S - eth1/1 default
10.0.1.0/24 directly conn. 0 A C - eth1/2 default
172.16.10.0/24 directly conn. 0 A C - eth1/3 default
10.10.10.0/24 directly conn. 0 A C - eth1/5 default
<-- 10.10.20.0/24 is absent from the routing table -->
The destination subnet 10.10.20.0/24 is missing. Any packet that NAT translates to an address in this range will be dropped at the routing step.
How to Fix
Add the missing static route for the destination subnet via the correct next-hop:
admin@PA-FW-01# set virtual-router default routing-table ip static-route to-server-vlan20 destination 10.10.20.0/24 nexthop ip-address 10.10.10.1 interface ethernet1/5 metric 10
admin@PA-FW-01# commit description "Add route to server VLAN 20 for DNAT traffic"
Configuration committed successfully.
Verify the route is now active and the FIB lookup resolves correctly:
admin@PA-FW-01> show routing route | match 10.10.20
10.10.20.0/24 10.10.10.1 10 A S - eth1/5 default
admin@PA-FW-01> test routing fib-lookup virtual-router default ip 10.10.20.80
Result:
interface: ethernet1/5
nexthop: 10.10.10.1
metric: 10
Now test end-to-end from an external host or using the session table to confirm traffic reaches the server.
Root Cause 6: Security Policy Blocking Post-NAT Traffic
NAT is evaluated first, but security policy must still permit the session. Security policy is evaluated against the post-NAT destination IP but the pre-NAT source zone. Engineers who write security rules targeting the public virtual IP (the pre-NAT address) instead of the internal server IP (the post-NAT address) will find that NAT works but sessions are still denied.
Why It Happens
The mental model most engineers bring to firewall rules — destination equals what you typed in the browser — is wrong on Palo Alto for destination NAT flows. The security policy evaluation happens after the NAT lookup, using the translated destination. A security rule with destination 203.0.113.20 will never match a session that NAT has already translated to 10.10.20.80. The correct destination in the security policy must be the internal IP.
How to Identify
Test security policy using the post-NAT parameters:
admin@PA-FW-01> test security-policy-match source 45.33.22.11 destination 10.10.20.80 destination-port 80 protocol 6 from untrust to dmz
Result: deny (implicit deny)
Now test with the public IP to see that the rule incorrectly targets the pre-NAT address:
admin@PA-FW-01> test security-policy-match source 45.33.22.11 destination 203.0.113.20 destination-port 80 protocol 6 from untrust to untrust
Rule: allow-web-server-inbound
action: allow
The rule matches when using the public IP (wrong), but denies when using the internal IP (correct post-NAT address). The rule must be fixed to reference the post-NAT IP and the post-NAT destination zone.
How to Fix
Update the security policy rule to reference the post-NAT internal IP and the correct destination zone:
admin@PA-FW-01# set rulebase security rules allow-web-server-inbound destination 10.10.20.80
admin@PA-FW-01# set rulebase security rules allow-web-server-inbound to dmz
admin@PA-FW-01# commit description "Fix security policy: use post-NAT IP and correct dst zone"
Configuration committed successfully.
Validate the fix:
admin@PA-FW-01> test security-policy-match source 45.33.22.11 destination 10.10.20.80 destination-port 80 protocol 6 from untrust to dmz
Rule: allow-web-server-inbound
action: allow
Root Cause 7: Candidate Configuration Not Committed
Palo Alto firewalls maintain a candidate configuration and a running configuration as two distinct datastores. Any change made in the GUI or CLI is written to the candidate only. Until a commit is executed, the running dataplane continues using the old configuration and new NAT rules have zero effect on traffic.
How to Identify
Check for pending uncommitted changes:
admin@PA-FW-01> show config diff
[edit rulebase nat]
+ rules web-server-dnat-new { ... }
[edit rulebase security]
+ rules allow-web-new { ... }
A non-empty diff confirms the running policy does not include your new rules. The session table and
test nat-policy-matchwill not reflect candidate configuration changes.
How to Fix
admin@PA-FW-01# commit description "Add NAT and security rules for new web server 10.10.20.90 behind 203.0.113.22"
Commit job 155 queued.
...
Configuration committed successfully.
After committing, re-run
test nat-policy-matchto confirm the new rule appears in running policy and matches as expected.
Prevention
Preventing NAT failures on Palo Alto firewalls requires procedural discipline and a solid understanding of how the platform separates NAT evaluation from routing and security policy. Adopt these practices to reduce incidents:
- Always test before and after commit: Run
test nat-policy-match
in candidate mode immediately after adding a rule. Test again after committing to confirm the running policy reflects your intent. - Place specific rules above general rules: Review rule ordering every time a new NAT entry is added. Name your catch-all rule explicitly (e.g., "catch-all-dipp-outbound") so its scope is obvious to any engineer reviewing the policy.
- Maintain an IP allocation record: Keep an authoritative record of all NAT pool IPs, public virtual IPs, and interface IPs to prevent overlap. Never assign a NAT VIP that matches a firewall interface address.
- Verify routes for all post-NAT destination IPs: After adding a destination NAT rule, always run
test routing fib-lookup
for the translated internal IP before testing end-to-end connectivity. - Write security policy using post-NAT IPs: Document and enforce the convention that security policy rules for destination-NATed servers must reference the internal (post-NAT) IP and the internal destination zone, never the public VIP.
- Audit zone assignments after interface changes: Whenever a new sub-interface, VLAN interface, or physical interface is added or reassigned, confirm its zone assignment and audit existing NAT and security rules for impact.
- Use address objects for all NAT IPs: Named address objects make rule intent explicit and prevent typo-induced IP mismatches. Group server IPs in address groups to simplify security policy management.
- Test bi-directional NAT in both directions immediately: After configuring any static bi-directional NAT rule, test both the outbound and inbound directions before closing the change window.
