Port forwarding and UPnP: how they work and what they expose

A forwarded port is a permanent entry in your router's translation table that sends every packet arriving on one external port to one internal IP and port, overriding its normal habit of admitting only replies to connections your devices started. UPnP lets any LAN device write that entry itself, with no password and no prompt, which is the argument for turning it off and hand-writing the two or three rules you need.

What the rule actually does

RFC 2663, August 1999, named the mechanism: NAPT extends address translation by "also translating transport identifier (e.g., TCP and UDP port numbers, ICMP query identifiers)." One public IPv4 address can then serve a house full of devices, and unsolicited inbound traffic has nowhere to land. Your router holds state only for flows that started inside, so a packet arriving cold on port 32400 matches nothing and gets dropped.

A forwarded port is a static override: everything hitting external port X goes to 192.168.1.50:32400, permanently, whoever sent it and whether or not anyone inside asked. The internal target sits in one of the RFC 1918 private ranges defined in February 1996: 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16. IANA reserves 0 to 1023 as system ports and 1024 to 49151 as registered ones.

"Whoever sent it" is the part that costs you. A NAT forward has no source filter, so the only thing between the internet and that service is the service's own authentication and patch level. Forwarding 3389 to a Windows box or 445 to a file server is not a smaller version of a VPN. It is the service itself, published.

Hole punching is not this. Ford, Srisuresh and Kegel described it at USENIX in 2005: both peers send outbound packets first, each NAT's own state opens a temporary return path, and nothing is reconfigured. They measured about 82 percent of tested NATs supporting it for UDP, 64 percent for TCP. A punched hole expires and is bound to the peer contacted first.

Three fates for an inbound packet at a home router A schematic with three rows sharing one router. With no rule, an unsolicited packet from the internet reaches the router and is dropped, because no flow state matches it. With a port forward, written by hand or written by UPnP, the same packet passes straight through to 192.168.1.50 port 32400 and the service is open to anyone. With hole punching, the LAN peer dials out first, its own NAT opens a temporary return path, and nothing on the router is reconfigured. Internet Your router Home LAN, RFC 1918 No rule stateful NAT Nothing arrives no flow state to match Port forward manual or UPnP 192.168.1.50:32400 open to anyone, no source filter Hole punch USENIX 2005 1. peer dials out first 2. its NAT opens a return path Measured hole punching support: about 82 percent of tested NATs for UDP, 64 percent for TCP.
The middle row is the entire UPnP argument. The rule UPnP writes is the same rule you would write by hand, so what changes is not the exposure but who authorized it.

Check for CGNAT before you debug anything else

The commonest reason a correct forwarding rule produces nothing is that it sits on the wrong box. RFC 6598, April 2012, reserved 100.64.0.0/10 so providers could "number the interfaces that connect CGN devices to Customer Premises Equipment (CPE)." If your router's WAN address falls in that block, your ISP translates above you and nothing you configure at home makes an inbound service reachable. Read the WAN address off the status page first.

UPnP writes those rules for anything that asks

TP-Link states the trust model on its own blog: "The main security concern is that UPnP requires no authentication. Any device on your network can send a request to your router to open ports, and the router will act on it automatically." The same page notes that "some malware has exploited UPnP to open ports and establish unauthorized connections from outside the network," and lands on "for most home users, disabling UPnP is the safer default."

Discovery runs over SSDP, which IANA registers on port 1900 for both UDP and TCP, assigned to the UPnP Forum. A game, a console, a torrent client and malware all use it to find the gateway before requesting a mapping. The router cannot tell those callers apart, because the protocol gives it nothing to tell them apart with.

Canada's Centre for Cyber Security says the same in ITSAP.00.008, noting that such devices "often operate with minimal authentication or access controls" and recommending "disabling UPnP, especially on perimeter devices such as home routers that manage firewalls, switches and Wi-Fi access points for other connected devices." OpenWrt's wiki is blunter: "Please take the time, figure out what devices need port redirects (most do not), and manually configure firewall redirect rules."

Distrust blanket claims about defaults, vendors included. TP-Link's blog says "Most routers enable it by default," while TP-Link's own support FAQ says "For some TP-Link models, UPnP is disabled by default for security reasons." Both are live and they disagree. Open the admin interface and read the actual state.

RFC 6887, April 2013, defines the Port Control Protocol, a standards-track alternative that treats UPnP as legacy, calling "An interworking function from Universal Plug and Play Internet Gateway Device (UPnP IGDv1) to PCP" just another PCP client. Whether your router speaks it is a per-model question.

What UPnP has actually exposed

Rapid7's HD Moore published the foundational measurement on January 29, 2013: "Over 80 million unique IPs were identified that responded to UPnP discovery requests from the internet," of which 40 to 50 million were vulnerable to at least one of three attacks. Worst case: "Over 23 million IPs are vulnerable to remote code execution through a single UDP packet," via flaws in the Portable UPnP SDK patched in version 1.6.18.

What one internet-wide UPnP scan found in January 2013 Three nested bars on one scale. Over 80 million unique IP addresses answered UPnP discovery requests sent from the internet. Of those, 40 to 50 million were vulnerable to at least one of three attacks, drawn as a solid bar to 40 million with a dashed edge at 50 million. Over 23 million were vulnerable to remote code execution through a single UDP packet. The measurement is from January 29, 2013 and no current internet-wide scan exists to check it against. Responded to discovery over 80 million Vulnerable to one of three 40 to 50 million range as reported Remote code one UDP packet over 23 million Rapid7, January 29, 2013. Unique IPs that answered UPnP discovery from the internet. Middle bar: vulnerable to at least one of three attacks. No current scan exists to check it.
The bars nest: every device counted in a lower bar is also in the one above it. The bottom one is the cheap attack, a single packet against a flaw the SDK only fixed in 1.6.18.

Then attackers weaponized the feature rather than the bugs. Akamai's UPnProxy research, published November 2018 as Eternal Silence, found "277,000 devices, out of a pool of 3.5 million, running vulnerable implementations of UPnP," and that "45,113 routers with confirmed injections expose a total of 1.7 million unique machines." No exploit was needed: they called UPnP's port-mapping function as designed, forwarding to internal SMB ports 139 and 445, tagging the rules "galleta silenciosa," and staging those machines for EternalBlue-class exploitation.

CallStranger, CVE-2020-12695, published June 8, 2020 with a CVSS 3.1 base score of 7.5, put the defect in the specification itself. Per NVD, the UPnP specification before 2020-04-17 "does not forbid the acceptance of a subscription request with a delivery URL on a different network segment than the fully qualified event-subscription URL." Yunus Cadirci, who found it, writes that the "Callback header value in UPnP SUBSCRIBE function can be controlled by an attacker and enables an SSRF-like vulnerability," usable for exfiltration past DLP controls, reflected TCP DDoS, and internal port scanning.

None of those figures describe the internet of 2026, and there is no current internet-wide UPnP scan to check them against. The trust model keeps producing bugs. CVE-2025-48819, published July 8, 2025 with a CVSS 3.1 score of 7.1, is a privilege-escalation flaw in Windows' own UPnP Device Host across Windows 10, Windows 11 and Windows Server. Its vector begins AV:A, adjacent network: the attacker is already on your segment.

The alternatives, compared

ApproachInbound port on routerWho can connectAuth before the serviceBehind CGNAT
Manual port forwardOne per serviceAnyone on the internetNone; service defends itselfNo
UPnP mappingOne per LAN requestAnyone on the internetNone, and no consent stepNo
SSH -R or hosted tunnelNoneAnyone with the URLTunnel provider's controlsYes
WireGuard or TailscaleOne UDP port, or noneHolders of an enrolled keyPublic key, before trafficYes for Tailscale
IPv6 firewall allow ruleOne allow rule, no NATAnyone on the internetNone; service defends itselfNot applicable

Outbound tunnels remove the listening port entirely

SSH needs no new software. The OpenSSH manual defines -R as specifying "that connections to the given TCP port or Unix socket on the remote (server) host are to be forwarded to the local side." The LAN machine dials out to a server you control and inbound connections ride back down it. The router never gets an inbound rule, so scanning your address finds nothing.

Cloudflare Tunnel is the managed form: install cloudflared and it "initiates an outbound connection through your firewall from the origin to the Cloudflare global network." A free tier exists; I could not confirm its seat and bandwidth limits, so read those off Cloudflare's plans page. ngrok publishes its numbers: Free gives 3 online endpoints, 1GB of transfer and 20,000 HTTP/S requests a month behind an interstitial warning page; Hobbyist at $8 a month billed annually raises that to 5GB and 100,000 requests without one.

You have swapped "anyone can reach my service" for "anyone with the tunnel's URL can, and a third party terminates the connection." For a hobby web app that is a win. For a NAS full of personal files it is not.

A VPN into the LAN is the better default

WireGuard, "an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography" in its authors' words, was merged into the mainline Linux kernel in 5.6, released March 29, 2020. A self-hosted server still needs one forwarded UDP port, which sounds like the thing you were avoiding. It is not. That port answers only to peers holding a valid key: "the server does not even respond at all to an unauthorized client; it is silent and invisible."

Tailscale builds on WireGuard and usually removes even that rule, using NAT traversal with relay fallback. Its Personal tier is $0 for up to 6 users with unlimited devices; Standard is $8 per user per month and Premium $18. For a household reaching a NAS, the free tier covers it and the answer to "which port do I forward" becomes "none."

On IPv6 the rule changes shape, not the risk

IPv6 removes NAT, and people read that as removing protection. RFC 4864, May 2007, settles it: "IPv4 NAT was not developed as a security mechanism. Despite marketing messages to the contrary, it is not a security mechanism." The protection you had was always the stateful firewall sitting in the same box.

RFC 6092, January 2011, specifies what that firewall should do on a residential IPv6 gateway. Inbound transport traffic is not forwarded inside unless an interior endpoint solicited it or you configured an exception. REC-34 says a gateway must by default answer any unsolicited inbound SYN with an ICMPv6 "Destination Unreachable" error, code 1, "after waiting at least 6 seconds." Exposing one service then means a firewall allow rule naming that device's address and port, not a translation entry.

Do not assume that default is in force. RFC 7084, November 2013, section 4.5, requirement S-1, says only that "The IPv6 CE router SHOULD support [RFC6092]." SHOULD, not MUST. Test it from outside rather than trusting the shipped configuration.

Sources