Every request to your infrastructure probably passes through one process: an nginx, Traefik, Caddy, or HAProxy reverse proxy that terminates TLS, routes hostnames to services, and balances load. That makes the proxy the single most valuable thing you can monitor — and the easiest to monitor wrong. When it breaks, everything behind it goes down at once. When a backend breaks, the proxy is the thing that tells you, in the vocabulary of 502s, 503s, and 504s. And in its most treacherous failure mode, it serves the wrong content with a perfectly healthy 200.

This guide covers monitoring the proxy layer properly: reading gateway status codes, splitting proxy failures from upstream failures, the default-vhost trap, automatic TLS renewal, config reloads, and a concrete CronAlert setup. Everything here is agentless — external HTTP checks, content assertions, and heartbeats — and applies equally to a VPS running docker-compose and a beefy HAProxy pair in a datacenter.

The proxy speaks in gateway codes — learn its vocabulary

When your monitor gets an error through a reverse proxy, the status code tells you which layer to blame. The three that matter:

  • 502 Bad Gateway — the proxy reached for the upstream and got a refused or broken connection. Signature of a crashed backend, a restarting container, or a wrong upstream port after a config change. The proxy is fine; the process behind it is not.
  • 504 Gateway Timeout — the upstream accepted the connection but never answered within the proxy's timeout. Signature of a hung or overloaded backend: exhausted database pools, blocked event loops, runaway queries. The app is alive but too slow to count.
  • 503 Service Unavailable — usually the proxy itself declaring no healthy upstream: every backend failed its health checks, or maintenance mode is on. This one often means your load balancer's health-check config deserves a look, not just the app.

Configure your monitors to treat all of these as down (they are), but read the code when the alert fires — it is the difference between restarting a process, profiling a slow one, and fixing a health-check threshold. Our HTTP status codes guide covers the full vocabulary, and timeout thresholds explains catching the slow-backend case before it becomes 504s.

Split the failure domain: through the proxy and around it

One monitor per service through the public URL is non-negotiable — it measures the exact path users take, through DNS, TLS, proxy, and upstream. But when that check fails, it cannot tell you which of those four layers broke. The upgrade is a second signal that bypasses the proxy.

If your backend is reachable on a separate port or internal hostname, point a second monitor at it directly. Now the matrix reads itself: public failing + direct passing means proxy, TLS, or DNS; both failing means the app. If the backend is not externally reachable — the common case for containerized stacks — invert the check with a heartbeat: a cron job on the host curls the backend locally and pings a CronAlert heartbeat URL only on success, giving you the "direct" signal without exposing a port. We cover that pattern in depth in uptime monitoring for Docker and self-hosted apps.

The same split-horizon idea scales up: in Kubernetes, the ingress controller is your reverse proxy, and a check through the ingress plus a liveness signal from inside the cluster splits ingress failures from pod failures — see Kubernetes uptime monitoring.

The default-vhost trap: wrong content with a 200

The nastiest proxy failure returns no error at all. Name-based virtual hosting means the proxy picks a backend by the Host header — and when your server block is deleted, shadowed, or broken by a typo, the request falls through to the default server. Depending on the box, that serves the "Welcome to nginx!" page, a different customer's site, or a stale copy of your app. Status code: 200. Every status-code monitor: green.

The defense is a keyword assertion on every site the proxy fronts: require a string unique to that site — a brand name in the footer, a product-specific element — so being served anything else fails the check regardless of the code. For high-stakes pages, a SHA-256 content-hash check goes further and catches any unexpected change. This single configuration line is what turns "the proxy answered" into "the proxy answered with my site."

TLS terminates at the proxy — so monitor it there

Your proxy is almost certainly where TLS terminates, which makes it the place certificate problems live. Caddy and Traefik renew Let's Encrypt certificates automatically, and nginx setups usually rely on a certbot cron job. All of these fail more often than their reputation suggests: ACME HTTP challenges blocked by a new firewall rule or CDN, DNS moved without updating the challenge config, rate limits after a redeploy loop, or a full disk where certs are stored.

Two layers of defense. First, external SSL certificate monitoring on every domain the proxy terminates — this warns you days before expiry no matter why renewal broke. Second, if renewal runs from cron (certbot), a heartbeat on the renewal job itself, so a failing renewal alerts you weeks before the certificate runs out rather than days.

Config reloads: where good proxies go bad

Almost every proxy outage traces back to a config change. The failure modes are sneakier than a simple crash:

  • The reload fails and nothing changes. nginx keeps serving the old config when a reload fails validation — good for uptime, bad when you assume your change is live. Your new site 404s or routes wrong while everything else hums along.
  • The config is valid but wrong. nginx -t passes; the regex or the proxy_pass port is still wrong. Traffic routes, just not where you meant.
  • The container crash-loops. In docker-compose and Kubernetes setups, a bad config can put the proxy container in a restart loop — taking every site down at once, intermittently, which is the most confusing possible symptom.

External monitors are the safety net for all three: per-site keyword checks catch wrong routing and stale configs, and everything failing at once with connection errors is the crash-loop signature. If you deploy proxy config from CI, watch your monitors for the five minutes after each deploy — that window is when the most common causes of downtime concentrate.

Don't forget the proxy's own health endpoints

Each proxy ships an internal status surface: nginx has stub_status, Traefik has its /ping endpoint and dashboard, HAProxy has its stats page, and Caddy exposes an admin API. These are useful — but they are internal, and exposing them publicly is a bad idea. Two clean patterns: route a minimal path (like Traefik's /ping) through a dedicated internal-only hostname and monitor it with a token, or sweep them from a local cron job that pings a heartbeat when all internal checks pass. Either way you get proxy-process health without widening your attack surface — the same approach we recommend for internal tools and admin panels.

A concrete CronAlert setup

For a proxy fronting a handful of services — nginx, Traefik, Caddy, or HAProxy — after you create a free CronAlert account:

  • One monitor per site, through the proxy. Public URL, expect 200, with a keyword unique to each site so the default-vhost trap fails the check.
  • SSL monitoring on every terminated domain. Warns on failed auto-renewal regardless of cause.
  • A direct or heartbeat signal per critical backend. Direct monitor if reachable; otherwise a local curl + heartbeat from the host to split proxy failures from app failures.
  • A heartbeat on certbot renewal if you run it from cron.
  • Response-time thresholds on the busiest routes. Rising latency through the proxy is your earliest 504 warning.
  • Alert channels wired to the right people. Email, Slack, Discord, Teams, Telegram, PagerDuty, Opsgenie, Splunk On-Call, webhooks, or PWA push.

Every plan includes the full REST API and MCP server, so you can script one monitor per vhost straight from your proxy config. Teams fronting global traffic can use the Team plan's multi-region checks — five edge regions with quorum — so one flaky route to your proxy never pages you at 3 a.m. over nothing (see how quorum kills false positives).

Frequently asked questions

What does a 502 vs 503 vs 504 from my reverse proxy mean?

All three mean the proxy is up but couldn't serve from an upstream. 502: the upstream connection was refused or broken — a crashed backend. 504: the upstream accepted but never answered in time — a hung or overloaded backend. 503: no healthy upstream at all, or maintenance mode — often a load-balancer health-check story. The code tells you whether to restart, profile, or fix your health checks.

Should I monitor through the proxy or the backend directly?

Both. The public-URL check measures what users experience and is the one that pages you. A direct check (separate port, internal hostname, or a local-curl heartbeat) splits the failure domain: public failing + direct passing points at the proxy/TLS/DNS; both failing points at the app.

Why is my site serving the wrong content or a default nginx page with a 200?

The default-vhost trap: a removed or shadowed server block sends requests to the default server, which serves the wrong content with a healthy 200. Add a keyword assertion requiring a string unique to each site so wrong content fails the check even when the status code is fine.

How do I monitor Traefik's or Caddy's automatic TLS certificate renewal?

Externally: SSL certificate monitoring on every domain the proxy terminates warns you days before expiry no matter why renewal broke — blocked ACME challenges, moved DNS, rate limits, or a full disk. Add a heartbeat on the renewal cron if you use certbot.

What should I monitor after a proxy config reload?

Your per-site monitors, closely, for the next few minutes. Failed reloads silently keep the old config; valid-but-wrong configs route traffic to the wrong place; and in containers a bad config crash-loops the proxy entirely. Keyword checks catch the first two; everything failing at once is the third.

Monitor the layer everything passes through

The reverse proxy is where your whole stack converges: one process terminating TLS, routing every hostname, and translating backend failures into gateway codes. Per-site checks with keyword assertions, SSL monitoring on terminated domains, a split-horizon signal for critical backends, and heartbeats on renewal jobs cover its failure modes end to end — and all of it fits in CronAlert's free plan. Create a free CronAlert account and put a keyword check on every vhost your proxy serves today.

Related reading: uptime monitoring for Docker and self-hosted apps, HTTP status codes explained, SSL certificate monitoring, Kubernetes uptime monitoring, and DNS monitoring.