Behind a reverse proxy
The Docker example ships Caddy. Any other reverse proxy works when it does these things:
- Terminates TLS for the host in
VAULT_PUBLIC_URL, and forwards every path to the server's port,8765by default. - Keeps the
Hostheader. The MCP endpoint answers the hostname ofVAULT_PUBLIC_URLand localhost, and answers any otherHostwith421. A proxy that rewritesHost, a Kubernetes Service name, or a LAN hostname you call the server by goes inVAULT_ALLOWED_HOSTS, space or comma separated. - Passes
/dav/*straight through for Obsidian, with theAuthorizationheader and the WebDAV verbs and headers intact:PROPFIND,MKCOL,MOVE,COPY,Depth,Destination,Overwrite. - Tells the server who the client is, in
X-Forwarded-For, and the server trusts that header only from the addresses inVAULT_FORWARDED_ALLOW_IPS. See The client's address.
When the server is not at the root of its host, put the path in VAULT_PUBLIC_URL (https://example.com/vault) and forward requests with the path unchanged: the server serves its routes under it.
A proxy that signs people in itself can pass their identity to the server; see A trusted proxy.
The client's address
The server limits sign-in attempts and other requests per client address. Behind a proxy every request comes from the proxy's address, so unless the server trusts the proxy, all clients share one limit and one person's failed attempts lock everyone else out.
Set VAULT_FORWARDED_ALLOW_IPS to the addresses your proxies connect from, as IP addresses or CIDR ranges, comma separated: VAULT_FORWARDED_ALLOW_IPS=10.0.0.0/24,192.0.2.10. The server reads X-Forwarded-For from right to left and takes the first address it does not trust as the client.
- Trust only your own proxies. A client that reaches the server directly from a trusted address can claim any address.
- With several proxies in a row, list every one of them, and make each pass
X-Forwarded-Foron to the next, adding the address it received the request from. A proxy that drops or replaces the header from the hop before it hands the server that hop's address instead of the client's. - The Docker example sets it to
*, which trusts every peer: only Caddy can reach the server there, since its port is not published.
To check it, sign in from another network and read the server's log: each request line starts with the client's address, and leaves out the query string, where sign-in links carry their tokens. When it shows a proxy's address instead, a hop is missing from the list or does not pass the header on.