Step-by-step guide to set up a Node.js web app on a Windows machine, served via nginx, and exposed to the public internet using Tailscale Funnel.
Download and install from nodejs.org (LTS version recommended).
Verify:
node --version
npm --version
C:\myapp\nginx-1.30.0\)No installer needed — nginx on Windows runs as a plain executable.
Create server.js in your project folder:
const http = require("http");
const os = require("os");
const PORT = 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`<h1>Hello from ${os.hostname()}</h1><p>${new Date()}</p>`);
});
server.listen(PORT, "127.0.0.1", () => {
console.log(`Running on http://127.0.0.1:${PORT}`);
});
Binding to
127.0.0.1(not0.0.0.0) keeps Node localhost-only. nginx handles external traffic.
Edit nginx-1.30.0\conf\nginx.conf. Find the location / block inside the server {} block and replace it:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Run in an elevated (admin) PowerShell or CMD:
netsh advfirewall firewall add rule name="nginx HTTP 80" dir=in action=allow protocol=TCP localport=80
Or via GUI: Windows Defender Firewall → Advanced Settings → Inbound Rules → New Rule → Port → TCP 80 → Allow.
Open a terminal in your project folder:
# Start Node app (keep this terminal open, or use a background method)
node server.js
# Start nginx (open a second terminal, cd into nginx folder)
cd nginx-1.30.0
nginx.exe
Verify both are working:
curl http://127.0.0.1:3000 # Node directly
curl http://127.0.0.1:80 # Through nginx
tailscale funnel --bg http://127.0.0.1:3000
This outputs your public URL, e.g.:
Available on the internet:
https://your-machine.taileeca1e.ts.net/
Check status anytime:
tailscale serve status
Your app is now publicly accessible at that HTTPS URL — no port forwarding, no DNS setup, no SSL certificate management needed.
Note: Tailscale Funnel must be enabled for your account/tailnet. Check under your Tailscale admin panel → DNS → HTTPS Certificates if it's not working.
| Check | Command |
|---|---|
| Node is running | curl http://127.0.0.1:3000 |
| nginx is proxying | curl http://127.0.0.1:80 |
| Tailscale network access | curl http://<tailscale-ip> |
| Public internet | curl https://<your-machine>.ts.net |
Tailscale Funnel persists automatically. For Node and nginx, you have two options:
Option A — Task Scheduler (simple)
Create two scheduled tasks (one for node server.js, one for nginx.exe) set to run at startup. Use Windows Task Scheduler GUI or:
# Example for Node (run as admin, adjust paths)
$action = New-ScheduledTaskAction -Execute "node.exe" -Argument "C:\myapp\server.js" -WorkingDirectory "C:\myapp"
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "NodeApp" -Action $action -Trigger $trigger -RunLevel Highest
Option B — NSSM (Non-Sucking Service Manager)
Install NSSM and wrap Node/nginx as proper Windows services:
choco install nssm # or download from nssm.cc
nssm install NodeApp "C:\Program Files\nodejs\node.exe" "C:\myapp\server.js"
nssm install nginx "C:\myapp\nginx-1.30.0\nginx.exe"
nssm start NodeApp
nssm start nginx
nginx won't start — port 80 in use
netstat -ano | findstr :80
taskkill /PID <pid> /F
Tailscale Funnel not working
tailscale serve reset then re-run the funnel commandtailscale serve status for current configNode app not responding
netstat -ano | findstr :3000 # confirm it's listening