Skip to content

How to Secure Self-Hosted Hermes WebUI with Password Auth and WebAuthn Passkeys

Problem

Self-hosted AI tools often ship with no authentication by default. When you bind to 0.0.0.0 or tunnel remotely, you leave the interface exposed. I needed to secure my Hermes WebUI instance before accessing it from my phone via Tailscale.

Authentication Layers

Hermes WebUI supports two authentication layers:

  1. Password authentication — enabled via HERMES_WEBUI_PASSWORD env var or the Settings panel
  2. Passkeys / WebAuthn — registered from Settings -> System after signing in with a password

Password auth is off by default for zero-friction localhost use. Once you register at least one passkey, you can remove the password and keep passkey-only sign-in.

Enabling Password Auth

The simplest way to add protection is the environment variable:

Enable password auth
export HERMES_WEBUI_PASSWORD=your-strong-password
./start.sh

For Docker deployments:

Docker with password
echo "HERMES_WEBUI_PASSWORD=your-strong-password" >> .env
docker compose up -d --force-recreate

Remote Access Patterns

For secure remote access, I use an SSH tunnel:

SSH tunnel
ssh -N -L 8787:127.0.0.1:8787 user@your-server

If you prefer Tailscale, bind to all interfaces and protect with a password:

Tailscale with all interfaces
HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PASSWORD=your-password ./start.sh

Security Details

All sessions use signed HMAC HTTP-only cookies with a 24-hour TTL. Security headers are set on all responses:

  • X-Content-Type-Options
  • X-Frame-Options
  • Referrer-Policy

POST body size is limited to 20MB. CDN resources are pinned with SRI integrity hashes.

Passkeys

Passkeys are same-origin and stored locally in the WebUI state directory. They are not backed by a cloud service. The password remains the bootstrap and recovery path until you explicitly choose to go passwordless.

Common Mistakes

I nearly made these mistakes when setting up remote access:

  1. Exposing port 8787 to the internet without any authentication — the default bind is 127.0.0.1 for a reason.
  2. Using a weak password — the env var approach is only as strong as the password you choose.
  3. Not understanding where passkeys are stored — they live in the WebUI state dir, not in a cloud identity provider.

Summary

In this post, I showed how to secure a self-hosted Hermes WebUI instance. The key point is that the security model is designed for self-hosters: off by default for localhost, easy to enable for remote access, and upgradeable to passwordless passkeys once you are set up. Always enable password protection before binding to 0.0.0.0 or tunneling remotely.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments