Port forwarding on a home router
· in fundamentals
The server-side commands (ip, ss, dig) were tested on Debian 13.7 (Docker) on 5 November 2026. Router screens differ by model and were not tested.
Port forwarding tells your home router to pass certain incoming connections to your server, so people outside your home can reach the services it runs. This guide covers the checks to make first, the forwarding rule itself, how to test it from outside, dynamic DNS, and what to do when your connection makes forwarding impossible.
Why a home server needs port forwarding
Your internet provider gives your router one public IPv4 address. Every device in your home, including the server, gets a private address from the router, usually starting with 192.168., 10. or 172.16. to 172.31.. These private ranges are defined in RFC 1918 and cannot be reached from the internet. The router translates between the two (network address translation, or NAT), so your devices can open connections out.
Incoming connections are the problem. When a request arrives at your public address on, say, port 443, the router has no idea which device it is for, so it drops it. A port forwarding rule fills that gap: "send anything arriving on port 443 to 192.168.1.20, port 443".
A port is a number from 1 to 65535 that tells a machine which program a connection is for. Some common ones for a home server:
| Service | Port and protocol |
|---|---|
| Website (HTTP, also used for Let's Encrypt's HTTP-01 check) | 80 TCP |
| Website (HTTPS) | 443 TCP |
| XMPP clients | 5222 TCP |
| XMPP server-to-server | 5269 TCP |
| WireGuard VPN (common choice) | 51820 UDP |
| SSH | 22 TCP |
Only forward the ports a public service needs. Admin pages, databases and file shares should never be forwarded. Reach them through a VPN or an SSH tunnel instead.
Check for CGNAT before you start
Some providers no longer give each customer a public IPv4 address. They put many customers behind one shared address with carrier-grade NAT (CGNAT). Port forwarding on your router then does nothing, because the provider's NAT drops the connection before it reaches you.
To check, compare two addresses:
- Find the WAN or internet address on your router's status page.
- Find your public address as the internet sees it. On the server, this asks an OpenDNS resolver to report the address your query came from:
(On Debian,dig +short myip.opendns.com @resolver1.opendns.comdigis in thebind9-dnsutilspackage.) - If the two match, you have a public address and forwarding will work. If the router's WAN address is in
100.64.0.0/10(from100.64.0.0to100.127.255.255), you are behind CGNAT. That range is reserved for exactly this use by RFC 6598. A WAN address in one of the private ranges means another router sits between yours and the internet (double NAT).
Behind CGNAT, ask your provider for a public IPv4 address. Some give one for free on request, some charge, some refuse. If you cannot get one, skip to the alternatives below. With double NAT, either set the first box (often the provider's modem) to bridge mode, or forward the port on both routers.
Give the server a fixed address on your network
A forwarding rule points at one private address. If the server gets a different address from the router after a reboot, the rule points at nothing. Fix the address first.
The simplest way is a DHCP reservation (also called a static lease or address reservation) on the router. The router always hands the same address to the same network card, identified by its MAC address. Find the server's current address and MAC address on the server:
ip -brief address
ip -brief link
Then, in the router's LAN or DHCP settings, add a reservation for that MAC address. Pick an address that the router does not hand out to other devices, or simply keep the one it already has. Reboot the server and check that it gets the reserved address.
The other way is to set a static address in the server's own network configuration. That works too, but you must choose an address outside the router's DHCP range, or two devices may end up with the same one.
Add the forwarding rule
Log in to the router's web interface. Its address is usually the default gateway, which ip route show default on the server prints after via. The setting has different names by maker: "port forwarding", "virtual server", "NAT forwarding" or "port mapping". Some provider apps put it under "advanced" or "security".
A rule has the same parts on every router:
- External port (or public port, WAN port): the port the outside world connects to, for example 443.
- Internal address: the server's reserved private address, for example 192.168.1.20.
- Internal port: the port the service listens on, usually the same as the external one.
- Protocol: TCP, UDP or both. Web servers and XMPP use TCP and WireGuard uses UDP. Choose only what the service needs.
Save the rule. Before you test, make sure the service is actually running and listening on the server. ss lists listening ports and the program behind each:
sudo ss -tlnp
A line such as 0.0.0.0:443 or [::]:443 means the service listens on all addresses. 127.0.0.1:443 means it only accepts connections from the server itself, and forwarding will not help until you change that. Also check the server's own firewall: if it runs ufw, the port must be allowed there too.
Test from outside your network
Testing from inside your home often gives the wrong answer. Many routers do not support "hairpin NAT" (also called NAT loopback): a request from inside the network to your own public address never comes back in, even though the forwarding works for everyone else.
Test from a different network instead. The easiest is a phone with Wi-Fi turned off, using mobile data. Open https://your-public-address or your domain in its browser, or use an SSH app to connect to the forwarded port. From a computer outside your home, nc -vz your-public-address 443 reports whether a TCP port is open.
If the test fails, work through the chain from the server outwards:
- Does the service answer on the server itself (
curl http://localhostfor a web server)? - Does it answer from another device in your home on the server's private address?
- Does the server's firewall allow the port?
- Does the router rule point at the right address, port and protocol?
- Is the router's WAN address the same as your public address, or are you behind CGNAT?
- Does your provider block the port? Some providers block certain incoming ports on home plans, and their terms of service say which.
If your router lacks hairpin NAT and you want to use your domain from inside the house, add a DNS entry for the domain on your local network that points to the server's private address. Many routers and local DNS filters can do this.
Dynamic DNS: a name that follows your address
Most home connections get a public address that can change, after a router restart or every so often. A domain name that points at the old address then leads nowhere. Dynamic DNS (DDNS) solves this: a small program checks your public address and updates a DNS record when it changes.
You have three common options:
- The router's DDNS client. Many routers support a few DDNS providers in their settings. This is the easiest, but limits you to the providers the router knows.
- ddclient on the server. ddclient is an open-source DDNS client, packaged in Debian, that supports many providers and DNS hosts with an update API.
- Your DNS host's API. If your domain's DNS host has an API, a short script run by a timer can update the record. This is what most DDNS clients do internally.
Use a low TTL (time to live) on a dynamic record, such as five minutes, so resolvers forget the old address quickly after a change.
IPv6 works differently
With IPv6 there is usually no NAT. Your provider gives your home a whole range of addresses, and every device, including the server, gets its own public one. Nothing needs to be "forwarded".
Instead, the router's firewall blocks incoming IPv6 connections by default on most home routers, and you open a hole for one address and port. Look for "IPv6 firewall", "pinhole" or "allow incoming" in the router settings. Use the server's stable address, not a temporary privacy address, which changes over time. ip -6 address on the server lists both. The temporary ones are marked temporary.
IPv6 helps if you are behind CGNAT for IPv4 and your provider gives you IPv6, because the IPv6 address is not behind the shared NAT. The catch is that visitors on IPv4-only networks still can't reach you, so IPv6 alone is not enough for a public website.
When port forwarding is not possible
If you are behind CGNAT, cannot change the router, or simply don't want open ports, there are three main alternatives. All three work by having your server make an outgoing connection, which NAT always allows, and receiving traffic back through it.
A private VPN: WireGuard or Tailscale
If only you and people you know need to reach the server, a VPN is the best fit. Your devices join a private network with the server, and nothing is exposed to the internet.
WireGuard is a small, fast VPN built into the Linux kernel. On its own, it needs one side to be reachable, which means a forwarded UDP port at home or a small rented server in the middle. Tailscale builds on WireGuard and adds a coordination service that connects your devices through most NATs, including CGNAT, without any forwarded port. Its coordination server is run by Tailscale. Headscale is an open-source, self-hosted replacement for it if you want no third party involved.
The limit: every device must run the VPN client, so this does not work for a public website or for friends on other XMPP servers. Tailscale's Funnel feature exposes a service to the public internet, but only on ports 443, 8443 and 10000, with bandwidth limits you cannot change. The Funnel relay cannot decrypt the traffic: your device handles the TLS.
Cloudflare Tunnel
Cloudflare Tunnel runs a program called cloudflared on your server. It makes outgoing connections to Cloudflare's network, and Cloudflare sends visitors to your site through them. You need no public IP and no open ports, and it works behind CGNAT.
The trade-offs:
- Cloudflare handles two separate connections, one with the visitor and one with your server. It sits in the middle of the traffic and can read it.
- Your site depends on a large company's free plan, whose terms can change.
- Cloudflare's terms for its free CDN say you may not use it to serve video or a "disproportionate percentage" of pictures, audio or other large files unless you use its paid services. A photo gallery or media server may break those terms.
- It is built for HTTP. Other protocols, such as XMPP server-to-server traffic, need a different route.
For a small public website behind CGNAT it is the quickest fix. If you care about who sees your traffic, which is one reason to self-host at all, the next option fits better.
A small rented server as a relay
Rent the smallest virtual server you can find, which has a public IP. Connect your home server to it with WireGuard, started from the home side so CGNAT does not matter. Then forward ports on the rented server through the tunnel to your home server, or run a reverse proxy there.
This works for any protocol and keeps TLS on your own machine, so the relay only passes encrypted bytes for HTTPS. It costs a monthly fee and is more work to set up and keep updated. Your connection still depends on the relay staying up.
| Port forwarding | WireGuard / Tailscale | Cloudflare Tunnel | Rented relay | |
|---|---|---|---|---|
| Works behind CGNAT | No | Tailscale yes; plain WireGuard needs one reachable end | Yes | Yes |
| Public visitors | Yes | No (Funnel: limited ports) | Yes, HTTP | Yes, any protocol |
| Third party sees decrypted traffic | No | No | Yes | No, if TLS ends at home |
| Cost | None | None for small use | Free plan | Monthly fee |
Keep the exposed parts safe
Bots scan public addresses for open ports all the time, so every forwarded port will be tried. A few rules keep the risk small:
- Forward only the ports a public service needs, and remove rules you no longer use.
- Keep the services behind those ports updated. The web server guide shows how to turn on automatic security updates on Debian.
- If you forward SSH, use key-only login, as the SSH guide explains, or keep SSH behind a VPN.
- Never forward the router's own admin page, a NAS admin page or a database port.
With the port open and a name pointing at it, you can host a website or an XMPP chat server from home. If you are still choosing a machine to run them on, see choosing home server hardware.
Frequently asked questions
How do I know if I am behind CGNAT?
Compare the WAN address on your router's status page with your public address as the internet sees it. If they differ and the router's address is in 100.64.0.0/10, you are behind CGNAT and port forwarding on your router will not work.
Can I port forward behind CGNAT?
Not on your own router, because your provider's NAT drops incoming connections first. Ask your provider for a public IPv4 address, or use a VPN such as Tailscale, Cloudflare Tunnel or a rented server as a relay.
Why does my port forward work from outside but not from home?
Your router probably lacks hairpin NAT, also called NAT loopback, so requests from inside to your own public address don't come back in. Test from a phone on mobile data, and add a local DNS entry for your domain that points to the server's private address.
Is port forwarding safe?
Bots scan public addresses all the time, so every forwarded port will be tried. Forward only the ports a public service needs, keep that service updated, use key-only login for SSH, and never forward admin pages or databases.
Do I need a static IP address to host a server at home?
No. Give the server a fixed private address with a DHCP reservation on the router, and use dynamic DNS so a domain name follows your public address when it changes.
Is Cloudflare Tunnel better than port forwarding?
It works behind CGNAT and needs no open ports. The trade-offs are that Cloudflare sits in the middle and can read the traffic, it is built for HTTP, and its free CDN terms restrict serving video and large numbers of large files.
Sources
- RFC 1918: Address Allocation for Private Internets
- RFC 6598: IANA-Reserved IPv4 Prefix for Shared Address Space
- Challenge types, Let's Encrypt
- WireGuard
- Tailscale Funnel, Tailscale documentation
- How NAT traversal works, Tailscale
- Headscale, project repository
- Cloudflare Tunnel, Cloudflare documentation
- Encryption modes, Cloudflare documentation
- Service-specific terms: application services, Cloudflare
- ddclient, project site
- RFC 6120: XMPP Core (ports 5222 and 5269)