When configuring a network with port forwarding (DNAT) for external access to internal servers, a common issue arises when trying to access these same services via the public IP from within the local network. This is known as the "NAT loopback" or "hairpin NAT" problem.
The core issue occurs because:
- Internal client sends request to public IP
- Router's NAT rules don't properly redirect internal traffic
- Traffic either gets dropped or fails to route back to internal network
For OpenWRT routers with iptables, we need to add specific rules to handle internal traffic:
# Add to /etc/firewall.user
iptables -t nat -A PREROUTING -i br-lan -d PUBLIC_IP -p tcp --dport 80 -j DNAT --to 192.168.2.10
iptables -t nat -A POSTROUTING -o br-lan -s 192.168.2.0/24 -d 192.168.2.10 -j MASQUERADE
For those preferring DNS solutions, dnsmasq can be configured to override certain domains:
# Add to /etc/dnsmasq.conf
address=/yourdomain.com/192.168.2.10
address=/subdomain.yourdomain.com/192.168.2.10
After implementing changes, verify with:
iptables -t nat -L -n -v
ping yourdomain.com # Should resolve to internal IP
curl -I http://yourdomain.com # Should return server headers
The NAT-based solution adds minimal overhead since:
- Rules are processed in kernel space
- Only affects traffic destined for specific IPs
- Doesn't impact external-to-internal traffic flow
For more complex setups consider:
- Split DNS configuration
- Proxy server on the router
- Network namespace isolation
When configuring public-facing services on a local network, many administrators encounter the classic "hairpin NAT" problem. The scenario:
- External requests via public IP (DNAT) work perfectly
- Internal requests to the same public IP mysteriously fail
- DNS resolves correctly, but routing breaks within LAN
The root cause lies in how the router handles packets when both source and destination are within the same network segment. In your OpenWRT setup with this iptables rule:
-A PREROUTING -i ppp0 -p tcp -m multiport --dports 22,25,80,443 -j DNAT --to-destination 192.168.2.10
The key limitation is that it only processes incoming traffic on ppp0, completely ignoring internal requests coming from br-lan.
Here's the complete iptables implementation for OpenWRT that solves this:
# Enable NAT reflection
iptables -t nat -A PREROUTING -d PUBLIC_IP -j DNAT --to-destination 192.168.2.10
iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -d 192.168.2.10 -j MASQUERADE
# For specific ports (recommended for security)
iptables -t nat -A PREROUTING -i br-lan -p tcp --dport 80 -d PUBLIC_IP \
-j DNAT --to-destination 192.168.2.10:80
iptables -t nat -A POSTROUTING -o br-lan -p tcp --dport 80 -s 192.168.2.0/24 \
-d 192.168.2.10 -j MASQUERADE
For environments with many domains/subdomains (as mentioned in your case), consider DNS-level resolution:
# In dnsmasq.conf (OpenWRT default DNS)
address=/yourdomain.com/192.168.2.10
address=/.subdomain.yourdomain.com/192.168.2.10
# For multiple domains
address=/domain1.com/192.168.2.10
address=/domain2.net/192.168.2.10
The most robust solution combines NAT reflection with DNS manipulation:
# 1. Configure dnsmasq wildcards (OpenWRT specific)
echo "address=/#.example.com/192.168.2.10" >> /etc/dnsmasq.conf
/etc/init.d/dnsmasq restart
# 2. Set up generic reflection NAT
iptables -t nat -N REFLECT
iptables -t nat -A REFLECT -j DNAT --to-destination 192.168.2.10
iptables -t nat -A PREROUTING -d PUBLIC_IP -j REFLECT
iptables -t nat -A OUTPUT -d PUBLIC_IP -j REFLECT
After implementation, verify with these commands:
# Check NAT rules
iptables -t nat -L -v -n
# Test DNS resolution from LAN
nslookup example.com 192.168.2.1
# Verify packet flow
tcpdump -i br-lan host 192.168.2.10 and port 80