Reverse proxy¶
MXroute Manager listens on port 5000 (HTTP inside the container). In production you should terminate TLS at a reverse proxy and forward traffic to the app.
Set FORCE_HTTPS=true and TRUSTED_PROXY_COUNT=1 (or the number of proxies in front of the app) when serving over HTTPS.
Supported reverse proxies¶
| Proxy | One-click portal deploy | Notes |
|---|---|---|
| Nginx Proxy Manager | Yes (default) | REVERSE_PROXY_BACKEND=npm |
| Cloudflare Tunnel | Yes | REVERSE_PROXY_BACKEND=cloudflare_tunnel - TLS at Cloudflare edge |
| Caddy | Yes | Admin API + DNS ACME via Cloudflare |
| Traefik | Yes (file provider) | Writes dynamic config fragments; you configure the cert resolver |
| Raw nginx, HAProxy, Apache | Manual | REVERSE_PROXY_BACKEND=manual - UI shows copy-paste snippets |
| Pangolin, SWAG, K8s Ingress | Manual | Same as any other proxy: forward to :5000 and configure portal vhosts by hand |
The main app works behind any reverse proxy. Deploy Portal (branded reset pages) uses the backend selected by REVERSE_PROXY_BACKEND.
Choose a backend¶
Set in .env:
REVERSE_PROXY_BACKEND=npm # default
| Value | Extra variables |
|---|---|
npm |
NPM_*, RESET_PORTAL_CNAME_TARGET |
cloudflare_tunnel |
CF_TUNNEL_ID, CF_TUNNEL_ORIGIN (no RESET_PORTAL_CNAME_TARGET) |
caddy |
CADDY_ADMIN_URL, CADDY_ORIGIN, RESET_PORTAL_CNAME_TARGET |
traefik |
TRAEFIK_DYNAMIC_DIR, TRAEFIK_ORIGIN_URL, RESET_PORTAL_CNAME_TARGET |
manual |
MANUAL_PROXY_ORIGIN (optional hint for UI snippets) |
Cloudflare (CF_API_TOKEN, CF_ACCOUNT_ID) is required for all automated portal deploy modes.
Main app behind a reverse proxy¶
Nginx Proxy Manager¶
- Create a proxy host, e.g.
mxtools.example.com. - Forward hostname: IP or hostname of the machine running MXroute Manager.
- Forward port:
5000 - Scheme: HTTP (TLS terminates at NPM).
- Enable SSL (Let's Encrypt or custom certificate).
Caddy¶
mxtools.example.com {
reverse_proxy 127.0.0.1:5000
}
Raw nginx¶
server {
listen 443 ssl http2;
server_name mxtools.example.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
HAProxy¶
frontend https_front
bind *:443 ssl crt /path/to/cert.pem
acl host_mxtools hdr(host) -i mxtools.example.com
use_backend mxroute_app if host_mxtools
backend mxroute_app
server app1 127.0.0.1:5000 check
Apache httpd¶
<VirtualHost *:443>
ServerName mxtools.example.com
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
</VirtualHost>
If OIDC is enabled, set OIDC_REDIRECT_URI to your public callback URL (e.g. https://mxtools.example.com/oidc/callback).
Branded reset portals¶
Configured in Domains → Password Reset Portal per domain.
One-click deploy workflow¶
- Choose a subdomain (e.g.
reset) and optional branding (title, logo, theme). - Click Deploy Portal - the app saves settings, creates a proxied Cloudflare CNAME, and configures your selected reverse-proxy backend.
- Share the portal URL with mailbox owners on that domain.
Disabling a portal removes the CNAME and proxy route when automation is configured.
Nginx Proxy Manager (default)¶
NPM_API_URL,NPM_IDENTITY,NPM_SECRET,NPM_FORWARD_HOST,NPM_FORWARD_PORTRESET_PORTAL_CNAME_TARGET- public hostname portal subdomains CNAME to (same as your main app host behind NPM)- Optional:
CF_ORIGIN_CA_KEYfor Origin CA certs instead of Let's Encrypt DNS-01
Cloudflare Tunnel¶
- Create a remotely managed tunnel (
config_src: cloudflare) and runcloudflaredwith its token. - Set
CF_TUNNEL_IDandCF_TUNNEL_ORIGIN(e.g.http://127.0.0.1:5000). - Deploy Portal adds an ingress rule for
reset.customer.comand a proxied CNAME to{tunnel-id}.cfargotunnel.com.
TLS terminates at Cloudflare; no local reverse proxy required for portal hostnames.
Caveats: cloudflared must stay running. Multi-level subdomains may need a Cloudflare Advanced Certificate on some plans.
Caddy¶
CADDY_ADMIN_URL(e.g.http://127.0.0.1:2019)CADDY_ORIGIN(e.g.127.0.0.1:5000)- Caddy build must include the Cloudflare DNS plugin for ACME.
RESET_PORTAL_CNAME_TARGETpoints portal CNAMEs at your public Caddy hostname.
Traefik (file provider)¶
- Enable the file provider watching
TRAEFIK_DYNAMIC_DIR. - Configure a
certResolverwith Cloudflare DNS challenge in your static Traefik config. - Set
TRAEFIK_ORIGIN_URL(e.g.http://127.0.0.1:5000) and optionalTRAEFIK_CERT_RESOLVER(defaultcloudflare).
Deploy Portal writes one YAML fragment per portal host; Traefik picks it up automatically.
Manual mode¶
Set REVERSE_PROXY_BACKEND=manual. Save portal branding in the UI; the Domains tab shows nginx, Caddy, HAProxy, and Apache snippets for each portal hostname. Publish the Cloudflare CNAME yourself (RESET_PORTAL_CNAME_TARGET).
Pangolin¶
Pangolin is a zero-trust access platform (Traefik-backed), not a dumb reverse proxy. Its public HTTP resources require authentication by default. Branded password-reset portals must be fully public, so Pangolin is not supported for one-click deploy. Use manual mode or a traditional proxy instead.
Related guides¶
| Guide | Topic |
|---|---|
| Getting started | Production deployment checklist |
| Configuration | REVERSE_PROXY_BACKEND and per-backend variables |
| Password reset | Branded portal workflow and security |
| Testing | Portal deploy and routing tests |