diff --git a/content/docs/configuration/environment-variables.mdx b/content/docs/configuration/environment-variables.mdx index 36b595d9..255f46d8 100644 --- a/content/docs/configuration/environment-variables.mdx +++ b/content/docs/configuration/environment-variables.mdx @@ -22,6 +22,8 @@ All flags and their corresponding environment variables: | `--webhook-private-network-allowlist` | `MEMOS_WEBHOOK_PRIVATE_NETWORK_ALLOWLIST` | empty | Allowed private destinations: exact hostnames, IP addresses, or CIDRs | | `--allow-private-webhooks` | `MEMOS_ALLOW_PRIVATE_WEBHOOKS` | `false` | Deprecated blanket exception for private webhook destinations | | `--log-level` | `MEMOS_LOG_LEVEL` | `info` | Log verbosity (`debug`, `info`, `warn`, `error`) | +| `--rate-limit` | `MEMOS_RATE_LIMIT` | `true` | Request rate limiting for sign-in, signup, link previews, uploads, and writes; `false` turns every limit off (0.31+) | +| `--trusted-proxies` | `MEMOS_TRUSTED_PROXIES` | `private` | Proxies whose forwarding headers identify the client address: IPs, CIDRs, `private` (loopback and private ranges), or `none`; comma-separated (0.31+). See [Reverse Proxy](/docs/deploy/reverse-proxy#client-addresses-and-rate-limiting) | | `--config-dir` | `MEMOS_CONFIG_DIR` | `/etc/memos/config` | Directory of [declarative configuration](/docs/configuration/declarative-configuration) files (26.10+) | | `--config-apply` | `MEMOS_CONFIG_APPLY` | `startup` | `startup` applies the configuration directory before serving; `off` leaves it to `memos config apply` (26.10+) | @@ -62,6 +64,7 @@ An explicit flag always wins over the environment variable for the same setting. - select private or public access explicitly in the `ACCESS` instance setting - set `MEMOS_INSTANCE_URL` to the canonical external URL when the app sits behind a proxy +- if the proxy is not on a loopback or private address, list it in `MEMOS_TRUSTED_PROXIES` so rate limits apply per client - prefer explicit `MEMOS_DRIVER` and `MEMOS_DSN` when you are not using SQLite defaults - allow only required private webhook destinations with `MEMOS_WEBHOOK_PRIVATE_NETWORK_ALLOWLIST`; leave the deprecated blanket option disabled - keep secrets such as database credentials outside committed compose files diff --git a/content/docs/deploy/reverse-proxy.mdx b/content/docs/deploy/reverse-proxy.mdx index ca780f51..0590f5b7 100644 --- a/content/docs/deploy/reverse-proxy.mdx +++ b/content/docs/deploy/reverse-proxy.mdx @@ -14,6 +14,16 @@ For production, run Memos behind a reverse proxy such as Nginx, Caddy, or Traefi In Memos 0.31, `MEMOS_INSTANCE_URL` and access policy are independent. A private instance can have a canonical URL behind a publicly reachable proxy. Select private or public access through the `ACCESS` instance setting; changing the URL does not change an existing access policy. The URL must be absolute HTTP(S), without credentials, a query, or a fragment. +## Client addresses and rate limiting + +Memos rate-limits sign-in, signup, link previews, uploads, and writes by default (`MEMOS_RATE_LIMIT`, 0.31+). Limits for anonymous requests are counted per client address. Memos only reads that address from `X-Forwarded-For` or `X-Real-Ip` when the request comes from a trusted proxy, set with `MEMOS_TRUSTED_PROXIES`: + +- the default, `private`, trusts loopback and private network ranges, which covers a proxy on the same host or Docker network +- list every proxy in the chain that is not on a private address, for example `MEMOS_TRUSTED_PROXIES=private,203.0.113.10` or a CIDR. This includes a CDN or load balancer in front of a local proxy: Memos walks `X-Forwarded-For` from the right and stops at the first untrusted address, so an unlisted public proxy becomes the "client" and every user behind it shares one limit +- if other hosts on your private network are untrusted, or Memos is reached through a NAT hop that shows a private address without rewriting headers, list the real proxy addresses instead of `private`, or set `none`. Otherwise a client can forge `X-Forwarded-For` to get a fresh limit +- configure the outermost proxy to replace any `X-Forwarded-For` and `X-Real-Ip` headers sent by clients instead of appending to them +- when the proxy connects through a Unix socket (`MEMOS_UNIX_SOCK`), there is no peer IP address, so Memos ignores the forwarding headers and all clients share one limit. Connect over TCP if you need per-client limits + ## Why this matters A reverse proxy gives you: