A surprising share of "my uptime monitor is wrong" problems are really "my uptime monitor isn't allowed to reach the thing it's supposed to measure." The endpoint wants a token. The staging site is behind basic auth. The WAF decided a request with no browser fingerprint is a bot and served it a 403. The API returned an HTML landing page because nothing asked for JSON. In each case the fix is the same small field on the monitor form: Custom Headers, a JSON object of header names and values that CronAlert sends with every check, on every plan.
This guide covers the five jobs that field does, the one thing it can't (assert on a redirect), and the credential hygiene that goes with storing a secret in a monitor.
What the field is
On an HTTP monitor's create or edit page, under the request settings, Custom Headers takes a JSON object. Each key is a header name, each value a string, up to 4,096 characters in total:
{
"Authorization": "Bearer <monitor-token>",
"Accept": "application/json",
"User-Agent": "CronAlert-Monitor/1.0 ([email protected])"
} The same headers are sent on every check, from every region on a multi-region monitor, for whatever method the monitor uses (GET by default; HEAD, POST, PUT, PATCH, and DELETE are available, with no request body). The REST API and the MCP server take the same object as a JSON string in the headers field, which is how teams that create monitors from CI set them.
1. Authenticate to a protected endpoint
The most common use. An API's authenticated endpoints, an internal dashboard's health route, an admin panel: anything that returns 401 to a stranger. Mint a credential specifically for the monitor, give it the least access that lets the endpoint answer, and put it in an Authorization header (or X-API-Key, or whatever the service uses). Expect 200. Now the monitor measures the thing behind the login, not the login.
Then add the inverse monitor, which costs nothing and catches a different failure. Point a second monitor at the same endpoint with no header and set the expected status code to 401. A single exact code is what the field takes, and 401 is as valid as 200. If a deploy ever drops the authentication middleware, the protected endpoint starts answering strangers with 200, the first monitor stays happily green, and the second one fires. An uptime monitor that alerts when something becomes reachable is unusual, and it's one of the cheapest security regression checks you can run. Monitoring internal tools builds on this for admin panels specifically.
2. Basic auth, the right way
Staging and preview environments are usually behind HTTP basic auth, and the reflex is to write the credentials into the URL: https://user:[email protected]. Don't. CronAlert's checks use the standard fetch API, which refuses to send a request whose URL contains credentials, so the monitor fails with an error on every check instead of authenticating. Use the header the browser would have sent:
# build the value once in a shell
printf 'user:password' | base64
# dXNlcjpwYXNzd29yZA==
# custom headers on the monitor
{ "Authorization": "Basic dXNlcjpwYXNzd29yZA==" } Base64 is encoding, not encryption; the value is the password with a hat on. Make the staging credential its own user that exists for monitoring, not the one the team shares in a password manager, and see the hygiene section below. Monitoring staging environments covers what to check once the monitor can get in.
3. Identify the monitor with a User-Agent
CronAlert checks don't announce themselves with a distinctive agent string; they carry the headers you give them and nothing else that identifies them. That's a feature if you want the monitor to look like ordinary traffic, and a nuisance when you're trying to find its requests in your logs or exclude them from analytics and rate limits. Set one:
{ "User-Agent": "CronAlert-Monitor/1.0 ([email protected]; team=platform)" } Now grep CronAlert-Monitor access.log shows every check and nothing else, your analytics can drop them, and your rate limiter can give that agent its own bucket so a 1-minute monitor on a strict endpoint never trips the 429 you're monitoring for. Including a contact in the string is a courtesy to whoever reads the logs next year. One caution: a User-Agent is trivially forged, so use it for identification and filtering, and use the next section's secret header for anything that grants a bypass.
4. Get past a WAF or bot protection
Bot-protection products judge requests by browser fingerprints the monitor doesn't have, so a protected site can hand the check a 403, a 429, or a 200 challenge page that fails a keyword assertion while real users are fine. Those are real false positives, and the right fix is a rule that exempts the monitor, not turning the protection off. Give the monitor a secret:
{ "X-Monitor-Token": "<40+ random characters>" } Then add a rule where the protection lives. In Cloudflare, a WAF custom rule with a Skip action matching http.request.headers["x-monitor-token"][0] eq "…", scoped to the paths the monitor hits, skips the rules you select. AWS WAF takes a header-match statement with an Allow action placed above the managed rule groups. Vercel's firewall supports custom rules with a Bypass action on header conditions. Rate-limiting rules can exclude the same header. In every case, scope the exemption to the monitored paths, because anyone holding the token holds the bypass; keep the token long, random, and rotated when someone who knew it leaves. If a product offers no exemption by header at all (some entry-level bot modes don't), monitor a path it doesn't cover, such as a dedicated health endpoint; health check endpoint design covers making that path honest.
Cloudflare Tunnel users have a specific version of this: an Access-protected hostname wants CF-Access-Client-Id and CF-Access-Client-Secret from a service token, which the tunnel guide walks through.
5. Ask for the response you want to check
Many endpoints negotiate. An API root returns HTML documentation to a browser and JSON to a client that sends Accept: application/json; a localized site picks a language from Accept-Language; a versioned API reads Accept: application/vnd.example.v2+json. If your Pro keyword assertion is looking for "status":"ok", make sure the request asks for the representation that contains it, or the check will fail on a perfectly healthy endpoint that answered with the HTML variant. API endpoint monitoring has more on choosing the assertion.
One header you might reach for and shouldn't rely on: Cache-Control: no-cache on the request. Most CDNs ignore request-side cache directives by default, so it won't make a monitor test the origin. If you need to know the origin is alive behind a CDN, monitor a path the CDN doesn't cache; monitoring CDN and static assets explains the split.
What headers can't do: assert on a redirect
CronAlert follows redirects, as a browser does. That's usually what you want, and it has one consequence worth knowing: the expected status code is compared with the final response, so a monitor expecting 301 or 302 can never pass. If you want to know that http:// still redirects to https://, or that the apex redirects to www, monitor the final destination and expect 200, and on Pro add a keyword that only the destination page contains. The status codes guide covers what the rest of the codes mean for a monitor.
Keeping the secrets small
Anything you put in Custom Headers is stored with the monitor and visible to anyone on the team who can open its edit page. That's fine for a User-Agent and a problem for a production admin token. Four habits keep it sane:
- One credential per monitor, minted for it. A read-only API key, a basic-auth user named
monitor, a service token scoped to one application. Never the key you also use for deploys. - Least access. The credential needs to make one endpoint answer. If the endpoint is a health route, the credential should be able to read exactly that.
- Rotate on departure. When someone with team access leaves, rotate monitor credentials along with everything else, and update the headers. On the Business plan, the audit log records who edited which monitor.
- Prefer a dedicated path. The best credential is none: a health endpoint that answers without a session, exposes nothing sensitive, and is excluded from bot protection removes the header entirely. Headers are the tool for when you can't have that.
Frequently asked questions
Can I set different headers for different regions?
No; a monitor's headers go with every check it makes, from every region.
Can a monitor send a request body?
No. Methods other than GET are supported, but without a body; headers are the only request customization. Monitor an endpoint that answers meaningfully to a bodiless request.
Can I override the Host header?
No. The Host is set from the URL. To test a backend by IP, use a TCP port monitor or give the backend a hostname.
Will the header fix "403 from a bare IP address"?
No; that one is Cloudflare refusing to fetch bare IPs at all. Use a hostname or a TCP monitor.
Which plan?
Headers and non-200 expected status codes are on every plan. Keyword and content assertions are Pro ($5/mo).
Let the monitor in, then make it prove something
A monitor that can't reach the real endpoint measures the wall in front of it: the login page, the challenge page, the HTML fallback. A header gets it through the wall; an expected status code, and a keyword on Pro, make it prove the thing on the other side is working. Create a free account, add the header, and add the 401 inverse monitor while you're there. Related reading: setting timeouts, why monitors from other tools get blocked, setting up uptime monitoring in five minutes, and choosing between HTTP, TCP, and heartbeat monitors.