Self-hosting behind your own reverse proxy or CDN
Self-hosting behind your own reverse proxy or CDN
Chomper's containers (chomper-ui, chomper-ingestion, and any paid
module such as Network Probes) are commonly placed behind a reverse proxy you
already run (nginx, Apache) and/or a CDN/WAF in front of that, such as Cloudflare. This page
covers the two configuration points customers hit most often, plus two smaller gotchas worth
knowing up front.
1. Keep a persistent, keepalive connection to chomper-ui
A bare proxy_pass with no upstream keepalive and no explicit timeouts works most
of the time, but a single transient connection hiccup between your reverse proxy and
chomper-ui can surface as a one-shot 502 — which your CDN then renders
as its own branded error page, making it look worse than it is. The fix is a persistent
upstream block with keepalive and explicit timeouts.
nginx
upstream chomper_ui { server 10.0.0.5:3001; keepalive 32;} server { listen 443 ssl; server_name dashboard.example.com; location / { proxy_pass http://chomper_ui; proxy_http_version 1.1; proxy_set_header Connection ""; 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; proxy_connect_timeout 5s; proxy_send_timeout 30s; proxy_read_timeout 30s; }}
The Connection "" header is what actually enables keepalive reuse on the upstream
hop — without it, nginx forwards the client's own Connection: close, and every
request pays a fresh TCP+TLS handshake to chomper_ui regardless of the
keepalive directive above it.
Apache (mod_proxy)
Requires proxy and proxy_http enabled
(a2enmod proxy proxy_http):
<VirtualHost *:443> ServerName dashboard.example.com ProxyPreserveHost On ProxyTimeout 30 <Location "/"> ProxyPass "http://10.0.0.5:3001/" keepalive=On connectiontimeout=5 timeout=30 ProxyPassReverse "http://10.0.0.5:3001/" </Location></VirtualHost>
2. Modules with an external agent need their own carve-out
The Network Probes module's agents (the small process you install on remote hosts to run
ping/DNS/curl/traceroute checks) reach chomper-ui through a dedicated public route,
/agent-proxy/network-probes/agent/*. It's intentionally public at the
chomper-ui level — the module's own issued key authenticates each request, not your
reverse proxy or your IP allowlist.
If your dashboard vhost is IP-restricted — a common pattern when only office/VPN IPs should reach the UI — that restriction applies to every path on the vhost unless you explicitly carve the agent path out. An agent host is essentially never one of your allowlisted IPs, since it runs wherever you're actually monitoring from.
nginx
server { listen 443 ssl; server_name dashboard.example.com; # Public — no allow/deny here. Auth is the module's own key, not the caller IP. location /agent-proxy/network-probes/agent/ { proxy_pass http://chomper_ui; proxy_http_version 1.1; proxy_set_header Connection ""; 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; } # Dashboard itself — IP-restricted. location / { allow 203.0.113.10; deny all; proxy_pass http://chomper_ui; proxy_http_version 1.1; proxy_set_header Connection ""; 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; }}
The order matters: nginx matches the most specific location prefix, so the agent
carve-out above is reached before the restricted / block regardless of the order
they're written in the file — but keep the more specific block first for readability anyway.
Apache
<VirtualHost *:443> ServerName dashboard.example.com ProxyPreserveHost On ProxyTimeout 30 <Location "/agent-proxy/network-probes/agent/"> # Public — auth is the module's own key, not the caller IP. ProxyPass "http://10.0.0.5:3001/agent-proxy/network-probes/agent/" keepalive=On ProxyPassReverse "http://10.0.0.5:3001/agent-proxy/network-probes/agent/" </Location> <Location "/"> Require ip 203.0.113.10 ProxyPass "http://10.0.0.5:3001/" keepalive=On ProxyPassReverse "http://10.0.0.5:3001/" </Location></VirtualHost>
Apache evaluates the most specific <Location> match, same principle as
nginx above — the agent carve-out wins over the restricted / block for anything
under that path.
3. Point agent/ingestion URLs at a port your CDN actually forwards
Free and Pro Cloudflare plans (and similar CDN/WAF products) only proxy a fixed list of ports
— 80/443 plus a handful of others. If chomper-ingestion listens on a non-standard
port such as :3000, requests routed through the CDN can silently time out even
though the port is open and reachable at the host/firewall level.
If your reverse proxy already forwards /api/v1/events on 443 (worth doing anyway,
for a single TLS cert to manage), point the module's/agent's ingestion URL at your bare proxied
hostname with no port, instead of the container's own :3000:
location /api/v1/events { proxy_pass http://10.0.0.5:3000/api/v1/events; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
4. Custom scripts behind a WAF/bot-fight product
If you write your own script or integration that posts events straight to
/api/v1/events from behind Cloudflare or a similar product, send an explicit
User-Agent header. Some WAFs silently block a generic HTTP client's default identity
(for example Python's stock urllib/requests string) even though the
request is otherwise correctly authenticated with your X-API-Key — an identical
request from curl or a browser passes untouched. Chomper's own probe agent already
does this (chomper-probe-agent/1.0); apply the same pattern to anything you build
yourself.
Quick reference
| Symptom | Likely cause | Fix |
|---|---|---|
| Occasional one-shot 502, otherwise everything works | No upstream keepalive / no timeouts on the proxy→chomper-ui hop | §1 — upstream + keepalive + explicit timeouts |
| Probe agents can't reach their assignment endpoint, dashboard itself loads fine | Dashboard vhost's IP allowlist also covers /agent-proxy/* | §2 — explicit carve-out location/block for /agent-proxy/<slug>/agent/ |
Network is unreachable against a non-standard ingestion port through a CDN | Free/Pro Cloudflare only proxies a fixed port list | §3 — proxy ingestion on 443, point the agent/module at the bare host |
| Custom integration gets blocked before it ever reaches Chomper's own auth check | WAF flags a generic HTTP client's default User-Agent | §4 — send an explicit, identifiable User-Agent |
Last updated 11 August 2026