Consider this common network configuration:
[Internet]
|
[Router/NAT] (Public IP: 203.0.113.42)
|
[Local Network]
├── [Web Server] (192.168.1.100:80)
└── [Client PC] (192.168.1.50)
When your client (192.168.1.50) tries to access your public IP (203.0.113.42), the router sees this as:
- Outbound packet: SRC=192.168.1.50, DST=203.0.113.42
- Inbound packet: SRC=203.0.113.42, DST=192.168.1.50
Most consumer routers won't recognize this as loopback traffic and either drop it or fail to route it properly.
pfSense Configuration
Add these NAT rules:
# Enable NAT reflection
nat on em0 inet from (em1:network) to em0 port 80 -> em0 port 80
# Alternative using pfSense GUI:
1. Firewall > NAT > Port Forward
2. Edit your existing rule
3. Enable "NAT Reflection" option
MikroTik RouterOS
/ip firewall nat
add chain=dstnat action=dst-nat to-addresses=192.168.1.100 \
to-ports=80 protocol=tcp dst-address=203.0.113.42 dst-port=80
add chain=srcnat action=masquerade src-address=192.168.1.0/24 \
dst-address=192.168.1.100 protocol=tcp dst-port=80
For environments where NAT loopback isn't supported:
# Internal DNS records:
webserver.local. IN A 192.168.1.100
# External DNS records:
webserver.example.com. IN A 203.0.113.42
Use these troubleshooting commands:
# Check port forwarding
nc -zv 203.0.113.42 80
# Trace NAT path
tcpdump -i eth0 port 80
# Verify DNS resolution
dig +short webserver.example.com
When implementing hairpin NAT:
- Ensure proper firewall rules are in place
- Monitor for unusual traffic patterns
- Consider rate limiting internal->external->internal traffic
Imagine you've set up port forwarding on your router to expose internal services (like a web server on 192.168.1.100:80) to the public internet via your public IP 203.0.113.5. Your DNS records point to this public IP. Here's what happens:
External Client:
1. DNS lookup → 203.0.113.5
2. Connects to 203.0.113.5:80 → Router forwards to 192.168.1.100:80 (works)
Internal Client:
1. DNS lookup → 203.0.113.5 (same public IP)
2. Attempts to connect to 203.0.113.5:80
→ Router sees source/dest on same interface
→ Typically drops the packet (fails)
Most consumer-grade routers don't implement Hairpin NAT (also called NAT loopback) by default due to:
- Security concerns about internal traffic appearing to come from WAN
- Additional NAT processing overhead
- Historical implementations treating LAN→WAN→LAN as an error state
For professional setups, here are configuration examples:
1. pfSense Configuration
# Enable NAT reflection in pfSense:
# System → Advanced → Firewall & NAT →
# ✔ Enable NAT Reflection for 1:1 NAT
# ✔ Enable Automatic outbound NAT for Reflection
2. Linux iptables Solution
# Enable NAT loopback for HTTP (adjust interfaces as needed):
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 192.168.1.100
iptables -t nat -A POSTROUTING -o eth1 -p tcp --dport 80 -d 192.168.1.100 -j SNAT --to 192.168.1.1
Split Horizon DNS
Configure internal DNS to return private IPs:
# Example BIND zone configuration:
@ IN A 192.168.1.100 ; Internal clients
@ IN A 203.0.113.5 ; External clients
Proxy Server Solution
For complex setups, consider a reverse proxy:
# Nginx configuration example:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.100;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Ubiquiti EdgeRouter
configure
set service nat rule 5010 description "Hairpin NAT for Web Server"
set service nat rule 5010 type destination
set service nat rule 5010 inbound-interface eth0
set service nat rule 5010 protocol tcp
set service nat rule 5010 destination port 80
set service nat rule 5010 inside-address address 192.168.1.100
set service nat rule 5010 inside-address port 80
commit
save