etherpad-lite/doc/configuration.md
John McLear 2d1a09dbe0
docs: refresh docs for 3.2.0 — correct stale content, document recent features (#7888)
* 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>
2026-06-05 13:44:25 +01:00

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 / --settings flag to the launcher, e.g. bin/run.sh -s /etc/etherpad/settings.json. This lets you run multiple instances from one installation. (bin/run.sh forwards the flag to pnpm run prod, which is the supported entrypoint — there is no server.js; the runtime is src/node/server.ts, loaded via tsx.)
  • 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 of PORT, or 9001 if unset.
    • "${MINIFY:true}" → the boolean true/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.json on 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 secure flag is set on cookies when the proxy terminates TLS (required for SameSite=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 explicit publicURL is 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 in settings.json.template, and the Docker equivalents (DB_TYPE, DB_HOST, …) are listed on the Docker page. The on-disk keyspace layout is described in doc/database.adoc.
  • Metrics — Etherpad exposes Prometheus-compatible metrics; see Stats.