---
title: Performance & content
description: Core Web Vitals and Lighthouse audits in a real browser, plus CDN, Brotli, CMS, favicon, sitemap, WebSocket, NTP and FTP/SFTP monitors.
canonical: https://watchfor.io/docs/monitors/performance
---

# Performance & content

Core Web Vitals and Lighthouse audits in a real browser, plus CDN, Brotli, CMS, favicon, sitemap, WebSocket, NTP and FTP/SFTP monitors.

Beyond "is it up?", these monitors check that your site is **fast**,
**correctly delivered**, and that supporting services behave.

## Core Web Vitals (browser)

Loads your page in a **real browser** and runs a full **Lighthouse** audit:
the three Core Web Vitals, the supporting lab metrics, and the four category
scores.

**Target:** the page URL.

- **Core Web Vitals:** Largest Contentful Paint (LCP) and Cumulative Layout
  Shift (CLS). **Interaction to Next Paint (INP) is not measured** — it
  needs real-user interaction data, which a synthetic audit cannot produce;
  Total Blocking Time is the lab stand-in for responsiveness.
- **Lab metrics:** First Contentful Paint, Total Blocking Time, Time to
  Interactive, Speed Index.
- **Scores (0–100):** Performance, Accessibility, SEO, Best Practices. The
  performance score is shown in the monitor list at a glance.
- **Evidence:** a full-page screenshot, a loading filmstrip, the top
  optimization opportunities with estimated savings, the Lighthouse version,
  and a link to the **full HTML report**.
- **7-day trend** of every metric on the Overview.

| Setting | Notes |
| --- | --- |
| **Device** | **Desktop** (fast CPU, cable network) or **Mobile** (4× CPU throttle, slow 4G). Fixed at creation — mobile and desktop scores aren't comparable, so create a second monitor to audit the other device. |
| **Timeout** | The audit budget, 60–300 seconds (default 180). |

Audits run from locations that have a browser available; the location
picker only offers those. Because one audit already answers the question,
there is no accelerated re-check phase — rules evaluate on the normal
interval, which has a higher minimum than lightweight checks. See
[Locations & scheduling](/docs/monitors/scheduling).

| Preset | Default | Fires when |
| --- | --- | --- |
| **Audit Status** | on | The audit could not complete. |
| **Largest Contentful Paint (LCP)** | on (critical, 4000 ms) | LCP is in the *Poor* band. |
| **Cumulative Layout Shift (CLS)** | on (critical, 0.25) | CLS is in the *Poor* band. |
| **Performance Score** | off | The performance score drops below your threshold (0–1). |
| **SEO Score** | off | The SEO score drops below your threshold (0–1). |

The default thresholds are Google's *Poor* boundaries; "needs improvement"
rules would be noisy on average sites, so add them only where you want them.
There is no INP preset: synthetic audits never produce INP, so a rule on it
could never fire. Free one-off audit:
[Core Web Vitals checker](/core-web-vitals-checker).

## CDN

Watches how a URL is **delivered**: which CDN or edge provider is in front of
it, whether the edge is really caching it, and how fast it answers.

Point it at the URL you care about, not just the hostname — `/` and
`/static/app.js` almost always have different caching rules, and a monitor on
the apex tells you nothing about your assets.

### How the provider is identified

Over sixty providers are recognised, including hosting platforms that run
their own edge — Hostinger, Kinsta, SiteGround, Wix, Squarespace — which is
what a large share of sites are actually served by.

Detection combines independent kinds of evidence: provider-specific response
headers, the CNAME chain, the network the edge address is announced from, and
branded delivery endpoints. Agreement between **different** kinds raises
confidence; five headers from the same vendor do not, because they are one
observation seen five times. Where a platform sits in front of a CDN the
report names both — a Kinsta site reads as *Cloudflare via Kinsta* — since the
settings you can change usually belong to the platform.

The report lists the evidence behind every verdict, with a link to the
vendor documentation the fingerprint came from. Providers we have no verified
fingerprint for stay unnamed rather than guessed at.

### Cache tracking

By default the monitor makes **two sequential requests** per check instead of
one. That second request is what makes cache measurement possible at all: one
request can tell you the edge missed, but only a second can tell "this is not
cacheable" from "this is not cached yet". It gives you `cdn.cache_hit_ratio`
and `cdn.ttfb_ms` to alert on.

Turn it off in **Settings → Measure cache behaviour** if you only want
provider detection and the cheaper single request.

When a provider publishes no cache header at all, the ratio is simply not
reported for that check rather than counted as a miss — so a quiet CDN never
looks like a broken one, and a rule on the hit ratio stays silent instead of
firing constantly.

A verdict of `DYNAMIC` or `BYPASS` is different: the edge did publish an
answer, and the answer was "I deliberately did not cache this". Those count
as non-hits, so a URL that is meant to be dynamic — a personalised page, a
logged-in view — sits at a hit ratio of 0 forever. That is deliberate,
because it means a URL that *stops* being cached moves the ratio and your
alert fires. It also means **a hit-ratio rule belongs on assets you expect
to be cached, not on your homepage.**

| Preset | Default | Fires when |
| --- | --- | --- |
| **Probe Status** | on | The site could not be fetched. |
| **CDN Not Detected** | off | No known CDN is in front of the site. |
| **Provider Must Be** | off | The detected provider differs from the one you set when enabling the rule (stored as the monitor's `expectedProvider`). |
| **Cache Stopped Working** | off | The share of requests served from the edge cache falls below your threshold (default 50%). Needs cache tracking on. |
| **Edge Response Slow** | off | Time to first byte from the edge climbs past your threshold (default 1000 ms). |
| **Error Status Returned** | off | The edge answers with a 4xx or 5xx. Separate from Probe Status, which only fires when nothing answered at all. |

Useful metrics for your own rules: `cdn.cache_hit_ratio` (0–1),
`cdn.ttfb_ms`, `cdn.status_code`.

Free: [CDN checker](/cdn-checker) — the same measurements from every probe
location at once, with a graded report.

## Brotli compression

Requests the page with `Accept-Encoding: br` and checks that the response
comes back Brotli-compressed — smaller payloads, faster loads. The default
rule **Brotli Not Supported** (warning) catches CDN configuration drift or an
origin upgrade that silently falls back to gzip. Free: [Brotli checker](/brotli-checker).

## Favicon

Confirms your **favicon** resolves and is served correctly, with a preview
on the Overview.

| Preset | Default | Fires when |
| --- | --- | --- |
| **Favicon Missing** | on (warning) | No favicon could be found. |
| **Wrong Content-Type** | off | The file is served with a non-image content type. |
| **Favicon Too Small** | off | The file is smaller than *N* bytes — usually a placeholder or an error page. |

## CMS

Detects the **CMS / platform** behind a site (WordPress, Drupal and others).

| Preset | Default | Fires when |
| --- | --- | --- |
| **CMS Not Detected** | off (warning) | No known platform was detected. |
| **Provider Must Be** | off | The detected platform differs from the one you set when enabling the rule (stored as `expectedCms`) — catches an unexpected platform change or a hijacked page. |

## Sitemap

Validates your XML sitemap and samples the URLs it lists.

**Target:** the sitemap URL, or the site URL — common locations such as
`/sitemap.xml` and `/sitemap_index.xml` are discovered automatically, and
sitemap indexes are followed.

| Setting | Notes |
| --- | --- |
| **URLs to Check** | How many listed URLs to fetch per check (default 50). |
| **Capture broken URLs** | Store the URLs that failed so the Overview can list them. |
| **Timeout** | Seconds (default 30). |

| Preset | Default | Fires when |
| --- | --- | --- |
| **Sitemap Available** | on (critical) | The sitemap is missing or not valid XML. |
| **Broken URL Ratio** | on (warning, 20 %) | More than *N* % of the sampled URLs return errors. |
| **Broken URL Count** | off | More than *N* sampled URLs are broken. |
| **Minimum URL Count** | off | The sitemap lists fewer than *N* URLs — a deploy that emptied it. |

Free: [sitemap checker](/sitemap-checker).

## WebSocket

Opens a **WebSocket** connection (`ws://` or `wss://`), times the HTTP
upgrade handshake and, optionally, measures ping/pong latency and validates
a message.

| Setting | Notes |
| --- | --- |
| **Check Ping/Pong** | Send a WebSocket ping and measure the round trip. |
| **Timeout** | Seconds (default 10). |

| Preset | Default | Fires when |
| --- | --- | --- |
| **Probe Status** | on | Connection refused, TLS error or the upgrade failed. |
| **Slow Handshake** | off | The upgrade handshake exceeds the threshold. |
| **High Ping RTT** | off | Ping/pong round trip exceeds the threshold. |
| **High Total Time** | off | The whole check exceeds the threshold. |
| **Response Must Match** | off | After connecting, optionally send a message and require a reply matching a pattern (both set on the rule). |

## NTP

Queries a **time server** and checks the answer's quality — clock offset,
round-trip delay, jitter, stratum and whether the server reports itself as
synchronized. Important wherever clock drift breaks signing, tokens, TLS or
logs.

**Target:** hostname, IP or `host:port` (default port 123).

| Setting | Notes |
| --- | --- |
| **Sample Count** | Queries per check (default 4); offset and jitter are computed across them. |
| **Max Offset** | The acceptable absolute offset in milliseconds (default 100). |
| **Require Synchronized** | Fail the check when the server reports an unsynchronized clock. |
| **Timeout** | Seconds (default 5). |

| Preset | Default | Fires when |
| --- | --- | --- |
| **Probe Status** | on | No valid NTP reply. |
| **Clock Offset Too High** | on (critical, 100 ms) | The server's clock is off by more than the threshold. |
| **Server Not Synchronized** | on (critical) | The leap indicator says "unsynchronized". |
| **High Stratum** | off | Stratum above *N* — the server is too far from a reference clock. |
| **High Network Delay** | off | Round-trip delay above the threshold. |
| **High Jitter** | off | Sample-to-sample variance above the threshold. |

## FTP / SFTP

Checks **file-transfer** servers end to end: connect, log in, list a
directory, write (and delete) a small test file, and measure upload/download
throughput.

**Target:** hostname, IP or `host:port` (FTP default 21, SFTP default 22).

| Setting | Notes |
| --- | --- |
| **Username / Password** | FTP defaults to anonymous. SFTP accepts a password or a **private key (PEM)**; a password also works against servers that only offer keyboard-interactive prompts (OpenSSH with `PasswordAuthentication no`, `KbdInteractiveAuthentication yes`) — every prompt is answered with it. |
| **FTPS (Explicit TLS)** | FTP only — upgrade the connection with `AUTH TLS`. |
| **Timeout** | Seconds (default 10). |

The operations a check performs are driven by which rules are on: enabling
**List Failed** makes the check list the directory, **Write Test Failed**
makes it upload and remove a temporary file, and the speed rules run a
throughput test. Turn on exactly what you care about.

| Preset | Default | Fires when |
| --- | --- | --- |
| **Probe Status** | on | The check could not run. |
| **Connection Failed** | on (critical) | The server doesn't accept the connection. |
| **Authentication Failed** | on (critical) | Login is rejected. |
| **Write Test Failed** | off | The temporary file could not be written. Turn it on for accounts that are allowed to write. |
| **List Failed** | on (warning) | The directory listing fails. |
| **TLS Handshake Failed** | off (FTP; available only while **FTPS (Explicit TLS)** is on) | FTPS is configured but the TLS upgrade didn't complete. |
| **Slow Connection**, **Slow Login** | off | Connect or login time exceeds the threshold. |
| **Slow Upload Speed**, **Slow Download Speed** | off | Throughput drops below the threshold in bits per second. |

> **Info**
>
> The write test creates and then deletes a small file in the login
> directory, so the account needs write permission there. Many FTP and SFTP
> accounts are read-only (anonymous mirrors, drop-boxes, chrooted users),
> which is why **Write Test Failed** is off by default — turn it on only for
> writable accounts. The speed rules need the write test too.

## Also via API

Create with `type` set to `browser`, `cdn`, `brotli`, `favicon`, `cms`,
`sitemap`, `websocket`, `ntp`, `ftp` or `sftp`. Config keys and alert metrics
for each are in the [type catalog](/docs/api/monitor-types):
[Core Web Vitals](/docs/api/monitor-types#core-web-vitals),
[CDN](/docs/api/monitor-types#cdn), [Brotli](/docs/api/monitor-types#brotli),
[Favicon](/docs/api/monitor-types#favicon), [CMS](/docs/api/monitor-types#cms),
[Sitemap](/docs/api/monitor-types#sitemap),
[WebSocket](/docs/api/monitor-types#websocket), [NTP](/docs/api/monitor-types#ntp),
[FTP](/docs/api/monitor-types#ftp), [SFTP](/docs/api/monitor-types#sftp).

---

Canonical page: https://watchfor.io/docs/monitors/performance · All docs: https://watchfor.io/docs · Site guide: https://watchfor.io/llms.txt
