In this lesson, you will learn how to configure your Linux gateway so that internal clients can reach local services using the same public IP address as external users.
The Problem: The "Hairpin" Dilemma
Imagine your server resides on your internal network at 192.168.1.10, and you have a port forwarding rule mapping your router's public IP (203.0.113.1:80) to that server. When an external user connects, the router performs Destination NAT (DNAT) to swap the destination to the internal IP.
However, when a device on the same internal network tries to reach 203.0.113.1:80, a routing issue occurs. The server receives the packet from the local client, sees the original source IP of that client, and tries to reply directly to the client rather than routing back through the gateway. Since the client didn't expect a direct response from the server (it expected one from the public IP), it drops the connection. This is where Hairpin NAT (or NAT Loopback) comes in.
The Solution: Source NAT on the Internal Interface
To fix this, we must instruct the router to perform Source NAT (SNAT) on packets that are destined for the internal server but originate from the internal network. This makes the packet appear to the server as if it originated from the router itself. Because the server replies back to the router, the router can reverse the NAT process and pass the packet back to the client correctly.
Using nftables, you can implement this with a rule in the postrouting chain:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
# Hairpin NAT: If traffic hits the internal interface and
# is destined for our internal server, masquerade it.
iifname "eth0" oifname "eth0" ip daddr 192.168.1.10 tcp dport 80 masquerade
}
}
By adding this rule, the gateway forces the server to see the router as the source, ensuring the reply path is symmetric and the connection succeeds.
Key Takeaway: Hairpin NAT solves internal accessibility issues by applying Source NAT to traffic looped back through the gateway, ensuring the server’s response always traverses the router.