The Windows Subsystem for Linux (WSL 2) isolates Linux distributions behind a virtual Network Address Translation (NAT) switch. While services hosted inside WSL 2 (such as Node.js development servers, Django apps, or Docker containers) are directly accessible from the local Windows host via localhost, they remain completely invisible to external devices on your physical Local Area Network (LAN). Using PowerShell and Windows netsh portproxy rules, developers can route incoming LAN traffic directly into WSL 2 virtual instances.
Understanding Windows NAT and PortProxy Mechanics
Exposing WSL 2 services across your physical local network requires coordinating three distinct networking components:
- PortProxy (
netsh interface portproxy): A native Windows networking service that listens for incoming TCP traffic on the host’s physical network adapter and forwards packets to the internal WSL 2 virtual IP. - Windows Defender Firewall Rules: External devices attempting to connect to your Windows host IP will be dropped by default unless explicit inbound firewall rules permit traffic on designated ports.
- Dynamic IP Resolution: Because WSL 2 regenerates its internal IP address upon reboot, portproxy bindings must be updated dynamically via automated PowerShell scripts.
Creating the Automated Port Forwarding PowerShell Script
Create a dedicated PowerShell script to query the live WSL 2 IP address and configure both portproxy and firewall rules:
# Define script location
$scriptPath = "$env:USERPROFILE\Documents\Set-WSLPortForward.ps1"
$scriptBody = @'
# Define ports to expose to your local network (e.g., 3000 for React, 8080 for web server)
$Ports = @(3000, 8080)
# Retrieve active WSL 2 internal IP address
try {
$wslIP = (wsl -e hostname -I).Trim().Split(' ')[0]
Write-Output "Detected WSL 2 IP: $wslIP"
if (-not $wslIP) {
throw "WSL IP address could not be resolved. Ensure WSL is running."
}
foreach ($Port in $Ports) {
# Configure Windows netsh portproxy forwarding
netsh interface portproxy delete v4tov4 listenport=$Port listenaddress=0.0.0.0 | Out-Null
netsh interface portproxy add v4tov4 listenport=$Port listenaddress=0.0.0.0 connectport=$Port connectaddress=$wslIP
# Ensure Windows Defender Firewall allows incoming traffic on the port
$ruleName = "WSL2_Inbound_Port_$Port"
Remove-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -LocalPort $Port -Protocol TCP -Action Allow | Out-Null
Write-Output "Successfully forwarded port $Port -> $wslIP:$Port"
}
} catch {
Write-Error "Error configuring WSL port forwarding: $_"
}
'@
$scriptBody | Out-File -FilePath $scriptPath -Encoding utf8
Executing and Testing the Port Forwarding Configuration
Execute the script from an elevated administrative terminal:
- Right-click the Start menu and select Terminal (Admin).
- Run the configuration script:
& "$env:USERPROFILE\Documents\Set-WSLPortForward.ps1" - Verify that the portproxy bindings have registered:
netsh interface portproxy show v4tov4 - Identify your physical Windows host LAN IP:
(Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias "Wi-Fi*", "Ethernet*").IPAddress - From a secondary device on the same local network (such as a smartphone or tablet), navigate to
http://<windows-host-ip>:3000to verify live connection to your WSL 2 development server.
Automating Forwarding on Windows Startup
To avoid manual re-execution following system restarts, create a Windows Scheduled Task with administrative credentials:
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File `"$env:USERPROFILE\Documents\Set-WSLPortForward.ps1`""
$trigger = New-ScheduledTaskTrigger -AtLogOn
$principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "WSL_PortForward_Init" -Action $action -Trigger $trigger -Principal $principal -Description "Automates WSL 2 LAN port forwarding on startup."
Removing Port Forwarding Rules
When testing is complete and you wish to close external network access:
# Clear specific port forwarding entry
netsh interface portproxy delete v4tov4 listenport=3000 listenaddress=0.0.0.0
# Remove corresponding firewall rule
Remove-NetFirewallRule -DisplayName "WSL2_Inbound_Port_3000"
This automated approach guarantees secure, repeatable access to WSL 2 services across physical networks without manual host editing.