Skip to content
WordPress Tips

WordPress Maintenance That Actually Matters: What to Do Weekly, Monthly, and Yearly

Most WordPress maintenance checklists are theatre — deleting spam comments and running database optimizers that move nothing. This is the maintenance routine that actually prevents outages: 5-minute weekly error log checks, monthly autoload audits and staged plugin updates, quarterly disaster recovery drills, and a yearly nuclear review of licenses, orphaned data, and plugin sprawl. With WP-CLI commands, SQL queries, and a Slack automation script you can deploy today.

Thakur Aarti
13 min read
WordPress maintenance checklist - weekly monthly and yearly tasks

Most WordPress maintenance checklists are theatre. Delete spam comments. Empty the trash. Run WP-Optimize. None of that will save you at 2am when a plugin update nukes the checkout page and you’re watching orders fail in real time.

Real maintenance is about preventing specific failure modes — and almost every checklist on the internet ignores the five or six things that actually cause production outages. This is the maintenance routine we use on every site under our care plan, broken down by cadence and tied directly to the problem each task prevents.

The Maintenance Theatre Problem

Before we get into the actual work, let’s name the thing nobody wants to name: most “WordPress maintenance” tasks are busywork that make you feel productive without reducing risk.

Deleting 400 spam comments from Akismet does nothing. They’re already flagged. They don’t impact performance. Emptying the trash in the Media Library is cosmetic. Running a “database optimization” plugin that drops a few kilobytes from wp_posts revisions is invisible to your users.

What actually takes sites down in 2026:

  • A plugin update that silently breaks a filter your theme depends on
  • An autoload bloat problem in wp_options that pushes every page load past the slow-query threshold
  • A stuck WP-Cron job that piles up thousands of duplicate action scheduler entries in WooCommerce
  • An expired API key on January 1 that kills Stripe webhooks or a licensed plugin
  • A PHP version bump from the host that exposes a deprecation you never fixed
  • A backup that’s been “running” for six months but has never actually been restored

Everything in this post is aimed at catching those. If a task doesn’t connect to a real failure mode, we don’t do it, and we don’t recommend it.

The 5-Minute Weekly Check

Weekly maintenance has exactly one job: catch small problems before they compound. You’re not fixing anything yet — you’re just confirming nothing is on fire. This should take five minutes with WP-CLI and a monitoring dashboard.

Confirm the site is actually up (not just returning 200)

A 200 OK from Uptime Robot does not mean your site works. It means the server responded. Your checkout page can be returning a white screen inside a 200 response, and you’d never know.

Set up a synthetic check that loads a real page and asserts content is present. If you already use a monitoring service like Better Stack or Uptime Kuma, use the keyword-match feature, not the ping check. The keyword should be something only a working page renders — a product SKU, a footer copyright, anything.

For WooCommerce stores, monitor the checkout URL with a test that looks for the “Place order” button text. If that button vanishes, you need to know within minutes, not when the first angry email arrives.

Check the error log, not the dashboard

Every WordPress dashboard lies to you. Plugin authors catch their own warnings and swallow them. The only place that tells the truth is the PHP error log.

# SSH into your server
tail -500 /var/log/php/error.log | grep -v "PHP Deprecated" | less

You’re looking for new warnings, fatal errors, and anything from a plugin you haven’t touched in a while. Deprecated notices are noise — filter them out so you can see the real signals. If you see a new entry that wasn’t there last week, investigate before it becomes a fatal.

On managed hosts, the error log lives in different places: Kinsta surfaces it in MyKinsta under Logs, WP Engine under User Portal → Sites → Logs, Cloudways under Monitoring → Logs. Bookmark the direct URL.

Verify backups actually ran

Not “did UpdraftPlus show a green checkmark.” Actually verify:

wp option get updraft_last_backup --format=json

Or for BackWPup:

wp db query "SELECT option_value FROM wp_options WHERE option_name = 'backwpup_log'"

The timestamp should be within the last 24 hours. If it’s not, your backups stopped running and you haven’t noticed. This happens more often than you’d think — usually because a plugin update changed the backup plugin’s scheduled hook name and the old cron entry silently died.

Look at WooCommerce scheduled actions (if you run WooCommerce)

This is the single fastest-degrading thing in a WooCommerce store and the one thing most checklists ignore completely. Action Scheduler runs behind WooCommerce for webhooks, subscriptions, emails, and dozens of async tasks. When it gets stuck, orders don’t process and emails don’t send.

wp action-scheduler queue:status

If you see more than a few hundred pending actions, or any “failed” actions, something is wrong. Log into the admin and go to WooCommerce → Status → Scheduled Actions, filter by “failed”, and read the last error. Nine times out of ten it’s a payment webhook failure or a subscription renewal bug. All ten out of ten times, it’s a problem.

The 30-Minute Monthly Deep Check

Monthly maintenance is where you catch slow rot — the stuff that degrades gradually enough that you’d never spot it in a weekly check.

Audit the wp_options autoload

This is the single most impactful check on this entire list, and nobody talks about it.

WordPress loads every option with autoload = ‘yes’ on every single page request. That’s how get_option() is fast — it’s already in memory. But plugins dump garbage into autoload constantly, and over a year of installs and removals, your wp_options autoload can grow to 5MB, 10MB, even 50MB. Every page load now reads 50MB of junk from MySQL before doing anything useful.

Check it:

SELECT SUM(LENGTH(option_value)) / 1024 / 1024 AS autoload_size_mb
FROM wp_options
WHERE autoload = 'yes';

If the result is over 1MB, you have a problem. Over 5MB and your site is slow for a reason you’ve never diagnosed. Find the culprits:

SELECT option_name, LENGTH(option_value) / 1024 AS size_kb
FROM wp_options
WHERE autoload = 'yes'
ORDER BY LENGTH(option_value) DESC
LIMIT 20;

Look at the top 20. You’ll usually see transients that should never have been autoloaded, dead plugin settings from plugins you uninstalled two years ago, and occasionally a plugin that’s caching an entire API response in an autoloaded option (this is a bug, report it).

Fix the obvious ones:

UPDATE wp_options SET autoload = 'no' WHERE option_name = 'some_giant_plugin_cache';

Then test. Don’t blindly disable autoload on anything with _transient_ in the name without understanding what the plugin is doing — transients that are supposed to be autoloaded can break functionality if you flip them. If in doubt, delete the transient entirely and let it regenerate.

Look for orphaned postmeta and usermeta

Every plugin you ever installed left rows in wp_postmeta and wp_usermeta. Some clean up after themselves. Most don’t.

SELECT meta_key, COUNT(*) AS count
FROM wp_postmeta
GROUP BY meta_key
ORDER BY count DESC
LIMIT 50;

You’ll see a list of every meta key on your site. Scroll it. If you see _yoast_wpseo_content_score with 40,000 rows and you removed Yoast six months ago, you’ve got orphans. Delete them:

DELETE FROM wp_postmeta WHERE meta_key = '_removed_plugin_meta_key';

Back up before running DELETE statements on production. Obviously.

Test an actual backup restore

This is the task that separates real maintenance from maintenance theatre. A backup you’ve never restored is not a backup — it’s a hope.

Once a month, pull the latest backup down to a staging environment and restore it. Confirm the database restored, confirm the files restored, confirm you can log in, confirm WooCommerce still loads orders. The first time you do this, you will find a problem. Probably that wp-config.php wasn’t in the backup, or that the backup plugin skipped uploads to stay under a size limit, or that the restore process silently corrupts serialized data because it doesn’t handle URL search-replace correctly.

This is the one monthly task that will save you more hours than every other item in this post combined. Do it.

Review plugin and theme updates, then update on staging first

Do not blind-update plugins on production. Ever. It doesn’t matter how trusted the plugin author is.

The process we use:

  1. Pull production to staging (WP-CLI wp db export + rsync, or use your host’s staging tool)
  2. On staging, run wp plugin update –all and wp theme update –all
  3. Visually walk through the five most important pages: home, a category, a product, checkout, account
  4. Run a quick Lighthouse or WebPageTest check on the homepage and compare to your baseline
  5. If anything changed, investigate before pushing to production
  6. Deploy to production during a low-traffic window

Yes, this takes 15 minutes. Yes, you should do it every month. No, you cannot skip it. The one time you skip it is the time WooCommerce 9.4 will deprecate a hook your custom checkout plugin relies on and checkout will break for four hours while you figure out why.

If you can’t do this yourself, this is exactly why managed maintenance services exist. Our WordPress Maintenance & Care Plans start at $99/month and include staged updates with visual regression checks on every push.

Check PHP deprecations before your host forces a PHP upgrade

Managed hosts bump PHP versions aggressively. You will get 30 days notice, then your site will be upgraded whether you’re ready or not. If you have PHP 7.4 code in a custom plugin and your host moves you to PHP 8.3, things break.

wp --allow-root eval 'echo PHP_VERSION;'
wp package install wp-cli/doctor-command --allow-root
wp doctor check --all

The doctor command is underrated. It catches dozens of real issues: constants defined late, cron running behind schedule, bad option values, missing required tables.

Also run a deprecation scanner like phan or rector against your custom code:

composer global require rector/rector
rector process wp-content/plugins/your-custom-plugin --dry-run

Don’t let the host pick the date you fix your code. Pick it yourself.

The Quarterly Safety Drill

Every three months, stop doing normal maintenance for an afternoon and do the things you’d do if something catastrophic happened. You’re not reacting to a real incident — you’re practising for one.

Full backup restore to a fresh server

Not staging. A brand new server. Spin up a VPS on Vultr or Hetzner for $5, restore your latest backup from scratch, and get the site working on a new hostname. Time yourself.

This tests three things your monthly restore doesn’t: (1) your backup is portable, not tied to a specific host’s environment; (2) your documentation is good enough that someone else could do this; (3) your DNS and SSL workflow is actually ready for a rebuild.

The first time you do this, it will take eight hours. After a year of quarterly drills, it takes ninety minutes. That’s the difference between “we’re down for a day” and “we’re down for an hour” when disaster strikes.

Security audit — not a plugin scan

Running Wordfence and feeling reassured because it says “no threats found” is not a security audit. A real quarterly security check:

# Look for files modified in the last 30 days under wp-content
find wp-content -type f -mtime -30 -name "*.php" | xargs ls -la | sort -k6,7

# Compare your core files against WordPress.org checksums
wp core verify-checksums

# Check every plugin against the repo
wp plugin verify-checksums --all

# Hunt for base64_decode, eval, and suspicious functions
grep -rEn "base64_decode|eval\s*\(|gzinflate\s*\(" wp-content/ --include="*.php"

Every file that shows up in the last query needs a human eyeball. Plugin authors legitimately use base64_decode sometimes, but they’re rare. If you see it in your uploads folder, that’s an active compromise and you have a bad afternoon ahead of you.

User and permissions audit

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

You’ll be surprised. Every client site we audit has at least one old administrator that should have been removed months ago — a former contractor, a previous developer, an e-commerce consultant who “just needed access for a week.” Delete them.

Then check application passwords:

wp user application-password list --user=admin_username

Revoke anything you don’t recognise. Application passwords are the most commonly exploited credential in WordPress because nobody audits them.

The Yearly Nuclear Review

Once a year, do the things that take too long to do monthly but are disasters if you ignore for years.

Renew every license and API key before December 31

This is the one yearly task that nobody documents and everyone forgets. Write it into a spreadsheet:

ServiceRenewsExpiresCostOwner
Stripe webhook signing secretN/ARotate annuallyDev
Google Maps API keyJan 15$200/mo capOps
Domain registrationMar 22Mar 22$15Ops
SSL certificate (if manual)Oct 1Oct 1$0 (LE)Dev
Yoast PremiumAug 4Aug 4$99Marketing
WP RocketJun 12Jun 12$59Dev
BoxBuilder for WooCommerceNov 1Nov 1$49Dev

If you don’t have this spreadsheet, build it this year. January 1 is the single worst day of the WordPress year because every Stripe plugin, every licensed premium plugin, and half of all API integrations renew on that date. Sites break at 00:01 and nobody is around to fix them until January 3.

Database major cleanup

Once a year, with a fresh backup, do the things you’ve been too scared to do:

-- Delete post revisions older than 1 year (keep recent ones)
DELETE FROM wp_posts WHERE post_type = 'revision' AND post_date < DATE_SUB(NOW(), INTERVAL 1 YEAR);

-- Delete orphaned postmeta
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.ID IS NULL;

-- Delete orphaned term relationships
DELETE tr FROM wp_term_relationships tr
LEFT JOIN wp_posts p ON tr.object_id = p.ID
WHERE p.ID IS NULL;

-- Rebuild indexes
OPTIMIZE TABLE wp_posts, wp_postmeta, wp_options, wp_usermeta;

Run these on staging first. Always. A year of orphan cleanup on a large WooCommerce store can delete 400,000 rows. That’s a noticeable operation.

Kill every plugin you’re not actively using

Open your plugins page. For every single plugin, ask: “if I removed this, what would break?” If you can’t answer that in one sentence, uninstall it. Don’t deactivate — uninstall. Deactivated plugins still have code on disk, still have rows in wp_options, and are an attack surface.

We regularly audit client sites that have 40+ plugins and work with them to get that number to 15. Performance improves measurably every time. Security surface shrinks. Update anxiety drops.

Run a full Core Web Vitals baseline

Once a year, run a proper performance audit — not just PageSpeed Insights. Use real field data from CrUX, cross-reference with WebPageTest, and track INP specifically (Interaction to Next Paint replaced FID in 2024 and the measurement methodology is completely different). If your INP on product pages is over 200ms, you have work to do and you should know that going into the next year’s planning.

Automating the Things Worth Automating

Half of this post can be automated. Wire it up once and let it run.

#!/bin/bash
# /usr/local/bin/wp-weekly-check.sh
# Run via system cron every Monday at 8am

SITE_PATH="/var/www/your-site"
SLACK_WEBHOOK="https://hooks.slack.com/services/..."

cd "$SITE_PATH" || exit 1

# Check autoload size
AUTOLOAD_MB=$(wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) FROM wp_options WHERE autoload='yes';" --skip-column-names)

# Check failed action scheduler
FAILED_ACTIONS=$(wp db query "SELECT COUNT(*) FROM wp_actionscheduler_actions WHERE status='failed';" --skip-column-names 2>/dev/null || echo "0")

# Check last backup age
LAST_BACKUP=$(wp option get updraft_last_backup --format=json 2>/dev/null | jq -r '.backup_time // 0')
NOW=$(date +%s)
BACKUP_AGE_HOURS=$(( (NOW - LAST_BACKUP) / 3600 ))

MESSAGE="Weekly check: autoload=${AUTOLOAD_MB}MB, failed_actions=${FAILED_ACTIONS}, backup_age=${BACKUP_AGE_HOURS}h"

curl -s -X POST -H 'Content-type: application/json' \
  --data "{\"text\":\"$MESSAGE\"}" \
  "$SLACK_WEBHOOK"

Drop that in cron. Review the Slack message every Monday morning with your coffee. If anything looks wrong, you have exactly one task this week: investigate.

The things you should never automate: plugin updates on production, database cleanup on production, restoring backups. Those are decisions that need a human at the keyboard.

What to Do When Something Actually Breaks

All of this is prevention. When prevention fails — and it will — the response matters more than the checklist.

First: don’t panic-update. When a site goes down, the instinct is to start clicking “update” on every plugin. That is the worst possible response. You don’t know what broke, and updating compounds the problem. Isolate the change. If the error started after you updated Plugin X, roll back Plugin X first:

wp plugin install https://downloads.wordpress.org/plugin/plugin-name.1.2.3.zip --force

Most plugins in the official repo have old versions available at predictable URLs. Write a cheat sheet of rollback URLs for your most critical plugins and keep it in your password manager.

Second: use the error log as your source of truth. The dashboard will tell you the site is fine. The error log will tell you exactly which file, which function, which line. Start there every single time.

Third: if you cannot fix it in 10 minutes, revert. Restore the database to before the problem. Restore the files. Take the L on whatever changes you lose, because a 10-minute restore beats a 90-minute debug session every time. You can always re-do the changes tomorrow on staging.

For white-screen emergencies specifically, we’ve written up a full recovery playbook in our WordPress white screen of death fix guide. Bookmark it now, not when you need it.

The Honest Summary

Here’s what you should actually do, in priority order:

  1. Set up a keyword-based uptime check on your most critical page this week. Not next week. This week.
  2. Schedule a 15-minute weekly review: error log, backups, action scheduler.
  3. Schedule a 30-minute monthly review: autoload audit, staged updates, backup restore test.
  4. Schedule a quarterly disaster drill: restore to fresh server, security audit, user audit.
  5. Build your yearly license spreadsheet now, before something on it expires.

Everything else on every other maintenance checklist on the internet is optional. These five things are what separate sites that stay online from sites that don’t.

If maintaining a WordPress site at this level isn’t something you can fit into your week — and for most agencies and busy developers, it isn’t — our WordPress Maintenance & Care Plans handle everything in this post, every month, on every site you own. $99/month, staged updates, real backup tests, and a human who reads the error log.

You don’t need a longer checklist. You need a shorter one you actually run.

Leave a Reply

Your email address will not be published. Required fields are marked *