* docs: refresh docs for 3.2.0 — correct stale content, document recent features The hand-maintained VitePress docs under doc/ had drifted behind a lot of recent work. They are authored prose (not generated from the OpenAPI spec), so they need manual upkeep. This pass corrects content that was actively wrong and documents features shipped since they were last touched. Corrections (was wrong / misleading): - cli.md: every command used `node bin/foo.js`, but the scripts are TypeScript run via pnpm — copy-paste failed. Rewrote to `pnpm run --filter bin <script>`, documented ~13 previously-undocumented operator tools, and split running-vs-stopped requirements. Registered the missing `compactStalePads` script in bin/package.json so the documented invocation actually works. - stats.md: described a pre-Prometheus world. Rewrote for the gated `/stats` (JSON) and `/stats/prometheus` endpoints, the live metric set, the opt-in `scalingDiveMetrics` instruments (#7756), and `measured-core`. - admin/updates.md: removed three false "SMTP not yet wired" claims (it is, via nodemailer + the `mail.*` block), documented the `node-engine-mismatch` preflight check and the rollback/preflight failure emails, and stripped obsolete "PR 1 / PR 2" staging language now that all tiers ship. - api/http_api.md: added the undocumented `anonymizeAuthor` (GDPR Art. 17) call, fixed copyPad/movePad version annotations (1.2.8 → 1.2.9), corrected getPadID's param name (readOnlyID → roID), and dropped a reference to a non-existent `getEtherpad` API call. - skins.md: colibris is the current default, not an "experimental" skin for a future 2.0. - localization.md: bare `window._('key')` is unbound and returns undefined; recommend `window.html10n.get(...)` / data-l10n-id instead. - README.md: bumped the v2.2.5 upgrade example to v3.2.0; fixed a docker.adoc link to docker.md. - docker.md: added MAIL_*, ENABLE_METRICS, GDPR_AUTHOR_ERASURE_ENABLED, PRIVACY_BANNER_*, PUBLIC_URL, AUTHENTICATION_METHOD, ENABLE_DARK_MODE, ENABLE_PAD_WIDE_SETTINGS; fixed the SOCKETIO_MAX_HTTP_BUFFER_SIZE default (50000 → 1000000). New documentation: - configuration.md (new): how settings + `${VAR:default}` substitution work, trustProxy, and — the previously-undocumented feature — running under a subpath/ingress via x-proxy-path / X-Forwarded-Prefix / X-Ingress-Path, with the sanitizer rules and Traefik/NGINX examples. Wired into the VitePress sidebar and the index hero. - hooks_server-side.md: ccRegisterBlockElements (the server-side companion plugin authors miss), exportConvert, exportHTMLSend, createServer, restartServer, and clientReady (marked deprecated). - hooks_client-side.md: aceDrop, acePaste, handleClientTimesliderMessage_<name>. VitePress build passes. The legacy .adoc set was intentionally left in place — it still feeds the per-version doc archives published to ether.github.com at release time (bin/release.ts), so it is not dead and is out of scope here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: address Qodo review — drop .js invocations from configuration.md and CLI help - configuration.md: the settings-override example referenced a nonexistent `node src/node/server.js`. Use the supported launcher instead (`bin/run.sh -s <file>`), and note the runtime is server.ts via tsx. - compactStalePads.ts / compactPad.ts / compactAllPads.ts: their header comments and runtime usage output still printed `node bin/*.js`, which points at files that don't exist. Switched to the documented `pnpm run --filter bin <script>` form so the --help text matches the docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.9 KiB
Configuration
This page explains how Etherpad is configured and documents the
reverse-proxy and subpath behaviour in detail. It is not an exhaustive list
of every setting — for that, see the fully-commented
settings.json.template,
which is the authoritative reference.
Where settings live
Etherpad reads its configuration from settings.json in the installation root.
A new install copies settings.json.template to settings.json on first run.
- Override the file location by passing the
-s/--settingsflag to the launcher, e.g.bin/run.sh -s /etc/etherpad/settings.json. This lets you run multiple instances from one installation. (bin/run.shforwards the flag topnpm run prod, which is the supported entrypoint — there is noserver.js; the runtime issrc/node/server.ts, loaded viatsx.) - Environment-variable substitution — any string value may reference an
environment variable using the syntax
"${ENV_VAR}"or"${ENV_VAR:default}". The variable name must be quoted, even when the resolved value is a number or boolean. A few rules worth remembering:"${PORT:9001}"→ the value ofPORT, or9001if unset."${MINIFY:true}"→ the booleantrue/false, not the string."${UNSET_VAR:null}"→null;"${UNSET_VAR:}"→ the empty string.- Substitution happens at load time, in memory only — env vars never
overwrite
settings.jsonon disk.
When running in Docker, almost every setting is wired to an environment
variable in the shipped settings.json.docker. See the
Docker page for the full env-var list.
Trusting a reverse proxy
If Etherpad runs behind NGINX, Traefik, HAProxy, a Kubernetes ingress, or any other reverse proxy, set:
"trustProxy": true
This makes Etherpad trust the standard X-Forwarded-* headers, so it:
- uses the real client IP (from
X-Forwarded-For) in logs and rate limits instead of the proxy's IP; - respects the forwarded protocol and host, so the
secureflag is set on cookies when the proxy terminates TLS (required forSameSite=None).
Leave it at the default false when Etherpad is reachable directly on a public
IP — otherwise any client could forge these headers.
Running under a subpath / ingress
Etherpad can be served under a URL-path prefix (for example
https://example.com/etherpad/) without recompiling anything. The prefix is
discovered per-request from upstream headers, so the same Etherpad process works
whether it is mounted at the root or under a path.
Three headers are checked, in this order; the first non-empty value (after sanitization) wins:
| Order | Header | Origin | Requires trustProxy: true? |
|---|---|---|---|
| 1 | x-proxy-path |
Etherpad's own convention | No — always honoured |
| 2 | X-Forwarded-Prefix |
HAProxy / Traefik / Spring | Yes |
| 3 | X-Ingress-Path |
Kubernetes / Home Assistant ingress | Yes |
x-proxy-path is always honoured because an operator must deliberately
configure their proxy to send Etherpad's custom header. The two standard
headers (X-Forwarded-Prefix, X-Ingress-Path) are honoured only when
trustProxy is true, because otherwise a client on a public IP could forge
them.
Once detected, the prefix is woven into the responses that would otherwise break under a subpath:
manifest.json(PWA install metadata);- the social-media meta tags (
og:url/og:image), unless an explicitpublicURLis configured; - the bootstrap script entrypoint and the asset / reconnect links in the pad, index, and timeslider pages.
Sanitization
The header value is treated as untrusted input even when read from a trusted
header, because it ends up inside HTML, JS, CSS, and HTTP Location headers.
The sanitizer (src/node/utils/sanitizeProxyPath.ts):
- strips every character outside
[A-Za-z0-9_./-]; - collapses a leading
//+to a single/, so the value can never be read as a protocol-relative URL; - prepends
/if the result doesn't already start with one; - rejects any value containing a
..path segment (returns empty).
The output is therefore always either empty, or a string that starts with
exactly one / and contains only [A-Za-z0-9_./-].
Example: Traefik
http:
middlewares:
etherpad-prefix:
stripPrefix:
prefixes:
- "/etherpad"
etherpad-headers:
headers:
customRequestHeaders:
X-Forwarded-Prefix: "/etherpad"
Apply both middlewares to the router and set trustProxy: true in
settings.json.
Example: NGINX
location /etherpad/ {
proxy_pass http://127.0.0.1:9001/;
proxy_set_header X-Proxy-Path /etherpad;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Here X-Proxy-Path is used, which works regardless of trustProxy. Use
X-Forwarded-Prefix instead if you prefer the standard header (and set
trustProxy: true).
Self-update, email, database, and metrics
These areas have their own pages:
- Self-update and outbound email (
adminEmail,mail.*SMTP) — see Updates. The corresponding Docker env vars (MAIL_HOST,MAIL_FROM, …) are listed on the Docker page. - Database — choose a backend with
dbType/dbSettings. The supported drivers and example settings are documented insettings.json.template, and the Docker equivalents (DB_TYPE,DB_HOST, …) are listed on the Docker page. The on-disk keyspace layout is described indoc/database.adoc. - Metrics — Etherpad exposes Prometheus-compatible metrics; see Stats.