Port forwarding
Three separate articles so far have named port forwarding without actually setting one up: Public vs Private IP described it as the router rule that lets an internet client reach 192.168.1.34:8080, and the device-level NAT article placed it as one of three ways to work around NAT's outbound-only table entries. What neither article did is show the rule itself, or what actually goes wrong when it's set up carelessly — which is common enough that it's worth doing properly here.
What the rule actually says
A port forwarding rule is a static instruction added to the router, independent of any traffic that has happened yet: "any inbound packet arriving at my public IP on port X, using protocol Y, should be rewritten and delivered to this specific internal address on port Z." It's the same NAT table the device-level article walked through — the difference is only when the entry gets created. A normal outbound connection creates its table entry automatically, the moment a packet leaves the network. A port forwarding rule pre-creates that entry permanently, before any packet has arrived, specifically so that a connection can be initiated from outside — which NAT's normal behavior otherwise makes impossible.
A typical home router's port forwarding form asks for exactly four things:
| Field | Example | Meaning |
|---|---|---|
| External port | 8080 |
The port on the router's public IP that inbound traffic targets |
| Internal IP | 192.168.1.34 |
Which device on the LAN receives the forwarded traffic |
| Internal port | 8080 |
The port on that device the traffic is delivered to |
| Protocol | TCP |
Whether the rule applies to TCP, UDP, or both |
External and internal port don't have to match. A rule can forward public port 2222 to internal port 22, which is a common trick specifically so that a service's well-known port never appears in scans of the public address at all — anyone probing port 22 from outside finds nothing, while the real SSH daemon is still reachable on 2222. This doesn't add real security by itself — anyone who already knows or guesses the mapped port reaches the same service — but it does quietly remove the service from the enormous volume of automated scans that only ever check well-known ports.
Protocol matters, and getting it wrong looks like a config that "does nothing"
The protocol field isn't a formality. TCP and UDP are entirely separate transports at the same port number — forwarding TCP port 51820 does nothing at all for UDP traffic arriving on that same port number, and vice versa. This trips people up constantly when setting up a UDP-based service: a WireGuard VPN endpoint, for instance, listens on UDP by default, and a rule created as "TCP" because that's the default the router's form pre-selected simply never matches any of the actual traffic arriving. The service is running, the rule exists, the port shows correctly forwarded in the router's UI — and every connection attempt still fails, because the router is watching for a protocol nothing is using.
ss -tulnp confirms the service really is listening — the u in -tulnp for UDP sockets, matching the udp in the first column. If the forwarding rule says TCP while the service the rule targets is listening on udp, that mismatch is the entire bug, and no amount of double-checking IP addresses or internal ports will find it.
Verifying a rule from a vantage point that actually tests it
The single most common mistake in testing a new port forwarding rule is testing it from inside the same network the rule is supposed to expose the service from the outside to. A device on the LAN reaching the service by its private IP directly, or even by the router's public IP, doesn't exercise the forwarding path the same way an actual external client does — and on many consumer routers, the public-IP-from-inside case fails for an entirely different reason (NAT hairpinning, which the device-level NAT article's practical scenario already walked through) that has nothing to do with whether the forwarding rule itself is correct.
A genuine external vantage point is required: a phone on cellular data with Wi-Fi off, a cloud VM, or an online port-checking tool that connects from outside the network being tested.
Run from a genuinely external host — a cloud VM works well for this — nc -vz opens and immediately closes a TCP connection to the given host and port, reporting only whether the connection succeeded. -z means "scan only, don't send data"; -v prints the result; -w3 caps the wait at three seconds so a filtered or dropped port doesn't hang the command. A failure here, tested genuinely from outside, means the problem is in the path between the internet and the router — the rule itself, the protocol, or an upstream firewall — not in anything happening on the LAN.
Every open rule is a permanent, unfiltered hole
A port forwarding rule bypasses the router's own inbound protection entirely
Everything the device-level NAT article called an "accidental security property" of NAT — the fact that unsolicited inbound packets have nowhere to go and get dropped — is exactly what a port forwarding rule cancels out, on purpose, for the port it names. The service now receives connections from literally anywhere on the internet, not just from clients you intend to reach it. Before adding one, confirm the service has its own authentication, is kept patched, and — for anything beyond a short-lived lab exercise — sits behind a firewall rule scoped to the source addresses that should actually be allowed to reach it, rather than relying on the port being open at all as the only gate.
Automated scanning of the entire public IPv4 space for open ports is constant and takes minutes, not days — a freshly forwarded port with a weak or default service behind it is a matter of when, not if, it gets probed.
Some routers also offer a DMZ host setting, which forwards every port to one internal device rather than naming ports individually. It's occasionally used for troubleshooting or for a device that genuinely needs many inbound ports (an older game console, for instance), but it removes the port-by-port boundary entirely — the device is now exposed on every port simultaneously, and it should never be a production configuration for anything that also holds sensitive data or credentials.
UPnP: automatic port forwarding, and why it deserves the same scrutiny
Many home routers support UPnP (Universal Plug and Play), which lets an application on the LAN request its own port forwarding rule automatically, without anyone touching the router's admin interface. A game console opening a rule for online play, or a BitTorrent client opening one for better peer connectivity, are the common legitimate cases — and both work by having software already running inside the network ask the router to open a hole in itself.
That trust model is also UPnP's weakness: any device or piece of malware on the LAN can make the same request, and most UPnP implementations don't ask for confirmation before granting it. A compromised IoT device requesting a forwarding rule to expose an internal service, or to route its own outbound abuse traffic through a rule it created itself, is a documented and recurring class of real-world incident. Disabling UPnP and adding forwarding rules manually is the more deliberate default for anything beyond a household game console; it trades a little convenience for actually knowing what's exposed.
Practical scenario: a rule that works for months, then suddenly doesn't
A small team self-hosts a Git server on a home lab machine, forwards TCP port 443 to it, and it works reliably for months. One day, external access stops working entirely — no configuration was changed, the service is confirmed running and listening, and the rule still shows correctly in the router's admin page exactly as it always has.
Comparing that against what was configured when the rule was created — 203.0.113.7 — shows the router's public address has changed. Most residential ISPs assign public addresses dynamically, exactly as Public vs Private IP already noted, and a port forwarding rule only ever names the internal side explicitly; the external side is simply "whatever this router's current public IP is." The rule itself never broke — every client trying the old address 203.0.113.7 is now reaching an entirely different customer of the same ISP, one who has no forwarding rule at all, which is why the failure looks like a dead service rather than a wrong address.
The durable fix is dynamic DNS (DDNS): a small client running on the router or on a device inside the network that notices when the public IP changes and automatically updates a DNS record to point at the new one, so anything referencing the service by hostname keeps working without anyone needing to notice the change happened at all. A static public IP from the ISP, where available, removes the problem outright but usually costs more.
Practice exercises
- Set up a simple local service —
python3 -m http.server 8080works well — forward the corresponding port on a router you control, and verify it from a genuinely external vantage point usingnc -vzor an online port checker. Confirm the internal service's protocol withss -tulnpbefore assuming the rule is wrong. - Explain why forwarding TCP port
9000does nothing for a service listening on UDP port9000, in terms of what a NAT table entry actually keys on. - A friend says "I don't need a firewall, I only forwarded one port." Explain concretely what is and isn't protected by limiting a forwarding rule to a single port, and what specifically that rule does not defend against once traffic reaches the forwarded service.
Everything forwarded so far still means the traffic travels across the public internet in the clear, visible to anyone positioned to observe it along the way. Sometimes the requirement isn't to expose a service to the world — it's the opposite: reaching a private network securely from somewhere else entirely, without opening anything to the internet at large. That's what a VPN is built for.
Sources
- IETF, RFC 3022 – Traditional IP Network Address Translator (Traditional NAT)
- IETF, RFC 6970 – Universal Plug and Play (UPnP) Internet Gateway Device – Port Control Protocol Interworking Function (IGD-PCP IWF) — describes the security-relevant interworking between UPnP IGD and the router's port control.
- man7.org, ss(8) – Linux manual page
- Ubuntu Manpages, nc – arbitrary TCP and UDP connections and listens