WSL 2 development servers are reachable from other devices on your LAN by making the app listen on its WSL network interface, creating a Windows
Expected result: your target distribution appears with
Check the current Windows network profile:
Expected result: the active Wi-Fi or Ethernet connection shows
For a quick test server, open your WSL distribution and run:
Expected result:
For your actual framework, use its equivalent LAN-bind option. Common patterns include:
or an application setting such as:
Confirm that Linux has a listener on TCP 3000:
Expected result: a line containing
If the result shows only
Expected result:
You can also open:
in a browser on the Windows host.
This test proves the server is running. It does not prove that a separate LAN device can reach it.
Run:
Expected result: a private virtual-network address, commonly beginning with
Use uppercase
Create a rule for the development port, Private profile only, and only that subnet:
Expected result: PowerShell displays the new firewall rule, with
What this does:
Expected result: a row similar to the following:
Expected result: an address on the active Wi-Fi or Ethernet adapter, for example:
Your LAN clients should use:
Do not give LAN clients the WSL
Expected result: one or more
Replace
Expected result:
Then browse to:
Expected result: the same page or API response served by your WSL application.
For an API, test the known health endpoint or root route. For example:
Expected result: a response from the WSL application. This is the definitive test because it exercises the LAN route, Windows Firewall rule, port proxy, WSL virtual network, and application listener.
This option is not available for Windows 10, so use the primary
The app works at
The most common cause is an application bound only to
In WSL, check the listener:
If it shows
This is expected under WSL 2 NAT networking. The forwarding rule persists, but the WSL virtual machine’s
Update the existing proxy instead of adding a duplicate:
Verify:
The port is already in use or
Check whether another process owns the port:
If a process owns the port, identify it:
Either stop or reconfigure that process, or choose an unused port such as 3001 and repeat the firewall and portproxy steps with the new port.
Also check for an existing portproxy rule:
Delete a conflicting rule only when you have confirmed it is the one you created:
If its status is not
Then re-run:
Confirm removal:
Expected result: no matching portproxy row and no matching firewall rule.
portproxy rule from the Windows host’s LAN address to the current WSL IP address, and allowing that TCP port through Windows Defender Firewall. This procedure supports Windows 10 and Windows 11 with WSL 2. It applies to the default WSL 2 NAT networking mode, which is the dependable choice when you need the same workflow across both Windows versions.
Example used below: a development server on TCP port 3000, running in the WSL distribution named Ubuntu. ReplaceUbuntu,3000, and192.168.1.0/24with your distribution, app port, and actual trusted LAN subnet.
Prerequisites and compatibility
Before changing network exposure, confirm the following:- Your distribution is running under WSL 2, not WSL 1.
- You have a Private Windows network profile for your trusted home or work LAN.
- You can open Windows PowerShell as Administrator.
- Your development server uses TCP. Windows
netsh interface portproxysupports TCP forwarding only; it will not forward UDP traffic. - You know the address range of your LAN. Common examples are
192.168.1.0/24,192.168.0.0/24, or10.0.0.0/24.
wsl --list --verboseExpected result: your target distribution appears with
VERSION set to 2.Check the current Windows network profile:
Get-NetConnectionProfileExpected result: the active Wi-Fi or Ethernet connection shows
NetworkCategory : Private.Warning: Do not expose a development server on a Public network, hotel Wi-Fi, guest network, or directly through your router to the internet. The firewall rule in this guide is intentionally limited to the Private profile and a specific trusted LAN subnet. Development servers often have debugging endpoints, file-watching features, weak defaults, or no authentication.
Primary procedure: publish a WSL 2 TCP development server to the LAN
1. Start the application so it listens beyond WSL loopback
A service bound only to127.0.0.1 inside Linux accepts requests from WSL itself but not from the Windows port proxy. Bind it to 0.0.0.0 instead.For a quick test server, open your WSL distribution and run:
python3 -m http.server 3000 --bind 0.0.0.0Expected result:
Serving HTTP on 0.0.0.0 port 3000 ...For your actual framework, use its equivalent LAN-bind option. Common patterns include:
npm run dev -- --host 0.0.0.0or an application setting such as:
Code:
host: 0.0.0.0
port: 3000
ss -ltnp | grep ':3000'Expected result: a line containing
0.0.0.0:3000 or the WSL interface address with port 3000.If the result shows only
127.0.0.1:3000, change the application’s bind address before continuing.2. Confirm the service works from Windows itself
From a normal PowerShell window on Windows, test the server through WSL localhost forwarding:Test-NetConnection localhost -Port 3000Expected result:
TcpTestSucceeded : TrueYou can also open:
in a browser on the Windows host.
This test proves the server is running. It does not prove that a separate LAN device can reach it.
3. Record the current WSL 2 IP address
Open PowerShell as Administrator. Theportproxy rule must forward to the WSL virtual machine’s current IPv4 address.Run:
Code:
$Distro = "Ubuntu"
$Port = 3000
$WslIp = (wsl.exe --distribution $Distro hostname -I).Trim().Split(' ')[0]
$WslIp
172.. For example:172.28.112.54Use uppercase
-I in hostname -I. Lowercase -i does not return the correct WSL 2 address for this purpose.4. Create a narrow Windows Firewall rule
First determine your trusted subnet. If your Windows host has an IPv4 address such as192.168.1.50 with a 255.255.255.0 subnet mask, the corresponding subnet is normally:192.168.1.0/24Create a rule for the development port, Private profile only, and only that subnet:
Code:
$LanSubnet = "192.168.1.0/24"
New-NetFirewallRule `
-DisplayName "WSL dev server TCP $Port (LAN)" `
-Direction Inbound `
-Action Allow `
-Protocol TCP `
-LocalPort $Port `
-Profile Private `
-RemoteAddress $LanSubnet
Enabled set to True.Warning: Do not replace-RemoteAddress $LanSubnetwithAnyunless you deliberately want every reachable network to access the port. Do not use-Profile Anyfor a development server.
Windows 11 firewall UI path
If you prefer to inspect the rule graphically:- Open Settings.
- Select Privacy & security.
- Select Windows Security.
- Select Firewall & network protection.
- Select Advanced settings.
- In Windows Defender Firewall with Advanced Security, select Inbound Rules.
- Find WSL dev server TCP 3000 (LAN).
- Double-click it to review Protocols and Ports, Scope, and Advanced tabs.
Windows 10 firewall UI path
- Open Settings.
- Select Update & Security.
- Select Windows Security.
- Select Firewall & network protection.
- Select Advanced settings.
- In Windows Defender Firewall with Advanced Security, select Inbound Rules.
- Find WSL dev server TCP 3000 (LAN).
- Double-click it to review the rule settings.
5. Add the Windows-to-WSL port proxy
Still in elevated PowerShell, add the forwarding rule:
Code:
netsh interface portproxy add v4tov4 `
listenaddress=0.0.0.0 `
listenport=$Port `
connectaddress=$WslIp `
connectport=$Port `
protocol=tcp
listenaddress=0.0.0.0tells Windows to accept connections on all of its IPv4 addresses.listenport=3000is the port LAN clients use on the Windows PC.connectaddress=$WslIpis the WSL 2 virtual machine’s current address.connectport=3000is the port on which your Linux application listens.
netsh interface portproxy show v4tov4Expected result: a row similar to the following:
Code:
Listen on ipv4: Connect to ipv4:
Address Port Address Port
--------------- ---------- --------------- ----------
0.0.0.0 3000 172.28.112.54 3000
6. Find the Windows host’s LAN IPv4 address
Run this in PowerShell:
Code:
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object {
$_.IPAddress -notlike "127.*" -and
$_.IPAddress -notlike "169.254.*" -and
$_.InterfaceAlias -notmatch "vEthernet|WSL|Loopback"
} |
Format-Table IPAddress, InterfaceAlias
Code:
IPAddress InterfaceAlias
--------- --------------
192.168.1.50 Wi-Fi
Do not give LAN clients the WSL
172.x.x.x address. That address is internal to the WSL NAT network and can change.Verification of success
Perform these checks in order.Check 1: Verify the proxy is listening on Windows
In elevated PowerShell:Get-NetTCPConnection -State Listen -LocalPort $PortExpected result: one or more
Listen entries on port 3000. A listener owned by the Windows networking infrastructure is normal for a portproxy rule.Check 2: Test through the host’s own LAN address
On the Windows host:Test-NetConnection 192.168.1.50 -Port 3000Replace
192.168.1.50 with the host IP found in Step 6.Expected result:
TcpTestSucceeded : TrueThen browse to:
Expected result: the same page or API response served by your WSL application.
Check 3: Test from a second device
From a phone, tablet, or another PC connected to the same trusted LAN, open:For an API, test the known health endpoint or root route. For example:
Expected result: a response from the WSL application. This is the definitive test because it exercises the LAN route, Windows Firewall rule, port proxy, WSL virtual network, and application listener.
Alternate method: mirrored networking on Windows 11
If you use Windows 11 version 22H2 or later and a current WSL release, mirrored networking can allow LAN devices to connect directly to WSL without aportproxy rule. It is genuinely useful for services that need IPv6, multicast, or protocols other than TCP.This option is not available for Windows 10, so use the primary
portproxy procedure when you need cross-version instructions.- In PowerShell, open or create the global WSL configuration file:
notepad $env:USERPROFILE\.wslconfig - Add:
Code:[wsl2] networkingMode=mirrored - Save the file.
- Restart all WSL 2 distributions:
wsl --shutdown - Start your WSL distribution and start the development server bound to
0.0.0.0. - Create a Hyper-V firewall rule for the WSL service port:
Code:New-NetFirewallHyperVRule ` -Name "WSLDevServer3000" ` -DisplayName "WSL Dev Server TCP 3000" ` -Direction Inbound ` -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' ` -Protocol TCP ` -LocalPorts 3000 - Test from another LAN device using the Windows host’s LAN IPv4 address and port 3000.
Warning: Mirrored networking changes networking behavior for every WSL 2 distribution under your Windows user account because.wslconfigis global. If VPN, corporate security software, containers, or existing WSL networking stops behaving as expected, remove thenetworkingMode=mirroredline and runwsl --shutdown.
Troubleshooting and rollback
The app works at localhost:3000 but not through the Windows LAN IP
The most common cause is an application bound only to 127.0.0.1 inside Linux.In WSL, check the listener:
ss -ltnp | grep ':3000'If it shows
127.0.0.1:3000, restart the application using 0.0.0.0 as its bind address. Then re-test:
Code:
Test-NetConnection localhost -Port 3000
Test-NetConnection 192.168.1.50 -Port 3000
netsh interface portproxy show v4tov4 shows the wrong WSL IP after restart
This is expected under WSL 2 NAT networking. The forwarding rule persists, but the WSL virtual machine’s 172.x.x.x address may change after wsl --shutdown, a Windows restart, network reset, or some WSL updates.Update the existing proxy instead of adding a duplicate:
Code:
$Distro = "Ubuntu"
$Port = 3000
$WslIp = (wsl.exe --distribution $Distro hostname -I).Trim().Split(' ')[0]
netsh interface portproxy set v4tov4 `
listenaddress=0.0.0.0 `
listenport=$Port `
connectaddress=$WslIp `
connectport=$Port `
protocol=tcp
netsh interface portproxy show v4tov4The port is already in use or netsh cannot create the listener
Check whether another process owns the port:
Code:
Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue |
Select-Object LocalAddress, LocalPort, State, OwningProcess
Get-Process -Id <OwningProcess>Either stop or reconfigure that process, or choose an unused port such as 3001 and repeat the firewall and portproxy steps with the new port.
Also check for an existing portproxy rule:
netsh interface portproxy show allDelete a conflicting rule only when you have confirmed it is the one you created:
Code:
netsh interface portproxy delete v4tov4 `
listenaddress=0.0.0.0 `
listenport=3000 `
protocol=tcp
A LAN client times out, but the host test succeeds
Check these items:- Confirm the client is on the same trusted LAN rather than a guest SSID, cellular connection, or isolated VLAN.
- Confirm Windows marks the active network as Private:
Get-NetConnectionProfile - Confirm the firewall rule scope includes the client’s address:
Code:Get-NetFirewallRule -DisplayName "WSL dev server TCP 3000 (LAN)" | Get-NetFirewallAddressFilter - Test basic TCP access from another Windows PC:
Test-NetConnection 192.168.1.50 -Port 3000 - Check the router or access point for client isolation, AP isolation, or a guest-network policy. Those features can prevent devices on the same Wi-Fi from connecting to one another.
The portproxy rule exists but Windows does not listen
Confirm that the IP Helper service is running:Get-Service iphlpsvcIf its status is not
Running, start it from elevated PowerShell:
Code:
Set-Service -Name iphlpsvc -StartupType Automatic
Start-Service -Name iphlpsvc
Get-NetTCPConnection -State Listen -LocalPort 3000You need UDP, WebRTC discovery, multicast, or IPv6
netsh interface portproxy forwards TCP only. Do not try to solve UDP failures by opening a broad Windows Firewall rule; the proxy still will not carry UDP packets. On eligible Windows 11 systems, use the mirrored-networking alternate method and create narrowly scoped Hyper-V firewall rules, or use an application-specific reverse proxy/tunnel that supports the needed protocol.Roll back the primary portproxy configuration
Run these commands in elevated PowerShell:
Code:
Remove-NetFirewallRule -DisplayName "WSL dev server TCP 3000 (LAN)"
netsh interface portproxy delete v4tov4 `
listenaddress=0.0.0.0 `
listenport=3000 `
protocol=tcp
Code:
netsh interface portproxy show v4tov4
Get-NetFirewallRule -DisplayName "WSL dev server TCP 3000 (LAN)"