I am using UFW to protect my network. How do I forward TCP HTTP port # 80 and 443 to an internal server hosted at 192.168.1.100:80 and 192.168.1.100:443 using UFW on Ubuntu Linux server?

UFW is an acronym for uncomplicated firewall. It is used for managing a Linux firewall and aims to provide an easy to use interface for the user. In this tutorial, you will learn how to forward incoming traffic to your server running ufw on port 80/443 to port 80/443 on another internal server hosted in your LAN/VLAN or Linux containers.
Tutorial details
Difficulty level Advanced
Root privileges Yes
Requirements Linux terminal
Category Firewall
Prerequisites ufw command
OS compatibility AlmaLinux Alpine Arch Debian Fedora Linux Mint openSUSE Pop!_OS RHEL Rocky Stream SUSE Ubuntu
Est. reading time 3 minutes
Advertisement

Our sample setup

Let us say you want to forward requests going to {80,443} to a server listening on 192.168.1.100:{80,443}:

Fig.01: How to configure ufw to redirect http traffic to another IP:port

Fig.01: How to configure ufw to redirect http traffic to another IP:port

All request for 202.54.1.1 port 80 and 443 need to redirect to another internal server.

DNAT

If you have a server on your internal network that you want make available externally, you can use the -j DNAT target of the PREROUTING chain in NAT to specify a destination IP address and port where incoming packets requesting a connection to your internal service can be forwarded. The syntax is as follows for the iptables command:
# /sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -d {PUBLIC_IP} --dport 80 -j DNAT --to {INTERNAL_IP}:80
OR
# /sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -d {PUBLIC_IP} --dport 443 -j DNAT --to {INTERNAL_IP}:443

Postrouting and IP Masquerading

To allow LAN nodes with private IP addresses to communicate with external public networks, configure the firewall for IP masquerading, which masks requests from LAN nodes with the IP address of the firewall’s external device such as eth0. The syntax is:
# /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
OR
# /sbin/iptables -t nat -A POSTROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24 -j MASQUERADE

How to configure ufw to setup a port forward

You need to edit /etc/ufw/before.rules file, enter:
$ sudo vi /etc/ufw/before.rules
Next configure ufw to redirect http traffic to another (LAN) IP:port. At the top file, append:

*nat
:PREROUTING ACCEPT [0:0]
# forward 202.54.1.1  port 80 to 192.168.1.100:80
# forward 202.54.1.1  port 443 to 192.168.1.100:443
-A PREROUTING -i eth0 -d 202.54.1.1   -p tcp --dport 80 -j  DNAT --to-destination 192.168.1.100:80
-A PREROUTING -i eth0 -d 202.54.1.1   -p tcp --dport 443 -j  DNAT --to-destination 192.168.1.100:443
# setup routing
-A POSTROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24 -j MASQUERADE
COMMIT

Save and close the file. Edit /etc/sysctl.conf:
$ sudo vi /etc/sysctl.conf
Set/edit as follows:

net.ipv4.ip_forward=1

Save and close the file. Reload changes:
$ sudo sysctl -p
Finally, restart the firewall to enable routing using the systemctl command:
$ sudo systemctl restart ufw
Make sure port 80 and 443 is allowed, otherwise ufw will block the requests that are redirected to internal 192.168.1.100:{80,443}:
$ sudo ufw allow proto tcp from any to 202.54.1.1 port 80
$ sudo ufw allow proto tcp from any to 202.54.1.1 port 443

Verify new settings:
$ sudo ufw status
$ sudo iptables -t nat -L -n -v

Finally, make sure your domain has DNS type ‘a’ set to 202.54.1.1.

A note about lxd proxy protocol

Do you want to avoid typing all these iptables rules? LXD now supports proxy devices. It allows forwarding network connections between the LXD host and instance type container or VM. In other words, we can forward traffic hitting one of the LXD host’s IP addresses to an address inside the instance/container. Of course, you can also do the reverse and have an address in the instance/container connected through the host. The syntax is:

lxc config device add {container-name} {device_name_here} proxy listen=tcp:${src_IP}:${src_port} connect=tcp:${dest_ip}:${dest_port}

In this example redirect ssh traffic to container named ubuntu-nginx:

lxc config device add ubuntu-nginx ssh-reditect proxy listen=tcp:1.2.3.4:2222 connect=tcp:10.83.200.253:22

Where,

  • ubuntu-nginx – LXD container or instance vm name
  • ssh-reditect – Unique name per LXD container or instance vm name
  • listen=tcp:1.2.3.4:2222 – Public or private IPv4/IPv6 and port to listen on
  • connect=tcp:10.83.200.253:22 – Forward traffic to this LXD instance’s IP:PORT

Related
Also, check all our complete firewall tutorials for Alpine Linux Awall, CentOS 8, OpenSUSE, RHEL 8, Debian 12/11, Ubuntu Linux version 16.04 LTS/18.04 LTS/20.04 LTS, and 22.04 LTS.

Conclusion

In this tutorial, you learned how to configure UFW to forward tcp port 80/443 to internal hosts or Linux containers hosted by the LXD. See the ufw command manual page using the man command:
$ man ufw

🥺 Was this helpful? Please add a comment to show your appreciation or feedback.

nixCrat Tux Pixel Penguin
Hi! 🤠
I'm Vivek Gite, and I write about Linux, macOS, Unix, IT, programming, infosec, and open source. Subscribe to my RSS feed or email newsletter for updates.

16 comments… add one
  • Dof Feb 5, 2017 @ 16:47

    just a little error :
    /sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -d {PUBLIC_IP} --dport 443 -j DNAT --to {INTERNAL_IP}:443

    • 🛡️ Vivek Gite (Author and Admin) Vivek Gite Feb 5, 2017 @ 19:26

      Thanks for the heads up!

    • Dave Feb 8, 2021 @ 5:44

      Hi Vivek / Dof
      I tried this line and made sure it is correctly typed but I get the following message.
      multiple -d flags not allowed
      Any suggestions?
      Thank you for the great information in the article :-)

      • 🛡️ Vivek Gite (Author and Admin) Vivek Gite Feb 8, 2021 @ 9:18

        No, the correct syntax is:

        -d ip1,ip2
        -d ip/subnet,ip2,cidr4 
        
  • Harold Angulo Mar 16, 2017 @ 19:16

    How Can I do this using domains and subdomains instead public IP address?

    • 🛡️ Vivek Gite (Author and Admin) Vivek Gite Apr 16, 2017 @ 16:52

      You can’t. You need to use reverse proxy such as Nginx. It is much better.

  • Zinc Jan 24, 2021 @ 0:14

    Been trying to do this across NIC cards– from a 170.*.*.* network to a 192.168.1.* private network. Seems like requests get forwarded to the 170 network instead of the 192 network which isn’t accessible to the outside– can’t it be made to route *through* the server to the other network?

    • Bartek Feb 1, 2021 @ 9:37

      Hi Zinc,
      If you forward traffic from a 170.*.*.* network to a 192.168.1.*, please be sure that hosts on your 192.168.1.* network have Gateway set to 192.168.1.10.
      In this example, 192.168.1.10 is your router which you set NAT.
      When you sent traffic to your 192.168.1.* network, the traffic must be sent back the same way through your NAT router.

  • Ban Soo Jun 6, 2021 @ 19:18

    A kind and noble person, whoever you are, I thank you from the bottom of my heart for helping with ufw and port forwarding. Unfortunately, I lost days, and only your instructions worked on Debian 10 server. Please add a donation button so that we can support your hard work.

  • Artista Apr 14, 2022 @ 22:54

    I need to implement a NAT between two private networks (of different customers), one is on 192.168.168.0/24 and the otrer one is on 10.10.10.0/24.
    I have a debian machine with 2 interfaces; enu33 10.10.10.15 and enu36 192.168.168.15.
    The target system is on 192.168.168.12:80.
    I have the rules:

    -A PREROUTING -i ens33 -d 10.10.10.15 -p tcp --dport 80 -j DNAT --to-destination 192.168.168.12:80
    -A POSTROUTING -s 192.168.168.0/24 -o ens33 -j MASQUERADE

    But cant make it work. Please any suggestion to debug.

  • ravi kumar Jun 4, 2022 @ 7:09

    Hii
    Vivek Gite,
    I have some problem and I want to redirect any port number on a specific IP_Address. please help me.
    ex- ip_add 172.10.283.119/3001 or 172.10.283.119/grafana
    172.10.283.119/ui
    Like this please help.

  • VeeDub Sep 12, 2022 @ 23:58

    Hello Vivek,
    I came across your post on how to forward ports to an internal server, as I am trying to forward a port to an LXC container.

    My scenario is different to your scenario. I have the LXC host on a private IP address 10.254.247.85 and I have the LXC container on the same subnet with IP 10.254.247.70 which is accessible via the bridge on 10.254.247.10

    I want to forward SSH to the container host, however as SSH is also being used on the container host. I want to forward the following port 10.254.247.85:2222 to 10.254.247.70:22

    So, I have made the following changes:

    /sbin/iptables -t nat -A PREROUTING -i eth0 -p tcp -d 10.254.247.85 --dport 2222 -j DNAT --to 10.254.247.70:22

    I have not entered the masquerade rule, as in my case the container host and the container are on the same subnet. So, I don’t believe the masquerade rule is needed in this case.

    I’ve then added the following entries to /etc/ufw/before.rules

    *nat
    :PREROUTING ACCEPT [0:0]
    # forward 10.254.247.85  port 2222 to 10.254.247.70:22
    
    -A PREROUTING -i eth0 -d 10.254.247.85 -p tcp --dport 2222 -j DNAT --to-destination 10.254.247.70:22
    COMMIT 
    

    When I perform:

    sudo systemctl restart ufw

    I lose Internet access on the container host. I’ve checked that the default route is still in place, but I can no longer access the Internet.

    If I remove the above changes to ufw, then Internet access is restored.

    So, I’m doing something wrong here. Hopefully, you can see what the problem is.

    Thank you
    VW

    • 🛡️ Vivek Gite (Author and Admin) Vivek Gite Sep 13, 2022 @ 4:42

      Your case is different. What you need is DNAT inside your container itself. For example, log into the lxd with IP 10.254.247.85:

      lxc exec {container-name-here} bash

      Then type as root on 10.254.247.85:

      iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination  10.254.247.70:22
      iptables -t nat -A POSTROUTING -j MASQUERADE

      Then from host do it:

      ssh -p 2222 10.254.247.85

      This will redirect 10.254.247.85:2222 to 10.254.247.70:22. Of course, you need to save those rules on 10.254.247.85. HTH

      • VeeDub Sep 13, 2022 @ 6:25

        Hello Vivek,

        Thanks for responding.

        I tried adding the iptables commands on 10.254.247.85 and I’m still not able to ssh to 10.254.247.70

        In other words ssh 10.254.247.85:2222 still times out.

        I’ve added port 2222 to ufw on 10.254.247.85

        From your instructions, I think you might think that 10.254.247.85 is also a container, it is not. It is the lxd/lxc host.

        I have configured the lxc bridge to be on the same subnet as the host. From some discussions that I have had on other forums, prior to discovering your post, having the lxc bridge subnet on the same subnet as the host has been implemented by other posters. That being said, I just want to get this working, so I’m happy to reconfigure the bridge subnet if you believe that is necessary.

        However, I have been able to ping 10.254.247.70 from external hosts without the need for any forwarding rules, other than allowing icmp through ufw on 10.254.247.70

        And as ping works, I was surprised that other ports didn’t just “work” once the routing was configured.

        • 🛡️ Vivek Gite (Author and Admin) Vivek Gite Sep 13, 2022 @ 17:53

          Indeed, I misunderstood your post. Sorry about that. You need a way to proxy connections between the host and containers. Right? In that case, there is an easier way. The syntax is:

          lxc config device add {container-name} ssh-reditect proxy listen=tcp:${src_IP}:${src_port} connect=tcp:${dest_ip}:${dest_port}
          lxc config device add c1 ssh-reditect proxy listen=tcp:10.254.247.85:2222 connect=tcp:10.254.247.70:22

          Test it:

          ssh -p 2222 user@10.254.247.85

          Read docs here: https://linuxcontainers.org/lxd/docs/master/instances/#type-proxy

          • VeeDub Sep 13, 2022 @ 23:43

            Brilliant. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

Use HTML <pre>...</pre> for code samples. Your comment will appear only after approval by the site admin.