Networking

MikroTik Hairpin NAT: Trace the Return Path and Fix Local Access

Archived Boxvirt illustration for Understanding NAT & Hairpin NAT in MikroTik
Editorial illustration from the original Boxvirt article.
Editorial draft. Sources and examples have been reviewed against primary documentation; release-specific procedures still require lab validation.
Hairpin NAT: the return path matters. LAN client: Uses a public service name; Router NAT: Translates the destination; LAN server: Reply must traverse the router.
When replies bypass the router, the client sees the wrong source. Compare narrowly scoped NAT with split DNS.Original Boxvirt diagram. Illustrative model, not a product screenshot. View full size ↗

Hairpin NAT lets a LAN client reach an internal service through the router’s public address. The common failure is a split path: the router translates the request, but a server on the same subnet replies directly to the client. The client receives a reply from an address different from the public endpoint it contacted.

Fixing this requires both a matching destination NAT rule and a consistent return path. Adding a generic masquerade rule does not fix DNS pointing at the wrong place or a WAN-only destination rule that never matches LAN traffic.

Describe the topology before changing NAT

This illustrative RouterOS 7 case uses one LAN subnet:

RoleExample
Router LAN interface and addressbridge-lan, 192.168.50.1/24
Client192.168.50.20
HTTPS server192.168.50.100:443
Public service IPv4203.0.113.10 — documentation address, replace it
Service hostnamefiles.example.com — replace it

The client and server share a subnet, and the MikroTik owns or receives forwarded traffic for the real public address. If a reverse proxy, CDN or upstream router terminates the public connection elsewhere, trace that topology instead of assuming this example applies.

Check your client’s actual A and AAAA DNS answers. An IPv6 connection does not use these IPv4 NAT rules. The Subnet Calculator can confirm whether the client and server really belong to the same subnet.

Follow the broken connection

Without source translation, the packet identities can look like this:

StageSourceDestination
Client request192.168.50.20203.0.113.10:443
After destination NAT192.168.50.20192.168.50.100:443
Server’s direct reply on the LAN192.168.50.100:443192.168.50.20

The last packet bypasses the router’s reverse translation. A narrow hairpin source-NAT rule makes the server reply to 192.168.50.1 instead, returning through the device that owns the translation state. That also means the server sees the router’s source address for these connections.

When the server already replies through this router because it lives on a different routed subnet, that same source translation may be unnecessary. Confirm the route rather than adding it everywhere.

Check existing rules and keep a rollback path

Use the safe-change workflow and inspect current NAT and forward rules:

/ip firewall nat print detail
/ip firewall nat print stats
/ip firewall filter print stats where chain=forward
/ip route print

A destination rule with in-interface-list=WAN normally excludes a LAN client’s request. Leave the external publishing policy intact; a separate narrow LAN candidate can handle reflection without broadening every port forward.

Create disabled LAN-only candidates

Replace every example address and interface first. The rules begin disabled; review order, put them before any conflicting NAT rule and enable individually during a controlled test.

/ip firewall nat add chain=dstnat action=dst-nat src-address=192.168.50.0/24 dst-address=203.0.113.10 protocol=tcp dst-port=443 to-addresses=192.168.50.100 to-ports=443 disabled=yes comment="Boxvirt hairpin HTTPS destination"
/ip firewall nat add chain=srcnat action=src-nat src-address=192.168.50.0/24 dst-address=192.168.50.100 out-interface=bridge-lan protocol=tcp dst-port=443 to-addresses=192.168.50.1 disabled=yes comment="Boxvirt hairpin HTTPS return path"

The explicit public destination prevents the first rule from redirecting unrelated internet HTTPS browsing. The second uses the router’s fixed LAN address and a narrow client/server/service match. Do not substitute an arbitrary interface address if it is not reachable by the server.

This example is for LAN access only. It does not create a WAN publishing rule. The forward policy must also allow the intended client-to-server service; NAT does not replace the access decision.

Test hostname, translation and application separately

  1. Confirm direct server health from an authorised client.
  2. Resolve the hostname on the LAN client and verify the intended public IPv4 answer.
  3. Open a fresh connection to the hostname and inspect NAT counters.
  4. Check that the server’s connection log sees the expected translated client address.
  5. Repeat from outside the LAN to confirm external access was preserved.

On a client with curl, this diagnostic preserves the real hostname and certificate validation while overriding DNS for one request:

curl --connect-timeout 5 --resolve files.example.com:443:203.0.113.10 -I https://files.example.com/

Replace both placeholders. Do not treat curl -k as the permanent fix for a certificate mismatch. A correct NAT path cannot make an unrelated TLS certificate valid for your hostname.

NAT decisions are associated with tracked connections, so counters do not necessarily increase for every packet. A browser may reuse an existing connection after a rule change. Use a fresh client connection and avoid clearing the whole connection table on an active router.

Consider split DNS before keeping reflection

Internal DNS can return 192.168.50.100 for files.example.com, letting local clients reach the service directly with the same hostname and certificate. This can avoid hairpin source translation and preserve client addresses in server logs.

It still needs working internal DNS, an appropriate route and firewall policy. Clients using another resolver or encrypted DNS may receive the public answer instead, and off-site clients need the public record. Test each supported client path deliberately.

To roll back the candidates, disable the two exact Boxvirt hairpin HTTPS rules and test a fresh connection. Retain the earlier external port forward and restore any internal DNS change separately if it was part of your experiment.

Frequently asked questions

Why does the service work on mobile data but fail on Wi-Fi?

Those tests use different ingress paths and possibly different DNS answers. Check that LAN requests match destination NAT, then check the same-subnet server return path. External success proves neither condition.

Is masquerade always required for hairpin NAT?

No. The same-subnet example needs a way to send replies through the translating router, but a routed topology may already do that. Explicit source NAT to a fixed LAN address is one option; masquerade is not a universal prerequisite.

Will hairpin NAT fix a Cloudflare-proxied hostname?

Not necessarily. A proxied answer points clients at Cloudflare rather than directly at your router’s public address, so the connection follows a different path. Diagnose proxy routing, origin connectivity and TLS before adding reflection rules.

Does hairpin NAT apply to IPv6 in the same way?

This guide covers IPv4 NAT. For a globally routed IPv6 service, diagnose DNS, routing and IPv6 firewall policy directly instead of copying the IPv4 translation recipe.

Sources

KEEP EXPLORING

Related guides