# Working with checks

Every Uptime check runs on a schedule and confirms a failure before it alerts, so a single slow response doesn't page anyone.

## How a check goes down

When a check fails, it waits for its **confirmation period** and checks once more. A failure during the confirmation period opens no incident and sends no alert. If the re-check fails too, the check goes **Down**, opens one [incident](/docs/concepts/incidents/), and alerts once. When it recovers, it sends a recovery alert, but only after it was Down.

[Heartbeats](/docs/uptime/heartbeat/) work differently: their grace period is the confirmation.

## Settings

These are under **Advanced settings** in the **Add check** dialog and in each check's settings. When you save a check's settings, only the fields you changed are saved.

| Setting | Default | Options |
| --- | --- | --- |
| Check frequency | 3 minutes | 20 s, 30 s, 1, 2, 3, 5, 10, 15, or 30 minutes. |
| Confirmation period | 2 minutes | Immediately, 30 s, 1, 2, 3, or 5 minutes. |
| Request timeout | 30 seconds | 5, 10, 15, 30, 45, or 60 seconds. For ping checks the field is **Ping timeout**: 5 or 10 seconds, default 10. DNS checks also allow 5 or 10 seconds. |
| Follow redirects | On | Follows up to 10 redirects. Off: a redirect response counts as the response. |
| Internet Protocol (IP) version | Both IPv4 and IPv6 | **IPv4 only** or **IPv6 only**. Website, keyword, ping, and TCP port checks. See [IP version](#ip-version). |
| SSL/TLS verification | On | Treats an invalid, expired, or self-signed certificate as down. |
| SSL expiration | Alert 14 days before (`https` URLs) | **Don't check for SSL expiration**, or alert 1, 2, 3, 7, or 14 days, or 1 or 2 months before. See below. |
| Domain expiration | Don't check for domain expiration | Alert 1, 2, 3, 7, or 14 days, or 1 or 2 months before. See below. |

![Advanced settings of a website check, with the IP version set to Both IPv4 and IPv6 and SSL expiration to Alert 14 days before.](../../../assets/screenshots/uptime-ip-version-expiration.webp)

Website and keyword checks also have [request settings](/docs/uptime/website/#request-settings) and [SSL and domain expiration](#ssl-and-domain-expiration), and TCP port checks have a [TLS option](/docs/uptime/port/#watching-a-tls-certificate).

### IP version

**Internet Protocol (IP) version** sets how website, keyword, ping, and TCP port checks connect. It's **Both IPv4 and IPv6** by default. With **IPv4 only** or **IPv6 only**, the check connects only over that family, from every location it runs from.

If the host has no address of the chosen family, or you entered an IP address of the other kind, the check is down with, for example, "ipv4.google.com has no IPv6 address". DNS checks don't have this setting: the DNS server's address decides.

### SSL and domain expiration

Website and keyword checks can watch their site's SSL certificate and its domain registration. Choose how early to be told with **SSL expiration** and **Domain expiration**. Both lists have the same choices.

![The SSL expiration list open: Don't check for SSL expiration, then Alert 1 day before through Alert 2 months before, with Alert 14 days before selected.](../../../assets/screenshots/uptime-ssl-expiration-options.webp)

- Once the certificate or the domain is within that many days of expiring, the check opens an [expiry incident](#expiry-incidents) and alerts its alert channels.
- The incident closes by itself once the certificate or domain is renewed, with an alert saying "…was renewed: it now expires on" and the new date. It can't be resolved by hand.
- Expiry dates are checked every hour, and a few seconds after you save a check. A domain's date comes from its registry (RDAP), which TunnelHQ asks at most once a day. When the registry gave no date, because of an error or because it doesn't publish one, TunnelHQ asks again after 6 hours.
- **SSL expiration** needs an `https://` address, and is greyed out for an `http://` one. If a check's address changes to `http://`, it stops watching the certificate, and any open certificate incident closes. It watches the site's own certificate, the one your visitors' browsers are shown, and doesn't depend on **SSL/TLS verification**.
- New checks start with SSL expiration at 14 days and domain expiration off.
- The choice is shared: changing either one in a check's settings applies it to the project's other website and keyword checks on the same host. Adding a check doesn't change the others.

A few seconds after you save a check, its card and its settings under **Domain expiration** show the date when it's known, such as "example.com expires on 13 August 2027, in 321 days". Some registries don't publish expiry dates, for example for `.de`, `.io`, and `.pk` domains. The settings then say so, such as "The registry for .de doesn't publish expiry dates", and domain expiration never alerts for that domain.

![SSL expiration set to Alert 14 days before and Domain expiration to Alert 2 months before, with the line example.com expires on 13 August 2027, in 321 days.](../../../assets/screenshots/uptime-domain-date.webp)

[TCP port checks with TLS](/docs/uptime/port/#watching-a-tls-certificate) keep their own reminders, at 21, 14, and 7 days, without an incident.

Check cards show the days left, such as **Cert 60d** and **Domain 45d**. Each turns amber within the check's chosen window, or within 14 days if that's longer: with **Alert 7 days before**, from 14 days; with **Alert 2 months before**, from 60 days. An expired certificate reads **Cert expired**, and one that isn't trusted for another reason, such as a self-signed certificate, reads **Cert invalid**.

![A website check's card with the chips Cert 32d and Domain 321d.](../../../assets/screenshots/uptime-expiry-chips.webp)

With **SSL/TLS verification** off, a check stays up while its certificate is expired, and its card still says **Cert expired**:

![A website check on expired.badssl.com that reads Healthy, with the chip Cert expired.](../../../assets/screenshots/uptime-cert-expired-chip.webp)

The alerts say what expires, when, and what happens if it lapses, for example:

- "The domain example.com expires in 13 days, on 9 October 2026. Renew it with its registrar before then: Website stops working if it lapses."
- "The SSL certificate of www.example.com expires in 5 days, on 1 October 2026. Renew it before then: once it expires, browsers and apps refuse to connect to Website."

### Expiry incidents

An expiry incident shows as **Expiring**, in amber, never as **Down**. It reads, for example, "Domain example.com expires on 9 October 2026" or "SSL certificate of www.example.com expires on 1 October 2026", and its detail panel says "Closes by itself once the certificate or the domain is renewed".

![An Expiring incident on the Incidents page: Expired certificate, SSL certificate of expired.badssl.com expired on 12 April 2015, and the check's address.](../../../assets/screenshots/uptime-incident-expiring-row.webp)

<Image src={expiringPanel} width={384} densities={[1, 2]} alt="The incident's detail panel: one Expiring badge, the message and address, Acknowledge Incident, the activity log, and Closes by itself once the certificate or the domain is renewed. There's no Resolve button." />

Expiry incidents sort after outages, and don't count in outage figures such as active outages and response times.

## Who gets alerted

- In **Add check**, every alert channel in the project is selected to start with.
- At least one must stay selected when you add a check. Otherwise you'll see "Pick at least one. To alert nobody, change it on the check once it is added."
- On a check's **Settings** tab, you can clear every channel. The check then alerts nobody.

## Public addresses only

Checks connect from TunnelHQ's servers, so private and reserved addresses, such as `10.x.x.x`, `192.168.x.x`, `127.0.0.1`, and `169.254.169.254`, are refused when you save:

- "URL is not allowed (must be a public hostname)"
- "Host is not allowed (it must be a public address)"
- `Host "x" could not be found. Check the address.` for a host that doesn't resolve

## Checking from several locations

When there's more than one place to check from, **Check from** is the last field in **Advanced settings**. The main location is always included.

![Advanced settings of a DNS check, with Check from showing Germany selected and Canada and UAE available.](../../../assets/screenshots/uptime-check-from.webp)

- With two or more locations, a check is **Down** only when most of the locations still checking see it down: both of 2, 2 of 3, or 3 of 4. Fewer is **Partial**. This differs from [tunnels](/docs/concepts/regions/#aggregate-status), where a monitor is Down only when no location is up.
- A location with no working checker is left out.
- **Alert on a partial outage** also alerts when only some locations see the check down. It's off by default, and can only be turned on with two or more locations.

## On a check's page

![A website check's page: its status, how long it has been up, the last check, incidents, the health timeline and the response-time chart.](../../../assets/screenshots/uptime-check-detail.webp)

- **Run Now**, at the top right, runs the check immediately (so does the ▷ button on its card in the Uptime list). It doesn't use the on-demand test quota that tunnel tests use. Heartbeats have no run button: they're checked when your job calls their address.
- **Send test alert** sends a test through every channel the check alerts. It needs a role that can edit monitors.
- A response-time chart, and an availability table for the last 24 hours, 7 days, 30 days, and 90 days: availability, downtime, number of incidents, and the longest and average incident. See [Availability](#availability).
- **Currently up for** and **Last checked**.
- For checks that use TLS, the number of days until the certificate expires is also shown on the check's card.

## Availability

The availability table on a check's page counts outages. A period's availability is the share of it with no incident, counted from when the check was created if that's later, so it always agrees with the downtime beside it. For example, 1 hour down in the last 24 hours reads 95.83%.

- A failure that ends within the [confirmation period](#settings) never becomes an incident, so it isn't downtime.
- Until a check has its first result, the table shows no figure.

The bars in the health timeline and the uptime figure on a check's card are worked out differently: checks count their results, and [heartbeats](/docs/uptime/heartbeat/#availability-and-the-bars) measure time. For a check that was down a lot, the card's figure and the table can differ slightly, because they cover slightly different windows.

## Uptime checks in the API

In the [REST API](/docs/api/), `GET /api/v1/servers?family=uptime` lists Uptime checks (`family=all` lists tunnels and checks together; the default is tunnels only), and `POST /api/v1/servers` creates website, keyword, ping, TCP port, and DNS checks, and [heartbeats](/docs/uptime/heartbeat/#heartbeats-in-the-api). Groups are managed in the dashboard only: `"type": "group"` is refused with `400` "Groups are created in the dashboard for now."

When you create a check, you can also send:

| Field | Values | Checks |
| --- | --- | --- |
| `ssl_expiration` | Days before the certificate expires to alert: `1`, `2`, `3`, `7`, `14`, `30`, or `60`. `0` or `null` turns it off. Needs an `https://` URL. | Website, keyword |
| `domain_expiration` | Days before the domain expires to alert, with the same values. `0` or `null` turns it off. | Website, keyword |
| `ip_version` | `"ipv4"`, `"ipv6"`, or `null` for both. | Website, keyword, ping, TCP port |

`30` and `60` match **Alert 1 month before** and **Alert 2 months before**. A field you leave out takes the value of the project's other checks on the same host, or, for the host's first check, the default: 14 days for an `https://` URL, and domain expiration off. Check rows return the same three fields, where `null` means off, or both IP versions.

A DNS check is created with `"type": "dns"` and these fields:

| Field | Details |
| --- | --- |
| `host` | The DNS server to monitor: a public IP address, such as `1.1.1.1`, or an IPv6 address with or without brackets. A private or reserved address, or a name, is refused with `400` "host must be the DNS server's public IP address, like 1.1.1.1." |
| `domain` | The domain to ask it about, such as `example.com`. Labels may start with `_`, as in `_dmarc.example.com`. Anything else is refused with `400` "domain must be a domain name to query, like example.com." |
| `keyword` | Optional. Text the answer must contain, usually the address the domain should point to. Up to 255 characters. |

DNS checks refuse `ip_version`, because the DNS server's address decides it, and `ssl_expiration` and `domain_expiration`. Queries always go to port 53. Every check row has a `domain` field: the domain a DNS check asks about, or `null` for other checks. A DNS check's `host` is its DNS server.

`PUT /api/v1/servers/:id` updates a website, keyword, ping, TCP port, or DNS check ([heartbeats](/docs/uptime/heartbeat/#heartbeats-in-the-api) have their own fields). Send only the fields you want to change; the rest stay as they are.

| Fields | Checks |
| --- | --- |
| `name`, `interval`, `timeout`, `maxretries`, `tags` | All |
| `ip_version` | Website, keyword, ping, TCP port |
| `url`, `accepted_statuscodes`, `ssl_expiration`, `domain_expiration` | Website, keyword |
| `keyword` | Keyword, DNS |
| `invert_keyword` | Keyword |
| `host` | Ping, TCP port, DNS |
| `port` | TCP port |
| `domain` | DNS |

- `interval` has the same minimum as when you create the check, and the expiration and IP version fields take the same values.
- Changing either expiration setting applies it to the project's other website and keyword checks on the same host, as in the dashboard.
- When you create or change a check, a field its type doesn't have is refused with `400` and a message naming it, such as "port is for port checks." or "keyword is for keyword and DNS checks."
- For a DNS check, `keyword` set to `""` or `null` clears it.
- A check's type can't be changed: the API answers "Create a new check instead".
- `active` pauses (`false`) or resumes (`true`) the check, as the dashboard's **Pause** and **Resume** buttons do: its history shows "Monitoring paused" or "Monitoring resumed", and the `monitor.paused` and `monitor.resumed` [webhooks](/docs/webhooks/) fire. It needs permission to pause monitors as well as to edit them, and a resume can be refused with `403` by the plan. Any value other than `true` or `false` is refused with `400` "active must be true or false." It can go in the same request as other changes, such as `{"active": true, "interval": 300}`.
- A running check picks up a change at once. A paused check stays paused unless the request also sends `"active": true`.

`POST /api/v1/incidents/:id/retest` retests any check behind an incident, including a DNS check. The [MCP server](/docs/mcp/) and the [CLI](/docs/cli/) manage Uptime checks from version 0.4.0.
