# Uptime check pass conditions

> Status code, body text, JSON values, headers, response time and TLS certificate: how to write them, evaluation order and failure reasons.

An HTTP uptime check passes only when **every** pass condition holds. Add them under **Pass conditions** when you create or edit a check — up to 10 per check. There are no regular expressions and no scripts: each condition is a plain comparison, so a check means the same thing in every region.

TCP checks have no conditions: they pass when the connection opens within the timeout.

## Defaults

- **Status code** — without a status condition, `2xx` passes. When **Follow redirects** is off, `3xx` passes too, because the redirect itself is the answer.
- **Valid TLS certificate** — added to every new check. For an `https://` URL the certificate must be trusted and match the host name; an expired, self-signed or wrong-host certificate fails the round. Remove the condition only if you really serve such a certificate. It has no effect on `http://` URLs.

## Conditions

| Condition | Passes when | Example |
|---|---|---|
| **Status code** | The final status is in one of the listed codes or ranges (inclusive) | `200-299, 304` |
| **Body contains** | The body contains the text | `"status":"ok"` |
| **Body excludes** | The body does not contain the text | `Maintenance` |
| **JSON value** | The value at a JSON pointer exists, equals a value, or has a type | `/db/healthy` equals `true` |
| **Header** | A response header exists, equals a value, or contains a text | `content-type` contains `json` |
| **Response time** | The total time is under the limit, in milliseconds | under `2000` |
| **Valid TLS certificate** | The certificate is trusted and matches the host name | — |

Details:

- **Body** conditions read the first 64 KB of the body after decompression (`gzip`, `deflate`, `br`; at most 1 MB unpacked). Text past that point is not seen. Matching is case-sensitive unless you tick **ignore case**. A `HEAD` request has no body, so body and JSON conditions need `GET`.
- **JSON value** uses a JSON pointer ([RFC 6901](https://www.rfc-editor.org/rfc/rfc6901)): `/data/items/0/id` is the `id` of the first item; write `~1` for a `/` and `~0` for a `~` inside a key. **Equals** compares strings as written and every other value as its JSON text — `true`, `3`, `null`. **Type** is one of `string`, `number`, `boolean`, `object`, `array`, `null`. A body that is not valid JSON fails every JSON condition.
- **Header** names are case-insensitive; values compare exactly as sent.
- **Response time** is the total of DNS, connect, TLS, first byte and download, measured on the probe. After a redirect it is the last hop's time.
- Texts and values are up to 256 characters.

## Order and failure reasons

Conditions are checked in a fixed order and the first one that fails names the reason shown on the check page and in alerts. A round that cannot even get an answer fails earlier, with a network reason.

| Order | What failed | Reason shown |
|---|---|---|
| — | No answer: DNS, connection, TLS handshake or timeout | **DNS lookup failed**, **connection failed**, **TLS error**, **timed out** |
| 1 | Status code | **unexpected status code** |
| 2 | Valid TLS certificate | **TLS error** |
| 3 | Header | **header check failed** |
| 4 | Body contains / excludes | **body check failed** |
| 5 | JSON value | **JSON check failed** |
| 6 | Response time | **too slow** |

A few results are **unknown**, never a failure, because they say nothing about your service: **not run — too many checks to the same host at once**, **address blocked** (the name now resolves to a refused address — fix the target) and **probe error**. See [how Belvador decides](https://belvador.dev/en/docs/uptime/quickstart#2-how-belvador-decides).

## Request headers

You may send up to 10 headers, for example an `x-health-token` your endpoint expects. `authorization`, `cookie`, `host`, `content-length`, `transfer-encoding`, `connection`, `te`, `upgrade` and any `proxy-*` or `sec-*` header cannot be set. Header values are part of the check settings, which every workspace member can read — use a token made only for health checks, never a user's credentials. When a redirect goes to another origin, your headers are not sent there.

Every request carries `User-Agent: BelvadorUptime/<version> (+https://belvador.dev/docs/uptime/probes)`; see [Probes and IPs](https://belvador.dev/en/docs/uptime/probes).

## Recipes

| Goal | Conditions |
|---|---|
| A health endpoint that reports its own state | Status `200`; JSON value `/status` equals `ok` |
| A page that must not show an error template | Status `200-299`; body excludes `Something went wrong` |
| An API that must stay fast | Status `200`; response time under `800` |
| A redirect that must stay a redirect | Follow redirects off; status `301`; header `location` contains `https://www.example.com/` |
