Serve the web UI under a sub-path behind a reverse proxy. Covers TLS, forwarded headers, the WebSocket path and cross-origin settings.
The pipedash-web server speaks plain HTTP and can run behind a reverse proxy that adds TLS or a sub-path. This page covers the settings on both sides.
Serve under a sub-path
server.base_path in config.toml or the variable PIPEDASH_BASE_PATH sets the path of the web UI, for example /pipedash. The variable wins over the file. An empty value serves the UI at /.
[server]
base_path = "/pipedash"Pipedash normalizes the value. It trims blanks and slashes, then adds a leading and a trailing slash, so pipedash, /pipedash and /pipedash/ all give /pipedash/. A segment may use letters, digits, -, ., _ and ~. Any other character, an empty segment, . or .. stops the server at startup with the error Invalid base path.
The server writes the value into the <base href> tag of the page it serves. Assets, API calls, the WebSocket and router links of the UI then resolve under the path.
The server itself still answers on /. It does not know the prefix in incoming requests.
The proxy must strip the prefix before it forwards a request. This includes the REST routes under /api/v1 and the WebSocket at /api/v1/ws. A request that keeps the prefix finds no route and no asset.
Through the proxy of the example below, the public addresses look like this.
| Public address | Forwarded to the server as |
|---|---|
https://tools.example.com/pipedash/ | / |
https://tools.example.com/pipedash/api/v1/health | /api/v1/health |
https://tools.example.com/pipedash/api/v1/ws | /api/v1/ws |
The UI also renders at the bare address https://tools.example.com/pipedash, as long as the proxy forwards it to /.
TLS termination
Pipedash has no TLS listener. The proxy handles TLS and forwards plain HTTP to PIPEDASH_BIND_ADDR. Bind the server to a loopback address, such as the default 127.0.0.1:8080, when the proxy runs on the same host. A container needs 0.0.0.0:8080, as the Docker page explains.
Forwarded headers
The server uses two request headers from the proxy.
Host: the cross-origin check compares it with theOriginheader of the browser.X-Forwarded-Proto: the check reads the first value of this header as the public scheme. Without the header the scheme ishttp.
The check applies to POST, PUT, PATCH, DELETE and WebSocket upgrade requests. A request that carries Sec-Fetch-Site is allowed when the value is same-origin or none. A request without that header falls back to comparing Origin with Host and the scheme. The server strips the default port of the scheme from Host before it compares. A mismatch returns 403.
A proxy that rewrites Host to its upstream address, or that sends no X-Forwarded-Proto on an HTTPS site, makes this fallback fail. Pass the original Host value, including a non-default port, and the scheme of the public address.
Requests without an Origin header, such as curl and scripts, are not affected.
nginx example
Apart from listen and server_name, every directive below maps to the server behavior above.
server {
listen 443 ssl;
server_name tools.example.com;
ssl_certificate /etc/ssl/tools.example.com.crt;
ssl_certificate_key /etc/ssl/tools.example.com.key;
location /pipedash/api/v1/ws {
proxy_pass http://127.0.0.1:8080/api/v1/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /pipedash/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}| Directive | Reason |
|---|---|
ssl_certificate and ssl_certificate_key | The server has no TLS, so the proxy holds the certificate. |
proxy_pass with a trailing / | The URI part replaces the matched /pipedash/ prefix, which the server needs stripped. |
proxy_pass in the WebSocket block | It strips the prefix from /api/v1/ws in the same way. |
proxy_http_version 1.1, Upgrade and Connection | The server accepts a WebSocket only after an upgrade request, and it detects it from the Upgrade header. |
Host $http_host | The server compares the original host, with its port, against Origin. |
X-Forwarded-Proto $scheme | The server derives the public scheme from this header. |
Serve the UI at the root of a host instead by setting no base path and using one location / with proxy_pass http://127.0.0.1:8080;.
Frontend on another origin
A browser that loads the UI from another origin than the API gets 403 on every write and on the WebSocket. The server's Origin check rejects them. To allow that setup, set PIPEDASH_CORS_ALLOW_ALL=true or server.cors_allow_all = true.
This setting allows any origin, method and header and turns the cross-origin write check off. When the API is open to the network, set PIPEDASH_VAULT_PASSWORD so every protected route needs the bearer token. The REST API page covers the token.
To serve the UI from another host, set PIPEDASH_EMBEDDED_FRONTEND=false. The server then answers 404 for everything outside /api/v1.
Desktop app
The desktop app ignores base_path, together with bind_addr and cors_allow_all. It runs no web server.