Static NAT is the rule that lets the rest of the internet reach a server you own. The classic case is a public-facing web server in the DMZ: it lives on a private IP, but customers reach it via a public IP, and the ASA quietly rewrites the destination on every inbound packet so neither side has to care.
This article walks the configuration end to end on Cisco ASA software 9.x, with real captures from the PingLabz reference lab. For the cluster context, see the Cisco ASA pillar; for the broader NAT picture, see Cisco ASA NAT Explained; for the matching outbound rule, see Cisco ASA Dynamic PAT for Internet Access.
What Static NAT Does (and Why It Matters)
Static NAT is a fixed, bidirectional translation between one real IP and one mapped IP. Unlike dynamic PAT, it does not need a flow to start before a translation exists: the binding is in place the moment the rule is configured, in both directions.
That property is what makes it the right tool for inbound traffic. When a customer types a URL that resolves to your public IP, that connection arrives at the ASA from the outside. Without a pre-existing static translation, the ASA has no idea who that destination IP belongs to on the inside. Static NAT puts the binding on the books before the packet shows up.
The ASA also installs a proxy ARP entry on the mapped interface (outside, in our case) so the ASA itself answers ARP requests for the public IP. That is why you do not have to physically configure 198.51.100.10 anywhere - the ASA is the answering machine for that IP, and it forwards the traffic inward.
The Lab Topology
All output below is from a live ASAv running Cisco ASA 9.23(1) with three interfaces:
- inside (G0/0): 10.10.0.254/24, security-level 100. Inside subnets behind it.
- dmz (G0/2): 192.168.50.1/24, security-level 50. The DMZ web server lives at 192.168.50.10.
- outside (G0/1): 203.0.113.2/30, security-level 0. Public IP space 198.51.100.0/24 is routed to this ASA from upstream.
The customer-facing public IP for the DMZ web server is 198.51.100.10. We never touch that address on the server itself; the ASA is the only place it lives.
Configuration: Static NAT in One Object
Like dynamic PAT, the modern way to write static NAT is object NAT. Define a network object for the real IP, attach the NAT rule to it.
object network DMZ-WEB
host 192.168.50.10
nat (dmz,outside) static 198.51.100.10Three things to read off this rule. First, host 192.168.50.10 is the real IP, on the real (DMZ) side. Second, (dmz,outside) is the interface pair: real interface first, mapped interface second. Third, static 198.51.100.10 is the mapped IP that outside hosts will use.
That is the entire NAT configuration. The ASA now answers ARP for 198.51.100.10 on the outside, untranslates inbound packets to 192.168.50.10, and translates the return traffic from the server back to 198.51.100.10.
You Still Need an ACL
Static NAT does not implicitly permit traffic. The ASA's default behavior is to deny inbound (low-security to high-security) connections, and security-level 0 to security-level 50 is exactly that direction. You need an interface ACL on outside that permits the inbound web traffic.
access-list OUTSIDE_IN extended permit tcp any object DMZ-WEB eq www
access-list OUTSIDE_IN extended permit tcp any object DMZ-WEB eq https
access-list OUTSIDE_IN extended permit icmp any object DMZ-WEB
access-group OUTSIDE_IN in interface outsideTwo important details. First, the ACL references the real address of the server (the object DMZ-WEB resolves to 192.168.50.10), not the mapped one. This was the opposite on ASA software before 8.3, which is why old documentation can confuse you. On modern ASA, ACLs always use real IPs.
Second, we permit only the ports the server is supposed to expose. Anything else gets dropped at the implicit deny at the bottom of the ACL. For the deeper troubleshooting walk on this, see Cisco ASA ACL Troubleshooting with packet-tracer.
Verifying the Configuration
The running config NAT block shows the rule lives in Section 2 (Auto NAT) along with the outbound PAT rules:
ASA-PERIM# show running-config nat
nat (inside,outside) source static INSIDE-NET-FULL INSIDE-NET-FULL destination static REMOTE-VPN-NET REMOTE-VPN-NET no-proxy-arp route-lookup
nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET
!
object network INSIDE-NET
nat (inside,outside) dynamic interface
object network INSIDE-TRANSIT
nat (inside,outside) dynamic interface
object network DMZ-WEB
nat (dmz,outside) static 198.51.100.10The xlate table shows the static binding always exists, even with no traffic. Notice the flags s (static) and the long idle time:
ASA-PERIM# show xlate
1 in use, 5 most used
Flags: D - DNS, e - extended, I - identity, i - dynamic, r - portmap,
s - static, T - twice, N - net-to-net
NAT from dmz:192.168.50.10 to outside:198.51.100.10
flags s idle 0:28:35 timeout 0:00:00Idle time keeps climbing without ever timing out (timeout is 0:00:00, meaning never). That is the static behavior: the binding is permanent until the rule is removed.
show nat detail shows the rule with both translate and untranslate counters. For an inbound-published server the meaningful number is untranslate_hits - that counts inbound packets matched against this rule:
ASA-PERIM# show nat detail
Auto NAT Policies (Section 2)
1 (dmz) to (outside) source static DMZ-WEB 198.51.100.10
translate_hits = 0, untranslate_hits = 7
Source - Origin: 192.168.50.10/32, Translated: 198.51.100.10/32
2 (inside) to (outside) source dynamic INSIDE-TRANSIT interface
translate_hits = 0, untranslate_hits = 0
3 (inside) to (outside) source dynamic INSIDE-NET interface
translate_hits = 46, untranslate_hits = 0untranslate_hits = 7 means the ASA has seen 7 packets arrive on outside destined for 198.51.100.10 and rewritten the destination to 192.168.50.10. translate_hits = 0 means the server itself has not initiated outbound traffic that matched this rule (the inside hosts use the PAT rules instead).
Walking an Inbound Packet with packet-tracer
The cleanest demonstration of how static NAT works is to send packet-tracer a hypothetical inbound HTTPS connection and read every phase:
ASA-PERIM# packet-tracer input outside tcp 198.51.100.50 51001 198.51.100.10 443
Phase: 1
Type: UN-NAT
Subtype: static
Result: ALLOW
Config:
object network DMZ-WEB
nat (dmz,outside) static 198.51.100.10
Additional Information:
NAT divert to egress interface dmz
Untranslate 198.51.100.10/443 to 192.168.50.10/443
Phase: 2
Type: ACCESS-LIST
Subtype:
Result: ALLOW
Config:
access-group OUTSIDE_IN in interface outside
Phase: 3
Type: NAT
Subtype: per-session
Result: ALLOW
Phase: 6
Type: NAT
Subtype: rpf-check
Result: ALLOW
Config:
object network DMZ-WEB
nat (dmz,outside) static 198.51.100.10
Phase: 10
Type: FLOW-CREATION
Subtype:
Result: ALLOW
New flow created with id 54, packet dispatched to next module
Phase: 11
Type: INPUT-ROUTE-LOOKUP-FROM-OUTPUT-ROUTE-LOOKUP
Subtype: Resolve Preferred Egress interface
Result: ALLOW
Found next-hop 192.168.50.10 using egress ifc dmzRead the phases in order:
- Phase 1 (UN-NAT static) is the heart of the trace. The ASA matches the destination 198.51.100.10 against the DMZ-WEB static rule and rewrites the destination to 192.168.50.10. It also "diverts" the packet to the DMZ egress interface so subsequent phases happen against the post-NAT destination.
- Phase 2 (ACCESS-LIST) permits the packet using the OUTSIDE_IN ACL. Note the ACL is matching against the real address 192.168.50.10 (via the DMZ-WEB object), which is exactly why we wrote it that way.
- Phase 6 (NAT rpf-check) confirms the reverse-path: the ASA verifies that an answer from the server can map back to the same NAT rule. This is the protection that prevents a return packet from leaking out the wrong interface.
- Phase 10 (FLOW-CREATION) creates the conn entry. From here on, the ASA short-circuits the slow path: subsequent packets in this flow will be processed by the existing conn, not re-evaluated against every rule.
- Phase 11 is the route lookup that decides which next-hop forwards the packet to the server.
If any phase had failed, packet-tracer would stop there and tell you why. That is the value of the tool: every potential failure point on the platform is named. For the full guide, see Cisco ASA packet-tracer Command.
Static PAT (Port Forwarding)
The static NAT above maps an entire IP. If you only want to forward one port (the classic "publish only TCP/443") you can write static PAT, sometimes called port forwarding:
object network DMZ-WEB-HTTPS
host 192.168.50.10
nat (dmz,outside) static interface service tcp 443 443That rule says: when a packet hits the outside interface IP on TCP/443, untranslate it to 192.168.50.10 on TCP/443. The mapped address is the outside interface itself (no extra public IP burned), and only the named port comes through. Non-443 traffic to the same outside IP is unaffected and continues to hit whatever else the ASA is doing on that address.
Static PAT is the right pattern when you have one public IP and several DMZ servers behind it, each on a different port. It is also the pattern you reach for in lab environments where extra public IPs are not available.
Failure Modes You Will Actually See
Top-of-list problems with inbound static NAT, ranked by frequency:
- ACL not in place or wrong direction. NAT works, packet hits the implicit deny on outside, gets dropped. Symptom:
untranslate_hitsincrements but the conn count does not. Fix: check the ACL withshow access-list; check thataccess-group ... in interface outsideis applied. - ACL referencing the mapped IP instead of the real IP. Common when retrofitting old ASA configs. The ACL never matches. Fix: rewrite the ACL using
object DMZ-WEB(which is the real IP) or the literal real IP 192.168.50.10. - Server is not actually on the DMZ subnet. The ARP-from-DMZ on the ASA fails, packet-tracer ends with "no-v4-adjacency". Fix: check
show arp; make sure the server is up and on the right VLAN. - Upstream routing not pointing the public range at the ASA. Customer can not reach 198.51.100.10 because the ISP does not route that block to this ASA's outside IP. The ASA is fine; the path to the ASA is broken. Fix: confirm with the upstream provider; check from a host on the ISP side.
- Conflicting NAT rule. A second NAT rule earlier in the table also matches. Fix:
show nat detailtells you which rule the ASA chose, and the order is Section 1, Section 2, Section 3. See NAT Order of Operations.
Where to Go Next
- Cisco ASA Dynamic PAT for Internet Access for the outbound counterpart.
- Cisco ASA Twice NAT Explained when source and destination both need translating.
- Cisco ASA ACL Configuration for the inbound permit rules and object groups.
- Cisco ASA Security Levels for why outside-to-DMZ needs an explicit permit in the first place.
- Cisco ASA packet-tracer Command for the full diagnostic walk.
- Cisco ASA pillar for the rest of the cluster.
Key Takeaways
- Static NAT is the inbound-publication tool: a fixed bidirectional binding between one real IP and one mapped IP, present from the moment the rule is configured.
- Object NAT keeps the configuration to two lines: a network object and a nested
nat ... staticrule. It lands in Section 2 of the NAT table. - An interface ACL on outside is still required. The ACL references the real IP, not the mapped IP, on modern ASA software.
- Use
show xlateto confirm the binding,show nat detailfor the hit counters, and packet-tracer with the public IP and port to confirm an inbound packet would land on the right server. - Static PAT (port forwarding) is the same idea limited to a single port - useful when you have one public IP and many servers.