Introduction / Why This Is Needed
Port forwarding allows you to make a local service running on an internal IP address and port accessible from an external network via the public IP and port of your Linux server. This is essential when you want to, for example, provide access to a web server on port 8080, a database, or an SSH daemon located behind NAT, without changing the configuration of the services themselves.
After completing this guide, you will be able to configure forwarding of incoming traffic from port 80 to the internal server 192.168.1.100:8080, or from port 2222 to the internal SSH server 192.168.1.100:22—depending on your needs.
Requirements / Preparation
Before you begin, ensure that:
- You have access to a Linux system (Ubuntu, CentOS, Debian, Fedora, etc.) with sudo or root privileges.
- The system has iptables installed and running (for classic distributions) or firewalld (for CentOS/RHEL/Fedora). You can check with:
sudo systemctl status iptables # for iptables sudo systemctl status firewalld # for firewalld - You know the internal IP address and port of the service you want to forward. You can find this with:
orip addr showhostname -I - You have a public IP address (or domain name) that will receive the incoming traffic. If the server is on a local network, this is your router's IP, and port forwarding must be configured on the router to point to your Linux server.
Step 1: Enabling IP Forwarding in the Kernel
By default, IP forwarding is disabled in most Linux distributions. This means the kernel will not forward packets between network interfaces. Enable it temporarily:
sudo sysctl -w net.ipv4.ip_forward=1
To enable it permanently (so the setting persists after reboot), edit the file /etc/sysctl.conf or create a new one in /etc/sysctl.d/:
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf
sudo sysctl -p /etc/sysctl.d/99-ipforward.conf
⚠️ Important: If you are using IPv6, also enable forwarding for it by adding
net.ipv6.conf.all.forwarding=1to the same file.
Step 2: Configuring Port Forwarding with iptables
This method is suitable for distributions where iptables is used by default (e.g., Ubuntu before 20.04, Debian, CentOS 6/7 without firewalld).
Assume you want to forward incoming connections on port 80 (HTTP) to an internal server with IP 192.168.1.100 on port 8080.
- Add a DNAT rule (destination NAT) to the
nattable'sPREROUTINGchain. This rule will apply to incoming packets destined for port 80:sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080-t nat— specifies the NAT table.-A PREROUTING— appends the rule to the end of the PREROUTING chain (processes packets before routing).-p tcp— protocol TCP (replace withudpfor UDP).--dport 80— target port on the external interface.-j DNAT— target is to change the destination address.--to-destination 192.168.1.100:8080— new address and port.
- Allow packet forwarding in the
FORWARDchain. This is needed so the kernel doesn't block forwarding packets to the internal server:sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 8080 -j ACCEPT
If the internal server needs to access the internet through this same gateway, add a MASQUERADE rule for outgoing traffic:sudo iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 8080 -j MASQUERADE
However, for simple incoming port forwarding, this is often not required if the internal server has a proper route back to the client.
Step 3: Configuring Port Forwarding with firewalld
For distributions with firewalld (CentOS 7+, RHEL 7+, Fedora), use the simpler syntax with zones.
- Identify the active zone (e.g.,
public):sudo firewall-cmd --get-default-zone
Usually this ispublic. - Add a port forwarding rule to that zone. To forward port 80 to
192.168.1.100:8080:sudo firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toaddr=192.168.1.100:toport=8080--add-forward-port— adds a port forwarding rule.port=80— external port.proto=tcp— protocol (can beudp).toaddr=192.168.1.100— internal IP.toport=8080— internal port.
- Make the rule permanent (so it survives reboots):
sudo firewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toaddr=192.168.1.100:toport=8080 --permanent💡 Tip: If your internal server needs internet access through this gateway, enable masquerading:
sudo firewall-cmd --zone=public --add-masquerade --permanent
Step 4: Saving Rules
iptables and firewalld handle rule persistence differently.
For iptables
On most distributions, iptables rules are not saved automatically after reboot. Use:
- Ubuntu/Debian with the
iptables-persistentpackage:sudo netfilter-persistent save
Or manually:sudo iptables-save > /etc/iptables/rules.v4 - CentOS 6/7 (if not using firewalld):
sudo service iptables save
orsudo /etc/init.d/iptables save
For firewalld
Rules added with the --permanent flag are saved in configuration files (/etc/firewalld/). After adding rules, reload firewalld to ensure they are applied:
sudo firewall-cmd --reload
Step 5: Verifying Functionality
- View active rules:
- For iptables:
You should see the rule in thesudo iptables -t nat -L -n -vPREROUTINGandFORWARDchains. - For firewalld:
Look forsudo firewall-cmd --list-allforward-portsin the output.
- For iptables:
- Check that the port is open on the external interface. Note: after DNAT, port 80 on the public interface is not listened to by a daemon but is forwarded. Therefore,
ss -tuln | grep :80might show nothing. It's better to test from outside. - Test from outside:
- From another computer on the network or from the internet, run:
orcurl http://PUBLIC_IP:80telnet PUBLIC_IP 80 - You should receive a response from the internal server (e.g., a webpage from port 8080).
If the service uses HTTPS, replace port 80 with 443 and the protocol withhttps. - From another computer on the network or from the internet, run:
Common Issues
Rules don't work after reboot
- iptables: You didn't save the rules. Run
sudo netfilter-persistent saveor the equivalent command for your distribution. - firewalld: You added the rule without
--permanent. Fix it:sudo firewall-cmd --zone=public --add-forward-port=... --permanentand reload.
"Permission denied" or "Operation not permitted" error
- Ensure you are running the commands with sudo or as root. Port forwarding requires administrator privileges.
Port already in use
- Another service (e.g., Apache/Nginx web server) is already running on the external port (e.g., 80). Stop it or choose a different port.
- Check:
sudo ss -tuln | grep :80
Incorrect internal IP
- Ensure the IP
192.168.1.100is correct and the service on it is running. Check:ping 192.168.1.100andcurl http://192.168.1.100:8080from the Linux server itself.
firewalld and iptables conflict
- On systems with firewalld (CentOS 7+), do not use iptables directly—firewalld manages rules via iptables. Instead, configure everything through
firewall-cmd. If you accidentally added an iptables rule, it may be overwritten by firewalld.
SELinux blocking (for CentOS/RHEL/Fedora)
- SELinux may forbid port forwarding by default. Temporarily disable it for testing:
If this helps, configure SELinux policies correctly. For firewalld, issues are rare, but for iptables you might need:sudo setenforce 0sudo semanage port -a -t http_port_t -p tcp 80
Internal service not responding
- Check that the service (e.g., web server) is listening on
0.0.0.0:8080or on the internal IP, not just on127.0.0.1. Usesudo ss -tuln | grep 8080. - Ensure the internal server (if separate) has a route back to the client via your gateway. Sometimes a static route or NAT on the gateway is required.
Forwarding UDP ports
- For UDP, use the same commands but replace
tcpwithudpin iptables orproto=udpin firewalld. Example for firewalld:sudo firewall-cmd --zone=public --add-forward-port=port=53:proto=udp:toaddr=192.168.1.100:toport=53 --permanent