Health and monitoring
Ask your Worker’s address for /health and you get one of three answers. That, plus a few lines in the Worker’s log, is how you find out whether a deployment is working before one of your readers finds out for you.
GET /health
Section titled “GET /health”No authentication, no configuration, and cache-control: no-store on every response.
| Response | What it means |
|---|---|
200 {"status":"ok","database":"ok"} |
Running, and the comments table exists, so the first migration ran. |
503 {"status":"degraded","database":"unmigrated"} |
The database is reachable and Charcha’s tables are not in it. |
503 {"status":"degraded","database":"unreachable"} |
The D1 binding itself refused the query. |
Two fields, and no fourth response. Nothing about counts, nothing about your configuration, nothing about the request that asked.
unmigrated is what a deploy whose migration step failed looks like, and it is the failure a one-click deploy actually produces, because Cloudflare’s deploy button creates the D1 database and does not migrate it. The fix is on the deploy page. Read the build log for the wrangler d1 migrations apply DB --remote line, and check whether the build token carries D1.
unreachable means the query threw. The reason D1 gave is in the Worker’s log and nowhere else. The endpoint deliberately does not repeat it.
If you have a monitor pointed here from an older deployment, one thing changed: database: "error" no longer appears, and unreachable replaced it. Anything reading only the HTTP status sees no difference.
What a 200 does not prove
Section titled “What a 200 does not prove”It does not prove every migration ran. The check looks for the table that migration 0001 creates, so a migration set that failed partway through a later step still answers ok. Your build log is what settles that. Closing the gap is on the not built yet list.
It proves nothing at all about your configuration. A deployment with no dashboard password, no Turnstile secret and an empty origin allowlist answers 200 quite happily. Configuration is reported on the Setup tab in the dashboard, which is behind the password on purpose. An unauthenticated caller learning which of your defences are switched off is reconnaissance.
The endpoint is public, and worth thinking about on those terms once. unmigrated is a real fact about your deployment that anyone can read off it. What it adds is a cheap oracle rather than a new disclosure, since GET /comments fails on an unmigrated deployment too, and that is public as well. The root page at / is the opposite case and reports nothing whatsoever.
The retention job
Section titled “The retention job”ip_hash is the only reader-derived identifier Charcha stores, and it does not stay in your table.
A Cron Trigger runs daily at 0 4 * * * and nulls ip_hash on every comment older than the retention window. One trigger, well inside the Workers Free plan’s limit of five per account.
The window is 30 days out of the box. It is a privacy policy rather than a constant, so you can change it through the ip_hash_retention_days setting without redeploying. Anything unset or invalid falls back to 30.
Nothing else is purged. The comment, the author’s name, their email address and the body are your site’s record and they stay.
How you know it ran
Section titled “How you know it ran”Each run writes one JSON line carrying event: "ip_hash_purge", how many rows it purged, the retention window it used and the cutoff timestamp it computed. That is the string to search your log for.
On failure it logs and re-throws, which marks the Cron invocation as failed, and that is the signal you actually see. A purge that failed quietly would leave the identifier in your table indefinitely and invisibly, which is a privacy incident rather than a missed chore.
The job only touches rows that still hold a hash, so its cost follows the retention window rather than the size of your table.
Set no IP_HASH_SECRET and no hash is ever written, so there is nothing here to purge, and the per-address rate limit abstains as well. See Spam defence.
What else the Worker tells you
Section titled “What else the Worker tells you”Observability is on and source maps are uploaded, so a stack trace in the Cloudflare dashboard points at real lines of Charcha rather than at minified soup.
Three log lines are worth recognising, because each one names a guard that is switched off.
All three are written once when a fresh copy of your Worker starts up, not once per request. Cloudflare shuts a copy down when nothing has hit it for a while and starts another on the next request, so a quiet log is not proof that every guard is on. You may be watching a copy that started before you did. Deploying again gives you a fresh one to watch.
guard: dashboard-passwordmeans there is noCHARCHA_DASHBOARD_PASSWORD, so the dashboard is refusing everyone.event: "spam_config"withlayer: "rate-limit"means the per-address half of rate limiting is not running, because there is noIP_HASH_SECRETor noCF-Connecting-IPon the request.event: "notify_config"means nothing can run after the response on that invocation, so a notification cannot be deferred.
A notification that fails to send says so in the log, is not retried, and cannot yet be surfaced anywhere the dashboard can show you.