Reverse proxy and TLS
meilisearch-cdc serves plain HTTP. Put a TLS-terminating reverse proxy in front of it, keep Server-Sent Events unbuffered, and tell the service it sits behind a proxy.
Service settings behind a proxy
| Variable | Why |
|---|---|
CDC_LISTEN=127.0.0.1:8080 | Only the proxy reaches the service. |
CDC_TRUST_PROXY=true | Uses X-Forwarded-For (first address) as the client IP for login rate limiting and the audit log, and X-Forwarded-Proto: https to mark the session cookie Secure. Enable it only when every request comes through a proxy that sets these headers, otherwise clients can spoof their IP. |
CDC_COOKIE_SECURE=true | Always sets the Secure flag on the cdc_session cookie, whatever the headers say. |
CDC_PUBLIC_URL=https://cdc.example.com | The base URL in emailed links (invitations, password resets, alert emails). It can also be set in Settings → Email; without either, the request's origin is used. |
Keep the Host header
Cookie-authenticated writes are protected against CSRF by comparing the browser's Origin (or Referer) host with the request's Host. Forward the original Host header, or every save in the dashboard fails with 403 cross-origin request refused.
What needs special care
/api/v1/eventsis a Server-Sent Events stream that stays open. The service already sendsX-Accel-Buffering: noandCache-Control: no-cache; the proxy must not buffer it and must allow long reads./api/v1/ingest/{task}(the webhook source) answers only after Meilisearch confirmed the documents, up to 60 seconds, and accepts bodies up to 64 MiB./mcpis the MCP endpoint (Streamable HTTP), which may also stream responses.
nginx
server {
listen 443 ssl;
http2 on;
server_name cdc.example.com;
ssl_certificate /etc/letsencrypt/live/cdc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cdc.example.com/privkey.pem;
client_max_body_size 64m; # webhook ingest bodies
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
# Live dashboard updates (Server-Sent Events) and MCP streams.
location ~ ^/(api/v1/events|mcp)$ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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 Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 1h;
}
}
server {
listen 80;
server_name cdc.example.com;
return 301 https://$host$request_uri;
}
With proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, a client-supplied header is kept in front of the real address. If nginx is the only entry point, prefer proxy_set_header X-Forwarded-For $remote_addr; so the first address is always the one nginx saw.
Caddy
Caddy obtains certificates automatically, sets X-Forwarded-For and X-Forwarded-Proto, forwards the Host header and flushes Server-Sent Events immediately:
cdc.example.com {
request_body {
max_size 64MB
}
reverse_proxy 127.0.0.1:8080 {
flush_interval -1
}
}
Serving under a sub-path
The dashboard calls the API at /api/v1 and the service worker registers at the root, so serve meilisearch-cdc on its own host name (cdc.example.com) rather than under a path such as example.com/cdc/.
Checklist
- The dashboard shows Live in the top bar (SSE works through the proxy).
- The session cookie is
Secure(browser developer tools → Application → Cookies). - Invitation and reset emails link to
https://cdc.example.com. - The audit log shows real client IPs, not the proxy's.
- The browser offers to install the app (installing needs HTTPS; see Mobile and app install).