You made a port forward to a NAS, a camera recorder or a server. From mobile data it works: you type the public address or your own domain name and you are in. On your own wifi the same name stalls. Sometimes it takes a long time and nothing happens, sometimes an error comes back at once.
What it is not: the port forward itself is fine. You just proved that from outside. It is also not a firewall rule that is too strict, because then it would not work from outside either. What goes wrong here happens on the router, in the NAT table, with traffic that has to go in and out on the same side. That is hairpin NAT, also called NAT loopback.
The reason is geometry, not security. Your laptop on 192.168.88.20 sends a packet to the public address. The router rewrites the destination to 192.168.88.10, the NAS. That answers 192.168.88.20 directly, without the router. Your laptop receives a reply from 192.168.88.10 while it was waiting on the public address, and drops it. The fix is that the router also replaces the source address.
The quick checks, in order
- Does it work from outside? Put your phone on mobile data and try. A good answer: you get in. If not, this is not your chapter, see A port forward does nothing.
- Are there hairpin rules at all?
/ip firewall nat print stats. A good answer: next to the ordinary dstnat rule within-interface-list=WANthere are two rules with hairpin in the comment, a dstnat withdst-address-type=local in-interface-list=LANand a srcnat masquerade. If they are not there, that is the whole problem. - Do the counters move? Try again from your laptop and look once more. A good answer: the counter on the hairpin dstnat climbs. If it stays at zero, your packet never reaches that rule.
- Does the router hold the public address itself?
/ip address printon the WAN interface. A good answer: a real public address. If you see 100.64.x.x, 10.x.x.x or 192.168.x.x, there is a modem or your provider in front of you, and the public address does not exist on this router. - What address does your laptop actually get? From the router:
:put [:resolve nas.example.com]. The public address means the traffic makes the whole trip. The internal address means split DNS is already in place and you need nothing from NAT.
The usual causes, most common first
There is no hairpin rule at all
A bare port forward in RouterOS matches on in-interface-list=WAN. Traffic from your own LAN never passes there. This is the default situation on a router you set up by hand. The cure is the pair of rules described below: a second dstnat for traffic from the LAN, plus a masquerade so the reply comes back the right way.
You are in a VLAN, the masquerade is not
The dstnat rule matches the whole LAN list, VLANs included. The masquerade matches one subnet. If your laptop sits in a VLAN and the masquerade covers the main network, the destination gets rewritten and the source does not, and the connection hangs in exactly the way described above. This is the case that stays unsolved longest, because it works from one device and not from another.
The public address is not on the router
If a modem in router mode sits in front of your MikroTik, or your provider puts you behind CGNAT, the public address belongs to another device. A hairpin rule that matches on "an address of this router" then finds nothing, because the public address is not one. See Double NAT.
The service itself refuses
A NAS with a strict list of allowed networks, or an application that clings to the public hostname, gives the same picture while the router is doing its job.
What the configurator does about it
As soon as you add one port forward in the Firewall & NAT section, the Hairpin NAT toggle appears, and it is on. Per forward the script then writes three rules instead of one: the ordinary dstnat from WAN, a second dstnat with dst-address-type=local and in-interface-list=LAN, and a srcnat masquerade for traffic from the LAN subnet to the internal address and internal port. All three carry a comment, so you can recognise them in /ip firewall nat print.
Be clear about the limit: that masquerade uses the subnet from the LAN section, and only that one. If you have VLANs and the device you work from sits in a VLAN, the generated rule does not cover you. Add a second masquerade yourself with that VLAN subnet as src-address, or solve it with DNS instead of NAT.
That second route is the nicer one, and the tool supports it: the DNS section has Static DNS records. Put your public hostname there with the internal address next to it, and internal traffic never reaches the NAT table, so hairpin stops mattering. Outside your network the public name keeps pointing at the public address. See DNS.
One side effect of dst-address-type=local is worth knowing: it matches any address of the router, the LAN address you open WebFig on included. Forward port 443 to a server while HTTPS on the router itself also listens on 443, and the hairpin rule takes that traffic too. Move the HTTPS port in Management access if that happens.
Where the cause lies outside your router
- Your client's DNS. A phone using DNS over HTTPS bypasses the router's static records. It gets the public address back and you are on the hairpin path after all.
- A modem doing NAT of its own. Put it in bridge mode, then your MikroTik holds the public address and everything here works.
Read on: Port forwarding, Firewall & NAT and DNS.