skip to content

System: Accessing Public IP address from behind NAT

This article describes a simple solution we came up with to for what must be a common problem for anyone hosting a website on a local network or at a hosting centre with a 1:1 NAT (Network Address Translation) or similar firewall.

What is a Private Network?

As well as the public IP addresses that we use every day (yours is and ours the RFC1918 specification cleverly allows for a number of private networks of various sizes:

RFC1918 nameIP address rangenumber of addresses
24-bit block10.0.0.0 –,777,216
20-bit block172.16.0.0 –,048,576
16-bit block192.168.0.0 –,536

Many home users will be familiar with the 16-bit block range as a means for accessing the router control panel or talking to other devices in the local network.

While everyone inside the local network can use these addresses they are not visible to the outside world. When someone else opens they will see their router and not yours. The outside world can only see the public IP address that has been assigned to your Internet connection.

Description of the problem

From inside the private network each server or device is known only by it's private IP address and is always referenced using that address. Internal IP addresses are either assigned manually, or dynamically by the router using DHCP (Dynamic Host Configuration Protocol).

With a 1:1 NAT firewall setup, requests from outside the network are translated into requests for a local address. For example, a public IP address 204.8.XXX.6 might be converted to a local address 10.30.XXX.6, 204.8.XXX.7 mapped to 10.30.XXX.7 and so on.

A consequence of this is that from a server inside the network it's no longer possible to access the public IP address. So any HTTP requests for locally hosted websites will fail because a DNS lookup will return the public address which is unreachable.

This makes it very difficult to run a search spider over hosted websites or to trigger scripts using programs such as wget or cURL.

Possible solutions

If you are hosting only a small number of domains then you can set up a local DNS server (BIND) mapping those domains to local IP addresses. You could also achieve this by editing /etc/hosts.

For a large number of domains, however, this is not really an option as it means twice as much work every time the DNS is updated.

Using NAT tables to get around a NAT firewall

Sure enough there's a simpler solution using iptables. We know that a request from the server itself (telnet, web browser or web spider for example) can't reach the external IP address, but that the same request using the internal address will get through.

Using the right routing rules we can translate those requests and it should all just work. After some experimentation what we came up with was the following rules and added them to the firewall script which runs on startup:

#!/bin/sh IPTABLES="/sbin/iptables" $IPTABLES -t nat -A OUTPUT -d 204.8.XXX.6 -s 10.30.XXX.0/XX -j DNAT --to-destination 10.30.XXX.6 $IPTABLES -t nat -A OUTPUT -d 204.8.XXX.7 -s 10.30.XXX.0/XX -j DNAT --to-destination 10.30.XXX.7 ...

(to see what was going we used the LOG option in iptables to monitor requests from the server ip address in each of the NAT chains. In this case requests appeared only in the OUTPUT chain and not in PREROUTING or POSTROUTING as they would for a router)

The above rules intercept any requests from our server (subnet 10.30.XXX.0/XX) directed at our public IP addresses, which would normally drop out or fail, and translates them into requests for the appropriate local IP address.

So an HTTP/1.1 request for (204.8.XXX.6) is translated into a request for (10.30.XXX.6) meaning that the server can handle it itself. You will need a separate rule for each external/internal address pair.

Essentially what we're doing is replicating the actions taken by the NAT firewall for external requests, only we're doing it for internal requests.

These rules are to allow a server behind a NAT firewall to make requests to itself using the public IP addresses. For router configuration you need to use a combination of DNAT, SNAT and Masquerading and there are loads of examples online.

< System

User Comments

Post your comment or question

6 September, 2023

But what happens when your home network is behind your routers nat which is behind one or many layers of your own cgnat? I'm aware of services such as zerotier but they specifically don't supply a public ip address, requiring you to rent a vps to run their agent on.

18 January, 2018

Nice trick with the hosts file. That one worked for me.

15 August, 2017

Anyone have a firewalld rule for this? God I hate centos7.

9 May, 2016

What about changing the hosts file for the internal server. i.e.

with this all request to the website locally resolves to internal IP instead of global IP ?

That's a valid solution, but it becomes less practical when you have 100+ domains pointing to the server, with regular changes.

15 March, 2016

Try to add this:

iptables -t nat -A PREROUTING -d <your pub IP>/32 -p tcp -m tcp --dport 2222 -j DNAT --to-destination localIP:port

8 March, 2016

How did you manage to add an OUTPUT rule (which is usually in the "filter" chain) to the "nat" chain?
I'm currently in need of doing the same thing, but i need to NAT different ports from the external IP to different internal IPs, I have already successfully added the PREROUTING rules to allow external connections, and I lack only the last bit.