1. Check WebUI on the server
First set a password: HERMES_WEBUI_PASSWORD or Settings, then restart WebUI. The health check below shows WebUI is running, not that sign-in is on.
SSH into the server using your normal account. On that server, run:
curl --fail --silent --show-error http://127.0.0.1:8787/healthExpected: an HTTP-success response from WebUI. If it refuses the connection, start or repair WebUI first; a tunnel cannot fix a stopped backend. This example uses WebUI’s default port 8787. Use your actual port if you changed it.
2. Open the tunnel from your laptop
Open a second terminal on your laptop. Replace your-user@your-server with the SSH destination you just tested:
ssh -N -L 127.0.0.1:8788:127.0.0.1:8787 your-user@your-serverLeave this terminal open. 8788 is the laptop port; the last 127.0.0.1:8787 is resolved on the server. -N runs only the forward, so a successful connection can sit quietly without a shell prompt.
You do not need to change WebUI to 0.0.0.0 or open port 8787 in the public firewall for this route.
3. Sign in through the local URL
If a fresh private window opens WebUI without asking for a password, go back to step 1 and set one.
On the laptop, open http://127.0.0.1:8788. Use WebUI’s normal sign-in. Your SSH key or password and WebUI password are separate credentials.
Expected: a password sign-in in a fresh private browser window, followed by your WebUI with the expected profile and sessions. If a fresh browser goes straight into WebUI without your intended authentication, stop and fix the password setup before proceeding. Try a short test message before using a long-running task. Keep the WebUI password enabled even though the connection travels through SSH.
4. Prove the route stops when you stop it
- In the laptop’s tunnel terminal, press Ctrl+C.
- Try a fresh request to
http://127.0.0.1:8788. It should fail to connect. An already-rendered page can remain on screen, so test a new request. - Run the tunnel command again and confirm the page becomes reachable.
This closes the laptop forward; it does not stop WebUI or revoke an already signed-in website session.
If it does not work
Address already in use- Choose another free laptop port, such as
8789, on the left of-L, then open that same port in the browser. Permission denied (publickey)- Fix the SSH account or key first. This is not a WebUI password failure.
- The SSH connection stays open, but the page fails
- Repeat step 1 on the server and check the final destination port. In a Docker install, use the host port actually published by Compose; do not assume container loopback is host loopback.
For a phone: a private HTTPS route
With Tailscale installed on the WebUI host and phone, Serve can proxy the loopback service inside your tailnet. Set a WebUI password first, and start WebUI with HERMES_WEBUI_TRUST_FORWARDED_FOR=1 while Serve is in use, so tailnet devices aren’t treated as this machine.
Restart WebUI with this setting added to the way you normally start it. If you set the password with HERMES_WEBUI_PASSWORD, pass it again at this restart. For a native foreground launch, stop the existing WebUI, open a private Bash terminal in its checkout, and enter the same password at the hidden prompt:
if read -r -s -p 'WebUI password: ' HERMES_WEBUI_PASSWORD && [ -n "$HERMES_WEBUI_PASSWORD" ]; then
printf '\n'
export HERMES_WEBUI_PASSWORD
HERMES_WEBUI_TRUST_FORWARDED_FOR=1 ./start.sh
else
printf '\nPassword required; WebUI was not started.\n' >&2
fiA blank or cancelled prompt does not start WebUI. The exported password reaches the new WebUI process without putting its value in the command or shell history. A password saved in Settings stays set without this prompt; for that case, keep your normal launch and add only HERMES_WEBUI_TRUST_FORWARDED_FOR=1.
For a service, put that setting in the service’s environment and restart it instead. Leave the server running; in another host terminal, check the existing Serve configuration before creating this route:
tailscale serve status
tailscale serve --bg http://127.0.0.1:8787
tailscale serve statusOpen the HTTPS URL Tailscale prints from a phone signed into the intended tailnet. Test in a fresh private browser window: WebUI must require the password before you can use it. A previously signed-in tab is not an authentication test. Restrict access to the intended users and devices in your tailnet policy. Serve is distinct from Funnel; this recipe does not turn on a public Funnel.
To remove the route you just created, use the same Serve flags with off, following the official reference. Do not run a global reset on a machine that already serves other applications.