Archived
docs: document reverse proxy path prefixes
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@jmfederico/pi-web": patch
|
||||
---
|
||||
|
||||
Support root and nested reverse-proxy deployments with one published client, including scoped PWA assets, WebSockets, and local or federated plugins.
|
||||
@@ -95,6 +95,60 @@ Use a private network, SSH tunnel, trusted reverse proxy, or federated PI WEB ma
|
||||
|
||||
Read more: [Remote-first development](https://pi-web.dev/remote-first)
|
||||
|
||||
## Reverse proxy deployments
|
||||
|
||||
The published production client is deployment-independent. The same package works at the origin root (`/`) or at canonical nested prefixes such as `/ai/` and `/test/ai/`; do not rebuild or configure PI WEB for a particular prefix.
|
||||
|
||||
For a root deployment, proxy `/` directly to `http://127.0.0.1:8504` without rewriting the path. For a nested deployment:
|
||||
|
||||
1. Redirect the slashless prefix (`/ai`) to its trailing-slash form (`/ai/`). The browser uses that document URL as the application base.
|
||||
2. Strip `/ai` before forwarding requests. PI WEB continues to serve `/`, `/api/...`, and `/pi-web-plugins/...` on its localhost listener.
|
||||
3. Put authentication on the whole served `/ai/` application and preserve authentication headers/cookies when forwarding.
|
||||
4. Proxy WebSocket upgrades through the same prefix. Do not create unprotected exceptions for API or plugin paths.
|
||||
|
||||
For example, this Nginx configuration belongs behind working TLS certificate directives:
|
||||
|
||||
```nginx
|
||||
# http context
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name pi.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/pi.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/pi.example.com/privkey.pem;
|
||||
|
||||
auth_basic "PI WEB";
|
||||
auth_basic_user_file /etc/nginx/pi-web.htpasswd;
|
||||
|
||||
location = /ai {
|
||||
return 308 /ai/$is_args$args;
|
||||
}
|
||||
|
||||
location ^~ /ai/ {
|
||||
# The trailing slash strips /ai/ before forwarding.
|
||||
proxy_pass http://127.0.0.1:8504/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_read_timeout 1h;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the same pattern for `/test/ai/` by changing both Nginx locations. If your proxy uses bearer tokens, SSO, or another authentication mechanism, keep that policy on the prefixed application location and continue forwarding the headers/cookies it requires; the slashless redirect serves no PI WEB content. Relative client, PWA, image, API, plugin, and WebSocket URLs then remain inside the deployment prefix; installed PWA `start_url` and scope do too.
|
||||
|
||||
## Machines and fleets
|
||||
|
||||
PI WEB can register other PI WEB runtimes as remote machines. One browser-facing PI WEB instance can proxy projects, files, git state, sessions, terminals, activity, Pi package management, and selected-machine settings from trusted remote machines.
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
<aside class="toc" aria-label="Config page contents">
|
||||
<strong>On this page</strong>
|
||||
<a href="#files">Config files</a>
|
||||
<a href="#deployment-paths">Deployment paths</a>
|
||||
<a href="#precedence">Precedence and reloads</a>
|
||||
<a href="#global-config">Global config</a>
|
||||
<a href="#project-config">Project config</a>
|
||||
@@ -133,6 +134,22 @@
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="deployment-paths">
|
||||
<h2>Reverse-proxy deployment paths</h2>
|
||||
<p>
|
||||
The deployment path is not a PI WEB config-file key or environment setting. The published client is
|
||||
portable: one build works at <code>/</code> and at canonical trailing-slash prefixes such as
|
||||
<code>/ai/</code> or <code>/test/ai/</code>.
|
||||
</p>
|
||||
<p>
|
||||
For a nested deployment, redirect the slashless prefix to the trailing-slash URL, strip the prefix
|
||||
before forwarding to PI WEB, and proxy authenticated HTTP and WebSocket traffic through the same
|
||||
location. Relative browser and PWA URLs then stay within that prefix. See the
|
||||
<a href="install#reverse-proxy-prefix">reverse proxy deployment example</a> for complete Nginx
|
||||
configuration.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="precedence">
|
||||
<h2>Precedence and reloads</h2>
|
||||
<p>Machine-global runtime values are resolved in this order:</p>
|
||||
|
||||
@@ -17,6 +17,12 @@ Pi package settings are separate from PI WEB config. They live in Pi's package-m
|
||||
|
||||
If you installed services with a custom config path, rerun `pi-web install --config /path/to/config.json` after changing that path or after upgrading from a version that only applied the custom path to the web service. This regenerates service files so the web/API and session daemon use the same `PI_WEB_CONFIG`.
|
||||
|
||||
## Reverse-proxy deployment paths
|
||||
|
||||
The deployment path is not a PI WEB config-file key or environment setting. The published client is portable: one build works at `/` and at canonical trailing-slash prefixes such as `/ai/` or `/test/ai/`.
|
||||
|
||||
For a nested deployment, redirect the slashless prefix to the trailing-slash URL, strip the prefix before forwarding to PI WEB, and proxy authenticated HTTP and WebSocket traffic through the same location. Relative browser and PWA URLs then stay within that prefix. See [Reverse proxy deployments](https://github.com/jmfederico/pi-web#reverse-proxy-deployments) for a complete Nginx example.
|
||||
|
||||
## Precedence and reloads
|
||||
|
||||
Machine-global runtime values are resolved as:
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
<a href="#pi-package">Install through Pi</a>
|
||||
<a href="#manual-run">WSL / manual run</a>
|
||||
<a href="#remote-access">Remote access</a>
|
||||
<a href="#reverse-proxy-prefix">Reverse proxy prefixes</a>
|
||||
<a href="#federated-machines">Federated machines</a>
|
||||
<a href="#manage-services">Manage services</a>
|
||||
<a href="#configure">Configure</a>
|
||||
@@ -220,6 +221,79 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="reverse-proxy-prefix">
|
||||
<h2>Reverse proxy root and path-prefix deployments</h2>
|
||||
<p>
|
||||
The published PI WEB client is deployment-independent. The same package works at the origin root
|
||||
(<code>/</code>) or at canonical nested prefixes such as <code>/ai/</code> and <code>/test/ai/</code>;
|
||||
no prefix-specific rebuild or PI WEB configuration is needed.
|
||||
</p>
|
||||
<p>
|
||||
For a root deployment, proxy <code>/</code> directly to <code>http://127.0.0.1:8504</code> without
|
||||
rewriting the path. For a nested deployment:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Redirect the slashless prefix, such as <code>/ai</code>, to <code>/ai/</code>. The browser uses the trailing-slash document URL as the application base.</li>
|
||||
<li>Strip the prefix before forwarding. PI WEB continues to serve root paths on its localhost listener.</li>
|
||||
<li>Apply authentication to the whole served <code>/ai/</code> application and preserve required authentication headers and cookies.</li>
|
||||
<li>Forward WebSocket upgrades through the same location as HTTP, API, image, PWA, and plugin traffic.</li>
|
||||
</ol>
|
||||
<div class="code-card">
|
||||
<div class="copy-row">
|
||||
<strong>Nginx path-prefix proxy</strong>
|
||||
<button class="copy-button" data-copy="#nginx-prefix-proxy">Copy</button>
|
||||
</div>
|
||||
<pre id="nginx-prefix-proxy"><code><span class="comment"># http context</span>
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name pi.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/pi.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/pi.example.com/privkey.pem;
|
||||
|
||||
auth_basic "PI WEB";
|
||||
auth_basic_user_file /etc/nginx/pi-web.htpasswd;
|
||||
|
||||
location = /ai {
|
||||
return 308 /ai/$is_args$args;
|
||||
}
|
||||
|
||||
location ^~ /ai/ {
|
||||
<span class="comment"># The trailing slash strips /ai/ before forwarding.</span>
|
||||
proxy_pass http://127.0.0.1:8504/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header Cookie $http_cookie;
|
||||
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_read_timeout 1h;
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Use the same pattern for <code>/test/ai/</code> by changing both Nginx locations. If your proxy uses
|
||||
bearer tokens, SSO, or another authentication mechanism, keep that policy on the prefixed application
|
||||
location and continue forwarding the headers or cookies it requires; the slashless redirect serves no
|
||||
PI WEB content. Do not create unprotected exceptions for <code>/api/</code> or
|
||||
<code>/pi-web-plugins/</code>.
|
||||
</p>
|
||||
<p>
|
||||
Once the proxy follows this contract, relative client assets, images, PWA assets, API calls, local and
|
||||
federated plugins, and WebSocket URLs stay inside the prefix. Installed PWA <code>start_url</code> and
|
||||
scope stay inside it as well.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="federated-machines">
|
||||
<h2>Federated machines</h2>
|
||||
<p>
|
||||
|
||||
+3
-1
@@ -174,7 +174,9 @@ PI WEB gateway you opened
|
||||
<p>
|
||||
Prefer a private path such as NetBird, Tailscale, WireGuard, private LAN, SSH tunnel, or an authenticated reverse
|
||||
proxy. If the remote is behind a path prefix, include that prefix in the machine URL, for example
|
||||
<code>https://devbox.example.test/pi-web</code>.
|
||||
<code>https://devbox.example.test/pi-web</code>. The machine registry normalizes the trailing slash; when
|
||||
opening that deployment directly in a browser, use its canonical <code>https://devbox.example.test/pi-web/</code>
|
||||
URL and configure the proxy to redirect the slashless form.
|
||||
</p>
|
||||
<div class="callout danger">
|
||||
Do not expose PI WEB directly to the public internet. Register machines only over trusted network paths
|
||||
|
||||
@@ -394,6 +394,12 @@ After editing, check the manifest endpoint and browser-console failure cases.</c
|
||||
UI should come from the selected PI WEB instance; on remote machines, the gateway copy is hidden unless
|
||||
the remote machine exposes its own copy.
|
||||
</p>
|
||||
<p>
|
||||
Current PI WEB manifests publish module references relative to the fetched manifest, so local and
|
||||
federated plugin modules follow root or nested reverse-proxy deployments without a prefix-specific
|
||||
build. The browser and federated gateway also accept leading-root module references emitted by existing
|
||||
PI WEB releases and keep them inside the current application base.
|
||||
</p>
|
||||
<p>
|
||||
For portable plugin assets, prefer URLs relative to the plugin module, such as
|
||||
<code>new URL("./asset.json", import.meta.url)</code>. If a remote plugin constructs absolute asset URLs,
|
||||
|
||||
+5
-3
@@ -325,14 +325,14 @@ Rules:
|
||||
|
||||
### Manifest and assets
|
||||
|
||||
The manifest contains each discovered plugin module:
|
||||
The manifest contains each discovered plugin module. Current PI WEB releases emit `module` relative to the fetched manifest so the same manifest works at the origin root or under a reverse-proxy path prefix:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
{
|
||||
"id": "my-plugin",
|
||||
"module": "/pi-web-plugins/my-plugin/pi-web-plugin.js?v=1234567890",
|
||||
"module": "./my-plugin/pi-web-plugin.js?v=1234567890",
|
||||
"source": "local",
|
||||
"scope": "local",
|
||||
"machineSpecific": false
|
||||
@@ -341,9 +341,11 @@ The manifest contains each discovered plugin module:
|
||||
}
|
||||
```
|
||||
|
||||
The browser resolves manifest-relative module references against the manifest URL. For backward compatibility, it also treats leading-root references such as `/pi-web-plugins/my-plugin/pi-web-plugin.js` from existing PI WEB releases as application-root input, not origin-root input. Federated gateways accept both forms from remote machines and rewrite them to deployment-portable, gateway-relative references.
|
||||
|
||||
`source` describes where the plugin came from (`bundled`, `local`, or the Pi package source). `scope` is `bundled`, `local`, `user`, or `project`. `machineSpecific` controls whether the gateway copy is valid for remote machines or only each selected machine's own copy can appear.
|
||||
|
||||
A plugin can fetch its own static assets with URLs under:
|
||||
At an origin-root deployment, a plugin's static assets are available under:
|
||||
|
||||
```text
|
||||
/pi-web-plugins/<plugin-id>/<path-inside-plugin-root>
|
||||
|
||||
Reference in New Issue
Block a user