Skip to main content

Monitoring WordPress Sites — What Breaks, and How to Catch It

WordPress runs over 40% of the web and fails in WordPress-specific ways. Keyword checks, wp-cron heartbeats, defacement detection and xmlrpc abuse.

The CompleteStatus Team 6 min read

Monitoring WordPress Sites — What Breaks, and How to Catch It

Want this checked continuously?

CompleteStatus grades your headers, SSL and email security 24/7 — free, commercial use allowed.

Monitor your site free

By most estimates WordPress powers over 40% of all websites, so when we talk about "monitoring a website," odds are we're talking about monitoring WordPress. And WordPress fails in its own particular ways: a plugin auto-update at 3 AM, a white screen that still returns HTTP 200, a scheduler that only runs when someone visits, and an attack surface that every bot on the internet has memorized. Generic ping-the-homepage monitoring misses most of it. Below is what actually breaks, and how to catch each one.

Auto-updates break sites while you sleep

Automatic plugin and theme updates are, on balance, a good thing: most WordPress compromises exploit known vulnerabilities in outdated plugins, and auto-update closes that window. But it also means your site changes itself at arbitrary hours with nobody watching. A plugin update that fatals on your PHP version, conflicts with another plugin, or breaks the theme's template hooks takes the site down at 3 AM on a Tuesday, and you find out from a customer email.

The fixes are layered:

  • Monitor at an interval that matches the risk. If your site can change itself hourly, a daily manual glance isn't monitoring. External checks every few minutes catch the broken update before your audience does.
  • Stagger risk. Keep auto-update on for security releases, but consider manual updates for major plugin versions, and do those when you're awake and watching.
  • Alert on response-time shifts too. A "successful" update that doubles page generation time is a slow-motion incident.

The white screen of death returns 200

WordPress's most famous failure mode, the blank white page from a PHP fatal error, frequently ships with a 200 OK status code, depending on where in the render the error lands. WP_DEBUG off (as it should be in production) means no error text, just emptiness. A status-code monitor sees 200 and stays green while every visitor sees nothing.

Since 5.2, WordPress tries to catch fatal errors with a recovery mode ("There has been a critical error on this website"). That is better, but the page can also return in ways a naive check calls healthy.

The answer is keyword assertions:

  • Assert presence of a string that only exists when the page truly rendered — your footer copyright, a nav label, a product heading.
  • Assert absence of the failure tells: Fatal error, There has been a critical error, Warning:, Error establishing a database connection. That last one is the classic: the database-down page can come back as a fully rendered HTTP 200 on many configurations.

One monitor with a presence and an absence assertion catches the white screen, the critical-error page, and the database error, none of which a status-code check reliably sees.

wp-cron only runs when someone visits

WordPress's scheduler, wp-cron, has a design surprise: it isn't cron. It piggybacks on page views, so scheduled tasks run when a visitor happens to hit the site after a task comes due. Two bad consequences:

  • Low-traffic sites don't run their tasks. Scheduled posts miss their publish time, backups don't fire overnight (when traffic is lowest and backups are scheduled), update checks lag.
  • High-traffic sites pay a tax: wp-cron logic firing on real user requests at the worst time.

The standard fix is switching to real cron. Disable the built-in trigger in wp-config.php:

define('DISABLE_WP_CRON', true);

Then run it on a real schedule, and while you're editing that crontab, give it a heartbeat so you'll know if it ever stops:

*/5 * * * * curl -fsS -m 30 https://example.com/wp-cron.php?doing_wp_cron > /dev/null \
  && curl -fsS -m 10 https://hb.example.com/ping/YOUR-CHECK-ID > /dev/null

The && means the heartbeat ping only fires when wp-cron actually ran successfully. If the server reboots and cron doesn't come back, if the crontab gets lost in a migration, or if wp-cron starts erroring, the ping goes silent and the missed heartbeat alerts you. Without this, a dead scheduler is invisible: the site looks fine, and you discover the problem when you need last night's backup and it doesn't exist.

Defacement and malicious plugins: watch for what changed

WordPress's popularity makes it the internet's favorite compromise target. A compromised site is often still up: uptime monitoring stays green while the site serves spam links, a defacement page, or a skimmer script injected by a malicious or backdoored plugin. Pharma-spam injections are often invisible to the site owner entirely, shown only to search engine crawlers and poisoning your rankings for weeks before anyone notices.

This is where change detection earns its keep:

  • Content change monitoring on key pages flags unexpected diffs — new outbound links, injected keywords, a replaced homepage.
  • Watch the rendered page, not just the HTML you deployed. Injected scripts often ride in via a plugin or database, not your theme files.
  • Alert on new external domains appearing in your pages. Your homepage suddenly loading a script from an unfamiliar domain is one of the strongest compromise signals there is.

Pair that with security-header and blacklist checks, and you'll hear about a compromise from your monitoring instead of from Google's "This site may be hacked" label.

xmlrpc.php: the door nobody uses but attackers love

xmlrpc.php is WordPress's legacy remote API. Almost nothing legitimate needs it anymore (the modern REST API replaced most use cases), but it remains a favorite for brute-force amplification (system.multicall lets an attacker try hundreds of password guesses in a single request) and for pingback DDoS abuse. If you don't use it, block it:

location = /xmlrpc.php {
    deny all;
}

Then add a monitor that expects the block: a check on /xmlrpc.php asserting a 403. If a plugin update, a host migration or a well-meaning colleague quietly re-opens it, you'll know the day it happens instead of the day it's abused.

Staging is not production

A surprising number of WordPress outages are really process failures: the update was tested on staging, worked, and broke production anyway: different PHP version, different plugin set, an object cache that exists on one and not the other. Monitor both, separately. Staging monitors can alert at low severity to a Slack channel; production pages a human. And check that staging isn't leaking into search results while you're at it. An indexed staging site splits your SEO and occasionally exposes half-configured plugins to the same bots probing production.

Monitoring that speaks WordPress

None of this requires WordPress-specific tooling. It requires monitoring that goes deeper than "did it return 200." CompleteStatus covers the list from one dashboard: uptime checks with keyword presence/absence assertions (the white screen, the database error page), heartbeat monitoring for your real-cron wp-cron setup, content change detection for defacement and injected scripts, SSL and domain expiry, and status-code assertions to verify your xmlrpc block stays blocked. When you do run planned plugin upgrades, use scheduled maintenance windows so the work doesn't page anyone — we've written up how to run maintenance windows properly.

Start monitoring your WordPress site free. The checks above take about ten minutes to set up.

Written with AI assistance and reviewed by the CompleteStatus team.