perkun.eu Services Portfolio Blog About Contact PL
← Blog

5/14/2024

How hairpin NAT works in MikroTik and why it matters

TL;DR: Hairpin NAT allows computers on the LAN to reach the public IP and hit a server on the same network. Without it, curl https://yourdomain.com from the LAN doesn’t work — even though the site is accessible from the outside.

This is one of those networking problems that can waste hours before you realize the site “isn’t working” only for devices on your own home network. External users don’t report any issue. Your local developer machine can’t reach production via the public domain. The fix: a few lines of MikroTik configuration.

The problem without hairpin NAT

Scenario: QNAP server at 192.168.10.200, domain my-site.com pointing to the router’s public IP 1.2.3.4. Port forwarding on MikroTik redirects port 443 from 1.2.3.4 to 192.168.10.200:443.

Connection from outside: a client sends a packet to 1.2.3.4:443 → the router sees the dst-nat rule → redirects to 192.168.10.200:443 → works.

Connection from LAN: a computer at 192.168.10.50 sends a packet to 1.2.3.4:443. The router (MikroTik) receives the packet on the LAN interface with the public IP as the destination. It checks the NAT table — the dst-nat rule applies to in-interface=ether1-wan, so it doesn’t match a packet coming from the LAN. The packet goes directly to the router’s public interface. The router doesn’t know to “bounce” it back into the LAN. The packet is dropped or gets a response from the router instead of the server.

Result: timeout, “connection refused,” or an SSL error. The site works for the entire world but not for your own computer when you’re testing.

Solution: dst-nat + src-nat (masquerade)

Hairpin NAT = two NAT rules. The first redirects packets from the LAN to the server in the LAN (as if they were coming from outside). The second rewrites the source address so the response comes back through the router rather than going directly to the client (which wouldn’t work, because the client doesn’t know about the redirect).

MikroTik configuration

# Rule 1: DST-NAT for traffic from LAN
/ip firewall nat add \
  chain=dstnat \
  src-address=192.168.10.0/24 \
  dst-address=1.2.3.4 \
  protocol=tcp \
  dst-port=443 \
  action=dst-nat \
  to-addresses=192.168.10.200 \
  to-ports=443 \
  comment="Hairpin NAT HTTPS - LAN to LAN"

# Rule 2: SRC-NAT (masquerade) for LAN -> LAN traffic through router
/ip firewall nat add \
  chain=srcnat \
  src-address=192.168.10.0/24 \
  dst-address=192.168.10.200 \
  protocol=tcp \
  dst-port=443 \
  action=masquerade \
  comment="Hairpin NAT masquerade - LAN hairpin"

The masquerade rule causes the packet, on its way to the server, to change its source address from 192.168.10.50 (client) to the router’s LAN address (192.168.10.1). The server replies to the router, the router knows where to forward the reply to the client. The connection works.

Rule order in the NAT table matters. Hairpin rules should come before the general WAN masquerade rules, but the order between dst-nat and src-nat doesn’t matter (they operate on different chains).

Testing

How to verify that hairpin NAT is working:

# From a computer on the LAN (192.168.10.50):
curl -v https://my-site.com

# Expected: connection to 192.168.10.200:443 via routing
# Bad result: connection timeout or "connection refused"

To confirm that the hairpin rules are being hit, check the counters in RouterOS:

/ip firewall nat print stats where comment~"Hairpin"

The counters should increment with every request from the LAN to the public domain. If they stay at zero — the rule isn’t matching (check src-address, dst-address, protocol, port).

Alternative: DNS split-horizon

Instead of hairpin NAT, you can configure a local DNS that returns the private IP 192.168.10.200 for my-site.com instead of the public 1.2.3.4. LAN devices reach the server directly without going through the router and NAT.

Pros: simpler, works for all ports/protocols, less overhead. Cons: requires your own DNS server on the network (Pi-hole with a custom DNS entry, or Unbound, or a static entry in the MikroTik DNS). Each new domain requires a manual entry. If you have many domains and subdomains, hairpin NAT is easier to maintain.

MikroTik has a built-in DNS server with support for static entries:

/ip dns static add name=my-site.com address=192.168.10.200

This is the simpler solution for a single domain. For complex infrastructure with many services — hairpin NAT scales better.

Summary

Hairpin NAT is a required configuration when you’re hosting something locally and want to use a public domain from both outside and inside the LAN. Two NAT rules in MikroTik RouterOS solve the problem definitively. The alternative is split-horizon DNS — simpler for individual domains, but less automatic with complex infrastructure.