63

After installing Ubuntu as WSL (Windows Subsystem for Linux), I've run (as root):

cd ~
python3 -m http.server

Output:

Serving HTTP on 0.0.0.0 port 8000 ...

And I tried to access to this web server from my windows machine, http://0.0.0.0:8000 or http://192.168.1.178:8000, but without any success. The web server is available only by the address http://127.0.0.1:8000 or http://localhost:8000. It means that I can't connect to this web server from another PC in my network. Is it possible to get access to WSL from the outside?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Roman
  • 922
  • 1
  • 8
  • 12
  • 3
    Maybe [this](https://www.nextofwindows.com/allow-server-running-inside-wsl-to-be-accessible-outside-windows-10-host) can help you. – ErazerBrecht May 01 '18 at 20:51
  • Thanks for help but server still available through the localhost only. And no ability to connect from another PC to this server. – Roman Jul 14 '18 at 08:47

13 Answers13

35

In your VM, execute ifconfig.

You will see your IP address in the first section (eth0:) inet x.x.x.x

This x.x.x.x is the IP address you have to put in your browser.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
countach
  • 447
  • 6
  • 5
  • This is the answer. Works for WSL2 and does not need any firewall magic. – Andreas H. Jun 29 '23 at 12:16
  • 1
    Hasn't [`ifconfig` been ***deprecated*** on Ubuntu/Linux](https://dougvitale.wordpress.com/2011/12/21/deprecated-linux-networking-commands-and-their-replacements/) for more than 10 years? Replaced by [`ip a`](https://en.wikipedia.org/wiki/Iproute2), `ip link`, and `ip -s`? – Peter Mortensen Aug 08 '23 at 19:01
35

None of the existing answers work for me, as WSL2 is running in its own VM and has its own network adapter. You need some sort of bridge or port forwarding for non-localhost requests to work (i.e. from another host on the same network).

I found a script in https://github.com/microsoft/WSL/issues/4150 that worked to resolve the issue:

$remoteport = bash.exe -c "ifconfig eth0 | grep 'inet '"
$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if( $found ){
  $remoteport = $matches[0];
} else{
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

#[Ports]

#All the ports you want to forward separated by coma
$ports=@(80,443,10000,3000,5000);


#[Static ip]
#You can change the addr to your ip config to listen to a specific address
$addr='0.0.0.0';
$ports_a = $ports -join ",";


#Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";

#adding Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

Running this script from an elevated/admin Powershell session did the trick for me. This allows accessing the WSL2 service running on a port to be accessible from the Windows host IP at that same port.

mlibby
  • 6,567
  • 1
  • 32
  • 41
  • 4
    Worked really well for me -- make sure you have `ifconfig` installed on your WSL2 distro. This can also be added to Task Scheduler on login as `powershell -executionpolicy bypass -f path\to\script.ps1` with a delay of at least 10 seeconds to allow WSL2 to launch first (making your Linux instance available to fetch the machine's IP from). – Andy Merskin Jul 18 '21 at 20:51
  • It does not help :( Do I need to restart my computer after that or anything? – Alexey Mar 03 '22 at 11:51
  • @Alexey This worked for me after restarting WSL: `wsl --shutdown` – GinoF May 25 '23 at 15:50
14

Please follow the steps mentioned in the link shared by erazerbrecht and run your HTTP server by providing your IP address (instead of using localhost) and port number.

Example (as root):

cd ~
python3 -m http.server -b 192.168.1.178 8000

Output:

Serving HTTP on 192.168.1.178 port 8000 (http://192.168.1.178 :8000/) ...

Otherwise, you can also do this instead of following the link:

  1. Go to the Windows Defender firewall → Advanced settings from the left menu
  2. Select Inbound
  3. Create New rule; Next
  4. Select Program as a rule type; Next
  5. Select All Program; Next
  6. Select Allow the connection; Next
  7. Check all three (Domain, Private, and Public); Next
  8. Provide the rule with a name
  9. Press Finish
  10. You’re are good to go
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Toran Sahu
  • 150
  • 2
  • 6
  • 1
    These are the instructions Microsoft provides. Windows firewall is blocking connections exposed by WSL. – Drew May 29 '19 at 19:16
  • 1
    It does work. Ensure you replace IP with your local windows IP. Also while accessing the webpage use IP:PORT to access. – Guru Jul 29 '19 at 19:01
  • I can confirm that following all of these instructions does not work. For me viewing my IP at port 8000 shows the document tree of the Windows user profile directory in the browser. The Linux HTTP server does see and log the request however. – Sienile Oct 17 '19 at 21:30
  • 1
    Neither suggestion in this answer worked for me on WSL2. Service can't bind to the Windows IP address because WSL2 has its own network adapter. Same for the firewall. The ports were already working when running the service in Windows, so no use allowing them again. – mlibby Mar 31 '21 at 14:58
  • @mlibby Have you found the solution? – JohnyL Apr 03 '21 at 20:42
  • @JohnyL I found one solution, I posted it as an answer here. https://stackoverflow.com/a/66890232/13468 – mlibby Apr 06 '21 at 16:11
  • @mlibby Thank you for your effort. Actually, it was working from the start, but the https protocol error distracted me, and I thought that page isn't reachable. Anyway, thanks for answer!! – JohnyL Apr 06 '21 at 18:22
6

it means that I can't connect to this web server from another pc in my network. Is it possible to getting an access to WSL from outside?

It should be noted, among all the answers here, that this question was originally for WSL1 (given when it was asked) and was self-answered by the OP as having been a WSL or Windows issue that was resolved by a Windows update.

Ironically, this is a known issue on WSL2 (see footnote), so years later, people started finding it (and answering it) in that context.

There are multiple solutions for this on WSL2, but with no disrespect to the other answerers here, they have all missed the two easiest ones:

  • WSL1: First, just use WSL1. For most (but not all) web development tasks where you need to access your app from another machine on the network, WSL1 will be better at networking and will run your application and tools just fine. WSL2 brings in real, virtualized Linux kernel, but the "fake kernel" (syscall translation layer) of WSL1 still works fine today for most tasks.

    When running python3 -m http.server under WSL1, you'll automatically be able to access it from other machines on the network via the Windows host's IP address (or DNS name). As the OP mentioned in the self-answer, that pesky WSL1 bug has been fixed for years now.


  • SSH reverse tunnel If you do need to use WSL2, here's an option that doesn't require port forwarding or any advanced firewall rules that need to be updated each time WSL restarts. The problem with those rules is that the IP address for WSL changes every time you restart, meaning those rules have to be deleted and recreated constantly.

    On the other hand, we can use SSH within WSL2 to connect to Windows, where the name, and perhaps even the IP address, is constant.

    One-time setup: Enable SSH

    There's some one-time setup for this, but it's useful regardless:

    First, install/enable Windows OpenSSH server. This is a feature that is built-in to Windows and just needs to be enabled. You'll find the full instructions here, but it's typically just a matter of (from an Admin PowerShell):

    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    Start-Service sshd
    Set-Service -Name sshd -StartupType 'Automatic'
    if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
        Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
        New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
    } else {
        Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
    }
    

    Edit your C:\ProgramData\ssh\sshd_config and set GatewayPorts yes. This is what allows us to use SSH as a gateway from other devices on the network.

    Finally, of course, you'll need a firewall rule in Windows allowing the HTTP(s) (or other) port you'll be using. Example (from Admin PowerShell):

    New-NetFirewallRule -DisplayName "WSL Python 8000" -LocalPort 8000 -Action Allow -Protocol TCP
    

    And restart the OpenSSH Server service:

    Restart-Service sshd
    

    Create the tunnel

    With that in place, it's a simple matter to create the tunnel we need:

    ssh -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local
    

    Replacing, of course, NotTheDr01ds with your own Windows username, if it differs from the WSL username

    That's going to use your Windows username and password since SSH is running on the Windows side.

    Once you have ensured that it works, two other recommendations:

    • Set up key-based authentication so that you don't need to enter a password each time
    • Change the SSH connection to:
      ssh -fN -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local
      
      That will put the SSH session into the background and not allocate a terminal for it.
NotTheDr01ds
  • 15,620
  • 5
  • 44
  • 70
  • Would it be a better idea to use "GatewayPorts clientspecified" instead of "GatewayPorts yes"? – solamour Jun 15 '23 at 22:22
  • @solamour Good catch -- Probably. I haven't tested it thoroughly with that configuration, but I don't see any issues with it at first glance. Working well for you, I assume? – NotTheDr01ds Jun 15 '23 at 23:06
  • In this particular case, "GatewayPorts clientspecified" and "GatewayPorts yes" shouldn't make much difference. Anyhow "SSH reverse tunneling" (ssh from WSL2 to Windows) does work, although I ended up using a plain tunneling (ssh from Windows to WSL2), just to make things simpler on Windows. Thanks for the excellent tip. – solamour Jun 16 '23 at 06:00
  • @solamour Right - Now that WSL2 attempts to re-use the same IP address each time, it's easier to go the Windows->WSL route. I should probably edit that in based on the more recent release since I originally wrote it up. – NotTheDr01ds Jun 16 '23 at 10:54
  • I expected WSL2 would get a different IP every time, but so far it has stayed same, even after rebooting Windows. Another option would be creating a bridged network interface in Windows, but that might involve more steps. As for my case, I need it only occasionally, so SSH tunneling is sufficient. – solamour Jun 16 '23 at 15:35
  • 1
    @solamour Right - It *used* to get a different IP each time, which is why I came up with this reverse tunnel technique. However, about 6 months or so ago, the WSL team added a feature to have it attempt to use the same IP each time. With that in place, the "normal tunnel" that you use is now easier than the reverse tunnel that I recommended. – NotTheDr01ds Jun 16 '23 at 17:06
5

Similar to countach’s answer, but using iproute2:

If using Ubuntu, type ip address in the WSL terminal. Look for the entry that says #: eth0 ... where # is some small number. There is an IP address there. Use that.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adam V. Steele
  • 559
  • 4
  • 18
3

Have a look at Microsoft documentation, Accessing Linux networking applications from Windows (localhost):

According to the docs if you are running an older version of Windows (Build 18945 or less), you will need to get the IP address of the Linux host VM

This should be a non-issue post the Windows 10 Build 18945 (2019-07-19).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hiten Rastogi
  • 152
  • 4
  • 15
3

For now, (March 2022) to access from outside an application running on WSL 2, we need to do the following:

  • Make rules in the firewall for accepting incoming (and maybe also outgoing) connections on the protocol and port on which the application is running (e.g., TCP/80)

  • Get WSL's VM IP address: hostname -I

  • As said on this page (Accessing a WSL 2 distribution from your local area network (LAN)), use this IP address to add, in Windows, a proxy that listens on the port and redirects to WSL's VM. This is done by the following command in a PowerShell running as administrator:

    netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=192.168.101.100
    

Where 192.168.101.100 is the VM's IP address from hostname -I and 80 the port we want to open to the outside.

As WSL's IP address changes when rebooted, this should be automated in a PowerShell script, where the previous proxy is removed and a new one is set to the current IP address. All credit goes to Edwindijas on GitHub from who's script this one is heavily inspired:

$ports=@(80,21,22) # the ports you want to open
$addr='0.0.0.0';

$wslIP = bash.exe -c "hostname -I"
$found = $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if(! $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') {
  echo "WSL's IP cannot be found. Aborting";
  exit;
}

$portsStr = $ports -join ",";
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $portsStr -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $portsStr -Action Allow -Protocol TCP";

for ($i = 0; $i -lt $ports.length; $i++) {
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$wslIP";
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
foxesque
  • 590
  • 6
  • 12
2

Avoid using firewall rules used in some answers on the web. I saw some of them create some kind of allow-all firewall rule (allow any traffic from any IP address and any port). This can cause security problems.

Simply use this single line from this answer which worked perfectly for me (it just creates a port proxy):

netsh interface portproxy add v4tov4 listenport=<windows_port> listenaddress=0.0.0.0 connectport=<WSL_port> connectaddress=<WSL_IP>

where <WSL_IP> is the IP address of WSL. You can get it by running ifconfig on WSL. Also <windows_port> is the port Windows will listen on and <wsl_port> is the port server is running on WSL.

You can list the current port proxies using:

netsh interface portproxy show all

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Erfan Azary
  • 488
  • 6
  • 11
2

I faced the same issue where I have to access the localhost from my WSL machine (ubuntu)

server running in wsl

so direct access to the url will not work in your browser as this is a VM but you can imagine it as a machine running on the same local network and access it like that as well, to do that just open a new terminal in ubuntu and run

ifconfig

if not installed in your wsl just run

sudo apt install net-tools

there you can see the inet address which can be directly access on your browser

![broadcast address

for example in my case I can access the server in my browser by going to

http://172.23.157.59:8000/

Rahul
  • 1,091
  • 1
  • 8
  • 12
  • 2
    Why the broadcast address? Is that for other PCs in your network? – deltaag Apr 04 '23 at 16:48
  • 1
    @deltaag thanks for noticing, it was a mistake I ment inet address I have updated the answer according to it. – Rahul Apr 16 '23 at 06:53
  • Please review *[Why not upload images of code/errors when asking a question?](https://meta.stackoverflow.com/questions/285551/)* (e.g., *"Images should only be used to illustrate problems that* ***can't be made clear in any other way,*** *such as to provide screenshots of a user interface."*) and [do the right thing](https://stackoverflow.com/posts/75589872/edit) (it covers answers as well). Thanks in advance. – Peter Mortensen Jun 16 '23 at 00:40
1

I followed the answer by @toran-sahu about adding an inbound rule to Windows Defender Firewall but recently (after adding a 2nd wsl2 instance) it stopped working again. I came across this issue thread and running the following in cmd prompt got it working again for me.

wsl --shutdown

update: it seems this issue comes from having Fast Startup enabled https://stackoverflow.com/a/66793101/8917310

momargoh
  • 66
  • 4
  • 1
    To be honest, this question probably wouldn't have been solved by the `wsl --shutdown`. When it's the Fast Startup issue, that makes even localhost forwarding break, so the original poster wouldn't have been able to the server via `localhost` in Windows. Since they say that worked, it's unlikely to be the issue you are thinking of. – NotTheDr01ds Jul 09 '21 at 21:58
  • `wsl --shutdown` by itself, without adding any firewall rules, actually worked for me. – 0x5453 Jan 01 '22 at 19:18
1

I have written a PowerShell script, using two simple commands which worked perfectly for me.

# Get the IP address of the WSL
$str = wsl -- ip -o -4 -json addr list eth0 | ConvertFrom-Json | %{ $_.addr_info.local } | ?{ $_ }
echo "WSL IP address: $str"

# Establish the connection with the WSL
# listenport,connectport ports can be changes as per the needs
netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$str

Create a .ps1 file using this code and execute it from the PowerShell terminal. It can be added to the scheduler, so that the script will run every time at Windows boot.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sanjay TM
  • 369
  • 2
  • 17
  • An explanation would be in order. E.g., how does it work? Why does it work? What is it supposed to do? What is the idea/gist? From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/72993134/edit), not here in comments (but *** *** *** *** *** ***[without](https://meta.stackexchange.com/a/131011)*** *** *** *** *** *** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Aug 08 '23 at 19:32
0

enter image description here

There's a npm package for that now:

npx expose-wsl@latest
Ionut-Cristian Florescu
  • 1,798
  • 1
  • 10
  • 11
-3

I've tested on Update for Microsoft Windows (KB4532132) with a reinstalled WSL, and it works as expected.

It seems the issue was related to an old Windows version or old WSL version.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Roman
  • 922
  • 1
  • 8
  • 12