Run Nginx as a Reverse Proxy on Windows via WSL2
Published September 13, 2026 · Editorial policy

Running your development web services inside WSL2 while trying to access them cleanly from your Windows browser often leads to a mess of changing localhost ports and confusing CORS issues. Windows developers working with microservices or multiple local apps hit this wall daily when they need a single, unified entry point. You end up wasting hours fighting network virtualization layers instead of writing code. By setting up Nginx inside your WSL2 Ubuntu distribution as a reverse proxy, you get a production-like routing environment that maps clean local domains on Windows directly to your Linux containers and Node apps.
The Routing Blueprint: How WSL2 and Windows Talk
Before writing a single configuration line, you need to understand how network traffic flows between Windows and WSL2. By default, WSL2 runs inside a lightweight utility VM with its own internal IP address, isolated by a virtual NAT bridge.
When you start an app on localhost:3000 inside WSL2, Windows cannot automatically route traffic to it via localhost:3000 without WSL's built-in port proxying translation. However, WSL2 now supports localhost forwarding by default in modern Windows 10 and 11 builds, meaning traffic hitting localhost on Windows can reach WSL.
The strategy here is to install Nginx directly inside WSL2. Nginx will listen on standard ports (like 80 or custom development ports) inside the Linux environment. It will then route incoming traffic based on server names (like app1.test and api.test) to the respective backend processes running inside your WSL2 workspace.
[Windows Browser]
│
▼ (http://app1.test)
[Windows Hosts File]
│
▼ (127.0.0.1)
[WSL2 Nginx Reverse Proxy]
├── / ───────> [Node.js App on Port 3000]
└── /api ────> [Python API on Port 5000]
Step 1: Install and Verify Nginx in WSL2
Open your WSL2 terminal (Ubuntu is assumed for these commands) and update your package lists to ensure you pull stable, current binaries.
sudo apt update && sudo apt install -y nginx
Verify that the Nginx service daemon is operational. Note that standard systemd support varies depending on your WSL2 version, but modern Windows builds enable systemd by default. Check your service status:
sudo systemctl status nginx
If systemd is not enabled in your WSL2 environment, you may see an error regarding the service manager. If that happens, start Nginx directly via its binary script:
sudo service nginx start
Open your Windows browser (Chrome, Edge, or Firefox) and navigate to http://localhost. You should see the default "Welcome to nginx!" HTML page. This proves that traffic is successfully bridging from your Windows host into the WSL2 virtual machine on port 80.
Step 2: Configure Local Domains in Windows
To make your reverse proxy useful, you want to use readable local domain names instead of port numbers. Windows requires an entry in the system's hosts file to resolve custom domains locally to 127.0.0.1.
Press the Windows Key, type Notepad, right-click it, and select Run as administrator.
Open the following file:
C:\Windows\System32\drivers\etc\hosts
Add the following lines at the bottom of the file and save it:
127.0.0.1 app1.test
127.0.0.1 api.test
Save the file. Now, whenever you type app1.test or api.test into your Windows browser, your machine will route that traffic locally rather than querying an external DNS server.
Step 3: Write the Nginx Reverse Proxy Configuration
Nginx manages site configurations inside /etc/nginx/sites-available/. Instead of modifying the default configuration file, create a dedicated configuration file for your local development projects.
Create a new file named dev-proxy using your preferred terminal editor:
sudo nano /etc/nginx/sites-available/dev-proxy
Paste the following configuration block into the file. This configuration sets up two server blocks: one for app1.test routing to a frontend dev server on port 3000, and one for api.test routing to a backend service on port 5000.
server {
listen 80;
server_name app1.test;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name api.test;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Save and close the file (Ctrl+O, Enter, then Ctrl+X in nano).
To activate this configuration, create a symbolic link from sites-available to sites-enabled:
sudo ln -s /etc/nginx/sites-available/dev-proxy /etc/nginx/sites-enabled/
Remove the default Nginx configuration symlink to prevent server name collision on port 80:
sudo rm /etc/nginx/sites-enabled/default
Test your configuration for syntax errors:
sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: the configuration file /etc/nginx/nginx.conf test was successful
Reload Nginx to apply the changes:
sudo systemctl reload nginx
Troubleshooting Common WSL2-Nginx Roadblocks
Even with a clean setup, networking layers between Windows and WSL2 occasionally drop connections or conflict with other software. Check these three common failure points if your proxy returns errors.
1. Connection Refused (502 Bad Gateway)
The Cause: Nginx is running, but the backend application (e.g., your Node.js or Python app) is not running, or it is bound only to 127.0.0.1 inside a separate Docker container instead of the WSL2 host network.
The Fix: Ensure your development server is running and bound to 0.0.0.0 or 127.0.0.1 inside WSL2. If your app runs inside a Docker container inside WSL2, your proxy_pass target must point to the Docker container's IP or you must use Docker's host networking mode (--net=host).
2. Windows Port 80 Conflicts
The Cause: World Wide Web Publishing Service (IIS) or another local Windows service (like Skype or a local Apache/XAMPP stack) is already binding to port 80 on the Windows host, blocking Nginx inside WSL2 from receiving external traffic.
The Fix: Open PowerShell as an administrator and run netstat -ano | findstr :80 to find the Process ID (PID) holding port 80. Stop the offending Windows service via the Windows Services manager or kill the PID using Stop-Process -Id <PID> -Force.
3. WSL2 IP Changes After Reboot
The Cause: In older Windows versions, WSL2 instances received a dynamically assigned NAT IP address on every reboot, occasionally breaking custom portproxy rules.
The Fix: Ensure you are running Windows 11 or an updated Windows 10 build that supports WSL mirror networking mode. Add networkingMode=mirror under the [wsl2] section of your %UserProfile%\.wslconfig file on Windows to share the host's network interfaces directly with WSL2.
Managing Your Proxy Lifecycle
Running Nginx locally means managing its state alongside your development workflow. Because WSL2 instances can hibernate or shut down when idle, Nginx may not automatically restart when you reopen your terminal.
To streamline your daily workflow, create a quick shell alias in your WSL2 ~/.bashrc or ~/.zshrc file:
alias proxy-restart="sudo systemctl restart nginx"
alias proxy-logs="sudo tail -f /var/log/nginx/error.log"
Reload your shell profile with source ~/.bashrc. Now, whenever you spin up your development stack in the morning, a simple proxy-restart command guarantees your routing infrastructure is live, letting you access your microservices through clean, production-matching domain names directly from Windows.


