Uptime Kuma Keyword Monitors vs HTTP 200: Sites That Look Up While the App Is Dead
Ivo Markham
September 21, 2026
An HTTP 200 on the homepage is the most popular lie in a homelab. The reverse proxy answers. Nginx is fine. The container that serves your actual app is wedged, restarting, or serving a stale maintenance page that still returns success. Uptime Kuma’s keyword monitors exist because a green status code is not the same thing as a working product.
This piece is about when a plain HTTP monitor is enough, when a keyword (or JSON/body) check earns its keep, and how keyword monitors fail in their own creative ways. If you already run Kuma, you probably have at least one monitor that should be stricter than it is.
What “up” means in three layers
Layer one is reachability: TCP connect, TLS handshake, something on the port. Useful for catching a dead host or a firewall that ate your rule.
Layer two is HTTP success: status codes in the 2xx range on a path you chose. Useful for catching a stopped container behind a proxy that still answers elsewhere, or a DNS name that points at the wrong box.
Layer three is application truth: the response body contains a string only a healthy app would emit, or a JSON field says "status":"ok", or a login page is absent from an authenticated health endpoint. Keyword monitors live here.
Most people stop at layer two because it is easy. Most 3 a.m. surprises live between layer two and layer three.

When a plain HTTP 200 monitor is honest enough
Keep a simple status-code check when:
- The path you hit is a dedicated health endpoint that fails closed when dependencies die.
- The service is a static site or a single binary with no upstream database.
- You only care that the reverse proxy and certificate path still work.
- False positives from flaky body content would be worse than occasional silent app death.
A well-designed /health or /ready endpoint that returns non-200 when Postgres is unreachable makes keyword checks redundant. The status code already carries meaning. Keyword monitors are a retrofit for apps that never shipped that honesty.
Also keep HTTP-only checks for things you monitor from outside your network where body content changes with A/B tests, localization, or CDN edge pages. Keywords that matched last month can page you forever after a marketing rewrite.
When keyword monitors catch the real outage
Use a keyword (or inverted keyword) check when the happy path returns 200 while the product is unusable.
Classic cases in a solo stack:
- A reverse proxy serves a default welcome page because the upstream container is gone.
- An app returns 200 with an error banner in HTML: “database unavailable,” “redis timeout,” “read-only mode.”
- A status page generator or splash screen sits in front of a dead API.
- A CDN caches a soft 200 error page longer than you expect.
- You accidentally monitor the marketing site while the app lives on
app.subdomain.
In those situations, asserting that the body must contain "ready":true or must not contain Welcome to nginx is the difference between a useful page and a decorative one.
Keyword monitors also help when you cannot change the app. Vendored containers without a readiness probe are common in homelabs. You cannot add a proper health handler tonight. You can require a string that only appears when the UI finished booting.

How keyword monitors fail
They are not magic. They introduce new false greens and false reds.
Brittle strings. Matching on a product name in the footer breaks when you rebrand. Matching on a CSS class breaks when you restyle. Prefer machine-oriented tokens from a health payload over human copy.
Partial pages. Some apps return 200 with a shell HTML document while JavaScript loads the real state. Your keyword may appear in the shell even when the API behind it is dead. Prefer an API health URL over the SPA root.
Auth walls. If the monitor hits a login redirect that still returns 200 with “Sign in,” a keyword for the dashboard title will flap depending on cookie state. Use a dedicated unauthenticated health route, or accept that authenticated checks are a different problem.
Inverted keywords. “Must not contain error” is powerful and dangerous. Error strings show up in docs pages, demo tenants, and intentionally rendered examples. Scope the check to a narrow path.
Encoding and compression. Rare, but messy responses and unexpected content types can make substring checks behave oddly. Confirm the monitor sees the same body you see with curl from the Kuma host.
A practical upgrade path inside Uptime Kuma
Do not rewrite every monitor tonight. Sort by blast radius.
- List services that would wake you if they lied about being up.
- For each, curl the current HTTP monitor URL from the Kuma host and read the body.
- If the body can be 200 while the app is dead, add a keyword or switch the URL to a real health endpoint.
- Run both monitors in parallel for a week. Compare pages.
- Delete the weaker one only after you trust the stricter one under normal deploys.
When you add a keyword, write down why in the monitor name: immich /api/server/config must contain version beats immich web. Future you will not remember the incident that motivated the string.
HTTP 200 versus keyword versus push
Keyword monitors still assume the service can answer HTTP. Backup jobs, sync scripts, and cron imports often need a push/heartbeat model instead: the job reports in, and silence is the alert. Do not stretch keyword monitors into that job. A green HTTP check on a dashboard that lists “last backup” as a static string will not save you.
Similarly, TCP-only checks belong under keyword and HTTP, not beside them as equals for app health. Use TCP for the pipe; use HTTP or keywords for the product.
Concrete setups that stop lying
Here are patterns that hold up on a typical Compose stack without turning Kuma into a second product.
Proxy in front of a dead upstream. Point the monitor at the app hostname users actually type. Require that the body does not contain the default reverse-proxy greeting, and that it does contain a short token from your app’s footer or health JSON. If you only check status codes, the proxy’s polite 200 will keep you asleep.
API that returns 200 with an error object. Many JSON APIs never learned HTTP semantics. They answer 200 with {"ok":false,"error":"..."}. A status monitor is useless. A keyword or JSON condition on "ok":true is the whole job.
Login portals and SSO wrappers. Checking the bare domain often lands on a 200 login page while the app behind SSO is offline. Prefer an unauthenticated /healthz on the app itself, exposed only on the Tailscale interface if you must. Keyword-checking “Dashboard” on a page that always says “Sign in to continue” teaches you nothing.
Certificate and content together. Certificate expiry monitors and keyword monitors answer different questions. Keep both when the site can be “content healthy” on a cert that expires next Tuesday. Do not merge them mentally into one green tile.
External versus internal vantage points. A keyword that works from inside the LAN can fail from a VPS checker if the body differs by geo or auth. Run the curl from the same network namespace as Kuma before you trust the string. Homelab monitors that only ever see hairpinned traffic miss CDN weirdness that real users hit.
Naming, intervals, and alert hygiene
Strict checks need calmer alerting. A keyword that flaps during rolling deploys will burn trust faster than a loose HTTP monitor ever did. Use retries and a slightly longer interval on content checks. If your deploy always rewrites the homepage for thirty seconds, either exclude that window with maintenance, or monitor a path that stays stable during rollout.
Group notifications by severity. “NAS web UI missing expected string” can wait for morning. “Password manager health token missing” should not share the same quiet Discord channel as a hobby RSS reader. Kuma lets you attach different notification channels per monitor. Use that instead of training yourself to ignore the channel.
Document the expected string in the monitor description field if you use it. The string itself is not self-explanatory six months later. Note whether the check is temporary until a real readiness probe lands. Temporary checks have a way of becoming permanent folklore.
When to stop and fix the app instead
Keyword monitors are a tax. Every deploy that changes copy, every theme update, every i18n experiment can bill you. If you control the service, spend the hour to add a readiness endpoint that:
- returns non-200 when critical dependencies are down,
- avoids heavy work on every probe,
- stays free of auth,
- and documents what “ready” includes and excludes.
Then your Kuma monitor can go back to being a boring status-code check. That is the end state worth aiming for. Keywords are how you survive until then, and how you watch third-party containers you will never patch.
If you do not control the service, keywords (or an external synthetic transaction) may be the permanent answer. Be intentional about which camp each monitor lives in.
Decision rules you can keep
Use HTTP status when the endpoint fails closed and the code is the contract.
Use a keyword monitor when the endpoint returns success while embedding failure in the body, or when a proxy can answer without the app.
Prefer fixing the app’s health endpoint over eternally clever keywords. Keywords are a bridge. Bridges are fine. Living on a bridge is not a strategy.
If your Kuma board is all green and users still hit a dead app, you do not have a notification problem. You have a definition-of-up problem. Keyword monitors are how many of us admit that without rewriting the service first.