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 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. Replace Ubuntu, 3000, and 192.168.1.0/24 with 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 portproxy supports 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, or 10.0.0.0/24.
Check installed distributions and their WSL version:
wsl --list --verbose
Expected result: your target distribution appears with VERSION set to 2.
Check the current Windows network profile:
Get-NetConnectionProfile
Expected 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 to 127.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.0
Expected 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.0
or an application setting such as:
Code:
host: 0.0.0.0
port: 3000
Confirm that Linux has a listener on TCP 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 3000
Expected result:
TcpTestSucceeded : True
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.

3. Record the current WSL 2 IP address​

Open PowerShell as Administrator. The portproxy 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
Expected result: a private virtual-network address, commonly beginning with 172.. For example:
172.28.112.54
Use 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 as 192.168.1.50 with a 255.255.255.0 subnet mask, the corresponding subnet is normally:
192.168.1.0/24
Create 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
Expected result: PowerShell displays the new firewall rule, with Enabled set to True.
Warning: Do not replace -RemoteAddress $LanSubnet with Any unless you deliberately want every reachable network to access the port. Do not use -Profile Any for a development server.

Windows 11 firewall UI path​

If you prefer to inspect the rule graphically:
  1. Open Settings.
  2. Select Privacy & security.
  3. Select Windows Security.
  4. Select Firewall & network protection.
  5. Select Advanced settings.
  6. In Windows Defender Firewall with Advanced Security, select Inbound Rules.
  7. Find WSL dev server TCP 3000 (LAN).
  8. Double-click it to review Protocols and Ports, Scope, and Advanced tabs.
Expected result: the rule permits TCP 3000 only on the Private profile and only from the selected subnet.

Windows 10 firewall UI path​

  1. Open Settings.
  2. Select Update & Security.
  3. Select Windows Security.
  4. Select Firewall & network protection.
  5. Select Advanced settings.
  6. In Windows Defender Firewall with Advanced Security, select Inbound Rules.
  7. Find WSL dev server TCP 3000 (LAN).
  8. 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
What this does:
  • listenaddress=0.0.0.0 tells Windows to accept connections on all of its IPv4 addresses.
  • listenport=3000 is the port LAN clients use on the Windows PC.
  • connectaddress=$WslIp is the WSL 2 virtual machine’s current address.
  • connectport=3000 is the port on which your Linux application listens.
Check the configured forwarding rule:
netsh interface portproxy show v4tov4
Expected 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
Expected result: an address on the active Wi-Fi or Ethernet adapter, for example:
Code:
IPAddress       InterfaceAlias
---------       --------------
192.168.1.50    Wi-Fi
Your LAN clients should use:
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 $Port
Expected 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 3000
Replace 192.168.1.50 with the host IP found in Step 6.
Expected result:
TcpTestSucceeded : True
Then 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 a portproxy 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.
  1. In PowerShell, open or create the global WSL configuration file:
    notepad $env:USERPROFILE\.wslconfig
  2. Add:
    Code:
    [wsl2]
    networkingMode=mirrored
  3. Save the file.
  4. Restart all WSL 2 distributions:
    wsl --shutdown
  5. Start your WSL distribution and start the development server bound to 0.0.0.0.
  6. 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
  7. 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 .wslconfig is global. If VPN, corporate security software, containers, or existing WSL networking stops behaving as expected, remove the networkingMode=mirrored line and run wsl --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
Verify:
netsh interface portproxy show v4tov4

The 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
If a process owns the port, identify it:
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 all
Delete 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:
  1. Confirm the client is on the same trusted LAN rather than a guest SSID, cellular connection, or isolated VLAN.
  2. Confirm Windows marks the active network as Private:
    Get-NetConnectionProfile
  3. Confirm the firewall rule scope includes the client’s address:
    Code:
    Get-NetFirewallRule -DisplayName "WSL dev server TCP 3000 (LAN)" |
      Get-NetFirewallAddressFilter
  4. Test basic TCP access from another Windows PC:
    Test-NetConnection 192.168.1.50 -Port 3000
  5. 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 iphlpsvc
If its status is not Running, start it from elevated PowerShell:
Code:
Set-Service -Name iphlpsvc -StartupType Automatic
Start-Service -Name iphlpsvc
Then re-run:
Get-NetTCPConnection -State Listen -LocalPort 3000

You 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
Confirm removal:
Code:
netsh interface portproxy show v4tov4
Get-NetFirewallRule -DisplayName "WSL dev server TCP 3000 (LAN)"
Expected result: no matching portproxy row and no matching firewall rule.