If you start Vaultwarden with a plain docker run -p 8000:80 and open it from another machine, you don't get a login page. You get this:
You are not using a secure context which is required for the Subtle Crypto API to work. You need to enable HTTPS!
This isn't a bug, and no Vaultwarden setting turns it off. Lots of people hit it, because Vaultwarden is a very common first self-hosted app. On October 14, 2026, the Vaultwarden repository had about 69,000 GitHub stars, and the vaultwarden/server image had been pulled more than 344 million times from Docker Hub. Below I reproduce the error, explain why it happens, and fix it with a reverse proxy.
TL;DR:
- The web vault needs the browser's
crypto.subtleAPI, which only exists in a secure context. Plain HTTP from a LAN IP isn't one. - No Vaultwarden setting turns this off. Put a reverse proxy with a certificate in front. Caddy with
reverse_proxy 127.0.0.1:8000fixed it in my test. - Bind Vaultwarden to
127.0.0.1:8000and setDOMAINto the HTTPS URL people will type. -
http://localhostloads the page, but current Bitwarden clients require HTTPS, so don't rely on it. - Once your own account exists, set
SIGNUPS_ALLOWEDto"false".
Reproduce the error
Start Vaultwarden over plain HTTP
This is the "broken" compose file I started with:
services:
vaultwarden:
image: vaultwarden/server:1.37.4
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./vw-data:/data
ports:
- "8000:80"
$ docker compose up -d
$ docker logs vaultwarden
| Version 1.37.4 |
[start][INFO] Rocket has launched from http://0.0.0.0:80
I opened it from another machine at http://<your server's LAN IP>:8000/, for example http://192.168.1.50:8000/. In my lab the address happened to be 172.30.0.2. In the browser:
window.isSecureContext = false
typeof crypto.subtle = "undefined"
Error: Could not instantiate WebCryptoFunctionService. Could not locate Subtle crypto.
What about http://localhost?
Opening http://localhost:8000/ does count as a secure context, and the login page rendered:
But the console logged this right away:
Unable to fetch ServerConfig from http://localhost:8000/api ... Insecure URL not allowed. All URLs must use HTTPS.
That matches what a Vaultwarden contributor wrote in PR #7137: since Bitwarden clients v2025.11.0, the clients require HTTPS everywhere. So treat localhost over HTTP as a quick peek at most, not a working setup. I didn't try to log in or sync that way.
Why Vaultwarden requires HTTPS
Web Crypto only works in a secure context
Bitwarden encrypts your vault in the browser using the Web Crypto API (crypto.subtle). Browsers only expose that API in a "secure context", which means a page served over HTTPS or from localhost. The Vaultwarden wiki says HTTPS is "pretty much required nowadays" for this reason.
Over plain HTTP from a LAN IP, crypto.subtle is simply missing. People have reported this error for years. In 2018 it looked like Cannot read property 'importKey' of null (#153). A more recent report is "Web Vault stuck on loading with Subtle crypto errors" (#5485).
Fix it with a reverse proxy
Run Caddy in front of Vaultwarden
The wiki recommends a reverse proxy and suggests Caddy if you don't already have a preference. Vaultwarden's built-in Rocket TLS (ROCKET_TLS=...) exists, but the wiki advises against it.
Here is what I ran. Vaultwarden now listens only on 127.0.0.1, and DOMAIN is set to the HTTPS URL users will actually type:
services:
vaultwarden:
image: vaultwarden/server:1.37.4
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.test"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8000:80"
Caddyfile:
vault.example.test {
tls internal
reverse_proxy 127.0.0.1:8000
}
docker compose up -d
docker run -d --name caddy --restart unless-stopped --network host \
-v "$PWD/Caddyfile":/etc/caddy/Caddyfile:ro \
-v caddy_data:/data caddy:2-alpine
This command depends on a few things:
-
--restart unless-stoppedmakes Caddy come back after a reboot, like Vaultwarden does. My original test run didn't have it, so add it. -
--network hostmeans Caddy binds ports 80 and 443 directly on the host, so nothing else may be using them (sudo ss -tlnp | grep -E ':(80|443) '). -
vault.example.testhas to resolve to the server. On a test box, add a line like127.0.0.1 vault.example.testto/etc/hosts. On other client machines, use the server's LAN IP instead. On a real server, create a DNS A or AAAA record for your domain.
The wiki's Using Docker Compose page shows a tidier layout for a real server. It runs Caddy as a second service in the same compose file, proxies to vaultwarden:80 over the compose network, and drops the ports: section from Vaultwarden. I tested the docker run version above, not that one.
tls internal makes Caddy issue a certificate from its own local CA, because my test box has no public domain. On a real server with a real domain, delete the tls internal line. According to Caddy's docs, it then gets a Let's Encrypt certificate automatically, as long as ports 80 and 443 are reachable and DNS points at the server. That path needs a public domain, so I couldn't run it here. The Umami analytics guide shows the same Caddy setup for a real domain.
Here is the difference in one picture:
Check that it works
With the proxy in place, https://vault.example.test was a secure context (isSecureContext = true). Vaultwarden reported the right domain in /api/config:
$ curl -sk https://vault.example.test/api/config # needs the /etc/hosts entry above
... "vault": "https://vault.example.test" ...
The login page loaded with no crypto errors.
To check the crypto was really working, I created an account. That's the step that needs crypto.subtle, because the client derives keys from your master password before sending anything. The server log showed registration succeeding (timestamps trimmed):
[response][INFO] (register_verification_email) POST /identity/accounts/register/send-verification-email => 200 OK
[response][INFO] (register_finish) POST /identity/accounts/register/finish => 200 OK
My browser was set to ignore the untrusted local CA. With tls internal in real use, you'd need to install Caddy's root certificate on every device, which is exactly the hassle the wiki warns about with self-signed certificates.
Lock down your new HTTPS vault
Turn off open registration
With the fixed compose file above, SIGNUPS_ALLOWED is still at its default, which is true. The whole point of this guide is to put the vault on an HTTPS domain, so anyone who can reach the URL can create an account on your server. Once you've created your own account (and any for family or team members), disable sign-ups:
environment:
DOMAIN: "https://vault.example.test"
SIGNUPS_ALLOWED: "false"
Then apply it with docker compose up -d. To add people later, the .env.template lists SIGNUPS_DOMAINS_WHITELIST for allowing only your email domain, or you can invite them from an organization. If you enable the /admin page to manage users, set a strong ADMIN_TOKEN first, as the wiki's Enabling admin page article explains. I didn't re-test with SIGNUPS_ALLOWED set to false or with the admin page.
Keep the HTTP port private
Don't publish Vaultwarden's plain-HTTP port to the network. In the compose file that's host port 8000 ("127.0.0.1:8000:80"). Keep the 127.0.0.1: prefix so the only way in is through the HTTPS proxy, not http://<server-ip>:8000. Then add an Uptime Kuma monitor for the HTTPS URL, so you hear about a broken certificate before your phone does.
Still seeing the error?
Check the URL and DOMAIN
- Make sure you're really on
https://. Bookmarks and old browser tabs often still point athttp://ip:8000. - Set
DOMAINto the exact HTTPS URL, including any subpath. If you change hostnames later, updateDOMAINtoo (discussion #7715).
Mobile apps and self-signed certificates
Mobile apps are pickier than browsers. The wiki says the mobile app needs a properly configured certificate chain, including OCSP stapling if you run your own CA. A long-running Android thread (#687) and discussion #7544 both ended up being certificate problems. The browser worked and the app didn't. I didn't test mobile apps.
If you can't use a public domain, follow the wiki's private CA guide instead of a one-off self-signed cert, and install the CA on every client.
FAQ
Running without a public server
Can I use Vaultwarden without HTTPS?
Not in practice. Over plain HTTP from another machine, the web vault won't load at all, as shown above. Over http://localhost the page renders, but current Bitwarden clients refuse non-HTTPS server URLs.
Do I need a domain to run Vaultwarden on my home network?
No, but a domain makes it much easier. The wiki has a guide for a private instance with Let's Encrypt certificates that uses the DNS challenge, so the server never has to be reachable from the internet. It covers a free Duck DNS subdomain or a domain on Cloudflare. Without any domain, you're left with a private CA installed on every device, and the wiki says that method is meant for testing and development only.
Can I use Nginx Proxy Manager instead of Caddy?
You should be able to. Any reverse proxy that serves a valid certificate and passes WebSockets gives the browser its secure context. I tested Caddy only. In Nginx Proxy Manager, a proxy host to port 80 with a Let's Encrypt certificate and Websockets Support turned on should do the job. The Nginx Proxy Manager guide walks through that setup. Per the WebSocket notifications wiki page, WebSockets have run on Vaultwarden's main port since v1.29.0, and v1.31.0 removed the old port 3012. You don't need a separate rule for it.
Clients and accounts
Why can't the Bitwarden mobile app connect to my server?
Usually because of the certificate. The browser may accept a chain that the app rejects. Check that your proxy serves the full chain, and see the mobile apps section above.
Why can people still sign up after I set SIGNUPS_ALLOWED to false?
People ask this often (for example in discussion #4058), and Vaultwarden's own docs explain two causes. First, if you ever saved settings on the admin page, Vaultwarden wrote a config.json, and per the wiki, values in that file take precedence over environment variables. Change the setting on the admin page instead. Second, the .env.template says SIGNUPS_DOMAINS_WHITELIST lets listed domains register even when SIGNUPS_ALLOWED is false.
Originally published on DeployLark: Fix the Vaultwarden "HTTPS Required" Secure Context Error




Top comments (0)