Hairpin NAT - the easy way

Decided to write a simple guide on Hairpin NAT, because quite a lot of users struggle to understand how to set it up.

I am not a networking professional and I am open to any criticism on how to implement it in a better way.

Official wiki page by Mikrotik regarding Hairpin NAT: https://wiki.mikrotik.com/wiki/Hairpin_NAT

Step 1 - add LANs to “address-list”

List all your LANs like this:

/ip firewall address-list add address=192.168.10.0/24 comment=Management list=LANs
/ip firewall address-list add address=192.168.11.0/24 comment=Work list=LANs
/ip firewall address-list add address=192.168.12.0/24 comment=Security list=LANs
/ip firewall address-list add address=192.168.13.0/24 comment=Home list=LANs
/ip firewall address-list add address=192.168.14.0/24 comment=Guest list=LANs

Step 2 - add WANs to “address-list”

If you have a single dynamic IP - add your “/ip cloud” domain to address-list named “WANs” and Mikrotik will automatically resolve it to IP. Using custom script in “/ip dhcp-client” is another option in order to keep WAN IP address in address-list updated.
If you have multiple WANs - it gets a little more complicated. I’ve written a simple solution for multiple dynamic WANs here: http://forum.mikrotik.com/t/hairpin-with-2-wan/145618/1

List all your WANs like this:

/ip firewall address-list add address=123.123.123.123 list=WANs

Step 3 - mark connections from LANs to WANs

Use this rule:

/ip firewall mangle add action=mark-connection chain=prerouting comment="Mark connections for hairpin NAT" dst-address-list=WANs new-connection-mark="Hairpin NAT" passthrough=yes src-address-list=LANs

Step 4 - perform Hairpin NAT

Use this rule, placed before any other NAT rule:

/ip firewall nat add action=masquerade chain=srcnat comment="Hairpin NAT" connection-mark="Hairpin NAT" place-before=0

Step 5 - port forwarding

Setup port-forwarding like this:

/ip firewall nat add action=dst-nat chain=dstnat comment="Port forward: something1" dst-address-list=WANs dst-port=5001 protocol=tcp to-addresses=192.168.0.8 to-ports=5001
/ip firewall nat add action=dst-nat chain=dstnat comment="Port forward: something2" dst-address-list=WANs dst-port=5002 protocol=tcp to-addresses=192.168.0.9 to-ports=5002

Thanks for the tutorial, but I have done it on different approach, so I don’t need to do hairpin nat. My config is like this:


/ip firewall nat add action=dst-nat chain=dstnat comment="Port forward" dst-port=5001 protocol=tcp dst-address-type=local dst-address-list=!router to-addresses=192.168.0.8 to-ports=5001

Whereas the address list for “router” is your router gateway addresses on /ip address. With this setup you don’t need to have a hairpin nat rule.

There are several ways to handle hairpin nat.
Understand hairpin nat is a situation where the admin wants local users, ON THE SAMELAN subnet as the server, to access the server NOT by lanip address but by the routers public IP address.

An easy work around for this problem (often called loopback on other devices) is simply to put the server on its own subnet. {solved}.
The regular rules for destination NAT will work fine.

Why have convoluted config like that?

A single NAT rule is capable of doing hairpin NAT efficiently.

I prefer to intercept all DNS request (or use for default the DNS on the Routerboard) for “www.mypublicinternalserver.net” and reply with directly the internal IP.
Also where direct public IP are used, are changed with private IP.
Done, no NAT problem.

My network, my rules…

No so simple darknate.
Its true that one needs to add a single sourcenat rule as your link describes at the top of the source nat chain,
but the not necessarily so for the associated DSTNAT rule.
The dstnat rule depends upon if the ISP connection is a static WANIP or a dynamic WANIP.
Quite correct if its a static WANIP then only the source nat rule is required, but in a dynamic WAN IP scenario there are at least 3 options..

And thus I prefer this link to point readers too. (no bias)
http://forum.mikrotik.com/t/forward-external-ip-address-of-router-port-22-to-internal-machine/148943/3

What I would like rextended to do is expand upon his method as its not so obvious for us mere peons learning RoS!

For those examples please ignore grammar errors and any consideration about security or limitating access.

Example 1)
Internal webserver is reachable worldwide from www.vattelappesca.rex
all the Public DNS resolve www.vattelappesca.rex to Public IP 123.45.67.89
That IP is not on RouterBOARD directly but is >INSIDE< one of RouterBOARD internal network.
NAT is not needed because are Public IP.
Reachable from Inside any local network without NAT: YES
No NAT required for that.

Example 2)
Internal webserver is reachable worldwide from www.vattelappesca.rex
all the Public DNS resolve www.vattelappesca.rex to Public IP 123.45.67.89
that IP is one IP on RouterBOARD and is redirected by dst-nat to one of RouterBOARD internal network (for example 192.168.68.92).
dst-NAT is required because is a Private IP and is not routerd on public network.
Reachable from Inside any local network using DNS or Public IP without src-NAT: NO,
is not the time to explain again why not, but src-NAT is needed for make webserver reply on correct way to the device.
Reachable from Inside any local network using ONLY Private IP without NAT: YES.

Example 3)
Internal webserver is reachable worldwide from www.vattelappesca.rex
all the Public DNS resolve www.vattelappesca.rex to Public IP 123.45.67.89
that IP is one IP on RouterBOARD and are redirected by dst-nat to one of RouterBOARD internal network (for example 192.168.68.92).
dst-NAT is required because is a Private IP and is not routerd on public network.
BUT
Internal webserver is reachable locally from www.vattelappesca.rex without NAT
because on LOCAL DNS the domain resolve to 192.168.68.92
Reachable from Inside any local network without NAT: YES
If is my network, already I set on all machine with DHCP, or manually, all DNS to be solved directly from RouterBOARD,
and adding static regex dns entry “(^|www.)vattelappesca.rex$” ==> 192.168.68.92 solve the problem without using NAT
And SSL certificate do not give any error because check the domain, not the IP

Ahh you twigged a memory, there was a way of directing requesting internally via DNS to the server and not using NAT. Sorry to be so foggy.
So there is a fourth method so lets focus on reality situation 2. Public IP, private IP server using DNS method…

THe only thing I could find on my search was this…

There’s no complexity with hairpinNAT, it’s just one rule. Set it and forget it. It’s extremely convenient. You have internal server and you make it accessible to whole world (using dstnat) as somehostname.domain.tld. By default, it works for everyone except you (when you’re in same LAN).

So you can either connect directly to internal address, or in case the protocol cares about hostname (e.g. http), you need to add some dns override, either to router or local hosts file. Hosts file is bad, because you’ll need to change it every time you move between outside and inside. Static dns entry on router is slightly better, but it works only when device uses router as resolver. Have a device with hardcoded outside dns and it doesn’t work again. Add another hostname to your server and you need to change everything again. Remove hostname from server, put it somewhere else and watch how everything in your LAN fails, because you forget to remove static entry from router and it’s still pointing to old internal server.

Or set universal hairpinNAT rule and never worry about any of this again.

No… simply open, copy, modify, save, the single DNS static rule.


What is the ploblem, I open the router and delete the static entry,
I do not pass all the day moving hosts… :stuck_out_tongue:
If a host disappears from the network, it is no longer on my management, so it doesn’t matter that the internal PCs reach it anymore…


Simply on NAT I intercept all DNS query not directed on Local Router and redirect it on RouterOS DNS…
Also this make connections work if DNS are set manually wrong…

To summarize and to make more plain for novice users…

One doesnt change current DHCP server DNS settings (typically they are the gateway of each subnet for example - as you stated solved from routerboard,
what this means is that will not work if you have direct EXTERNAL DNS settings under DHCP server such as 1.1.1.1 or 8.8.8.8 ??
What is not clear is if one in IP DNS, allows remote requests
and then enters in some the Servers box (the one above dynamic servers). Does that also negate this method???

One makes a a static REGEX dns entry, whatever that means. It initially soundedl like one had to enter this in an IP firewall layer 7 protocol thing, but
it appears Regexp also is found in the IP DNS settings under the Static Button selection. so assuming here is where the rule/text is entered

The rule seems to state if a user types in the address shown (dyndns name) it should resolve to the private IP of the server via DNS (vice NAT).
Then some noise about ssl security, but not clear how this is used or invoked??

++++++++++++++++++++++++++++++++++++++
Clear up limitations on what other DNS setting can and cannot be made
Clear up location of rule/text to be posted
Clear up ssl security bit

No layer7, hairpin NAT or other frills.

Simply do only this, for example RouterBOARD 192.168.88.1 , local www.vattelappesca.rex server 192.168.88.68…
This DOES NOT BROKE SSL certificate

/ip dns static
add address=192.168.88.68 regexp="(^|www\\.)vattelappesca\\.rex\$" ttl=5m
/ip firewall nat
add chain=dstnat src-address=!192.168.88.1 dst-address=!192.168.88.1 dst-port=53 protocol=udp action=dst-nat to-addresses=192.168.88.1
add chain=dstnat src-address=!192.168.88.1 dst-address=!192.168.88.1 dst-port=53 protocol=tcp action=dst-nat to-addresses=192.168.88.1

this intercept all DNS request except for request started from RouterBOARD or already directed to RouterBOARD

(this NAT configuration also allows you to run any machine that has (at least one) a wrong DNS in the settings)

Okay understood,
but you havent cleared up the limitations required for this to work on
DHCP Server settings for each subnet if applicable?
Alllow remote DNS box
DNS servers entry (above dynamic servers)

  1. You can add address list instead as single IP excluded from interceptation, both as src and dst
  2. pihole for example? Yes, you still can add it and resolve inside pihole the DNS with local IP
  3. please explain, I not understand
    You mean DNS dynamic or staic defined on /ip dns? This DNS use input/output chain, not forward, and are untouched.
    The static DNS have precedence over any other way the RouterBOARD can obtain DNS.
    (first static, then static with regex, then the others)

Thanks, that answers it!

So If I had this scenario.
/ip dhcp-server network
add address=192.168.0.0/24 dns-server=1.1.1.1 gateway=192.168.0.1
IP static DNS 192.168.0.1

The router would not use 1.1.1.1 for DNS queries from 192.168.0.1 subnet???

On that way

/ip dhcp-server network
add address=192.168.0.0/24 dns-server=1.1.1.1 gateway=192.168.0.1

You provide the DNS 1.1.1.1 “suggested” to the internal DHCP client, which obtains it automatically, instead of setting it manually,
if it does not use one hard-coded inside
(example of hard-coded is android than use 8.8.8.8 and 8.8.4.4 instead of the DNS provided from the DHCP,
only if you block 8.8.8.8 and 8.8.4.4 use the DNS provided from the DHCP)
If you omit the DNS on DHCP network parameters, if “allowed remote request” on /ip dhcp is checked the DHCP server suggest the routerboard IP (192.168.0.1)
else suggest the DNS presents on /ip dns servers

The only way to make routerboard use the 1.1.1.1 is to put it on /ip dns servers, as only DNS server

It is not possible to select which DNS the RouterBOARD should use for each subnet

One could not construct a flow chart from that post its all over the map… I think language is a barrier,
State it in Italien and I will translate



/ip dhcp-server network
add address=192.168.0.0/24 dns-server=1.1.1.1 gateway=192.168.0.1

Non è possibile specificare, con questa istruzione, quale DNS la Routerboard deve utilizzare per ogni subnet

Se omesso un DNS server viene comunque inviato tramite DHCP server, tra quelli gà presenti come DNS della routerboard in “/ip dns”
Se è attivo “allowed-remote-request” su “/ip dns” la RouterBOARD in assenza di dns-server specificato, manda il proprio IP

Questa istruzione indica solo ai PC che utilizzano il DHCP quale DNS utilizzare,
ma non sono obbligati ad usarlo, per esempio gli Android hanno codificato all’interno l’8.8.8.8 e il 8.8.4.4
e l’unica maniera per fargli usare i DNS forniti, è bloccare 8.8.8.8 e 8.8.4.4 nel firewall se le richieste arrivano dalla LAN.

L’unico modo per poter far usare alla RouterBOARD l’IP 1.1.1.1 come DNS è impostarlo su “/ip dns servers” come unico DNS

Okay, let me ask the question in a different way.

What DNS setup by an admin would prevent your method of hairpin nat from working?

As it is not a hairpin NAT method, just the best way to configure the network when this need are present
(reach internal Server, on another subnet, from valid public DNS),
the only thing that can get in the way is an IP based SSH certificate (instead of a hostname certificate).
Or some device that uses external DoH or DoT, but since it is a matter of deliberately obstacle the reaching of one’s own address,
I don’t think that someone is self-hindering …

/ip dns static
add address=192.168.88.68 regexp=“(^|www\.)vattelappesca\.rex$” ttl=5m
/ip firewall nat
add chain=dstnat src-address=!192.168.88.1 dst-address=!192.168.88.1 dst-port=53 protocol=udp action=dst-nat to-addresses=192.168.88.1
add chain=dstnat src-address=!192.168.88.1 dst-address=!192.168.88.1 dst-port=53 protocol=tcp action=dst-nat to-addresses=192.168.88.1

Okay,
I understand the part in Green. This basically says any lan subnet request to look for that domain name should be (re)directed to the server.

What the heck did you add the NAT rule underneath for. They confuse the crap out of me.