# Reverse Proxy (URL Path Prefix)

Source: https://www.potatoannotator.com/docs/deployment/reverse-proxy

**Potato can run behind a reverse proxy under a URL sub-path** such as `https://host/app1/`, which is common when several internal servers share one public HTTPS endpoint:

```text
https://host/app1/  ->  http://127.0.0.1:8000/
https://host/app2/  ->  http://127.0.0.1:8001/
```

The annotation UI loads static assets and performs actions through root-relative URLs (`/static/...`, `/updateinstance`, `/annotate`, `/media/...`). When Potato is mounted under `/app1`, those URLs would otherwise resolve against the public root, which shows up as 404s on CSS and JS, a hidden interface, or autosaves failing with "annotations not saved." A deployment prefix fixes this without per-site nginx hacks.

## How it works

Both options below set the WSGI `SCRIPT_NAME`, which Potato reads as the single source of truth for server-rendered `url_for(...)` output and for the client-side prefix exposed to the browser as `window.config.url_prefix`. That prefix wraps `fetch()`, `sendBeacon()`, `EventSource`, and root-relative `href`/`action`/`src` attributes. When no prefix is set, `SCRIPT_NAME` is empty and nothing changes, so this is a no-op for ordinary `potato start` runs.

## Option A — `POTATO_PROXY_FIX` (proxy sends a forwarded header)

Use this when you control the proxy and it can send forwarded headers. Potato enables Werkzeug's `ProxyFix`, which reads `X-Forwarded-Prefix` (and `-Proto`/`-Host`/`-For`) per request.

```bash
export POTATO_PROXY_FIX=1
potato start config.yaml -p 8000
```

nginx, stripping the prefix and forwarding it as a header:

```nginx
location /app1/ {
    proxy_pass         http://127.0.0.1:8000/;   # trailing slash strips /app1/
    proxy_set_header   Host              $host;
    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   X-Forwarded-Prefix /app1;
}
```

`ProxyFix` trusts forwarded headers. Only enable `POTATO_PROXY_FIX` when the app is reachable **exclusively** through the trusted proxy. If the internal port is also directly reachable, a client could spoof `X-Forwarded-Prefix` or `-Host` and poison generated URLs.

## Option B — `POTATO_URL_PREFIX` (proxy config cannot change)

Use this when you cannot add forwarded headers but you know the public mount path. Potato injects the prefix into `SCRIPT_NAME` itself.

```bash
export POTATO_URL_PREFIX=/app1
potato start config.yaml -p 8000
```

The proxy must still strip the prefix before forwarding, so Flask receives unprefixed paths such as `/static/styles.css`:

```nginx
location /app1/ {
    proxy_pass       http://127.0.0.1:8000/;     # trailing slash strips /app1/
    proxy_set_header Host $host;
}
```

If both variables are set, the per-request forwarded prefix wins and `POTATO_URL_PREFIX` is the fallback.

## Live streaming (Server-Sent Events)

The live-agent and live-coding viewers use SSE. The URL prefix is applied automatically, but SSE also needs the proxy to disable buffering on the stream location, or events are held back:

```nginx
location /app1/api/ {
    proxy_pass            http://127.0.0.1:8000/api/;
    proxy_set_header      Host $host;
    proxy_buffering       off;
    proxy_read_timeout    3600s;
}
```

## Verifying

1. Load `https://host/app1/` and confirm CSS and JS load with no 404s.
2. Make an annotation and confirm it autosaves.
3. Navigate Next/Previous and confirm media and data render.
4. If using live agent evaluation, confirm the stream connects and updates.

## Notes and limitations

- Root-relative links inside displayed annotation content are also prefixed. Content authors who mean to point at the public root should use absolute URLs.
- `pip`-installed deployments rely on packaged static assets; ensure your build includes the nested `static/` directories.

## Related

- [Production Setup](/docs/deployment/production-setup) — HTTPS and process management
- [Live Agent Evaluation](/docs/features/live-agent-evaluation) — the SSE viewers affected by buffering

For implementation details, see the [source documentation](https://github.com/davidjurgens/potato/blob/main/docs/deployment/reverse-proxy.md).
