KBeezie

There's no place like ::1

Menu
  • Home
  • Start Here
  • Security Series
  • About

WordPress Plugins on a VPS: When They Hurt More Than They Help

2026/05/15

What you need in place before removing these plugins

If a plugin is currently compensating for something your server stack doesn't do, removing it will make things worse. Here's what should already be handling each job:

1. Edge caching (CDN). Anonymous page views should be served from Cloudflare, Fastly, or BunnyCDN — not your origin. Verify with cf-cache-status: HIT in response headers. If it says DYNAMIC or MISS, edge caching isn't configured yet.

2. Static asset caching at nginx. A static_caching.conf that sets expires max on hashed assets (CSS, JS, fonts, images with content-hash filenames) ensures repeat visitors never re-request the same static files.

3. Database maintenance via cron. If the plugin was cleaning post revisions and optimizing tables, replace it with a weekly cron via wp-cli (the WordPress command-line toolkit — not preinstalled on most VPS images, but available in all major package managers):

0 4 * * 0 wp db optimize --path=/path/to/site/public_html

4. PHP-FPM opcache enabled and sized. Default on Debian, but verify: opcache.memory_consumption=128, opcache.max_accelerated_files=10000.

5. nginx FastCGI cache (optional). For pages that bypass the CDN — logged-in users, admin — nginx's fastcgi_cache serves full HTML from a local disk cache, avoiding PHP entirely for repeat requests. Independent of any WordPress plugin.

A note on plugins that generate static files: Not all caching plugins force PHP bootstrap. Some — WP Super Cache in mod_rewrite mode, WP Rocket, and similar — write flat HTML files to disk. If your nginx config checks for those files with try_files before passing the request to PHP, the cached page is served by nginx directly in kernel space — no PHP involvement at all. That's a legitimate architecture even on a VPS. The catch is that you need the rewrite rules set up correctly. If nginx skips the file check and passes every request to index.php first, those static files are never touched, and you're back to full bootstrap on every page view — the plugin cost with none of the benefit.

6. Monitoring. Monit with load thresholds, PHP-FPM slowlog, and atop for retrospective forensics. If you're removing a plugin that previously masked performance problems, you need to see what happens next.


What to audit in any WordPress plugin

The WP-Optimize failure had a specific shape. Any plugin that does these things on init carries the same risk:

file_exists(), is_dir(), stat() in a loop ■ High risk
I/O contention under concurrent access. Search plugin source for loops containing filesystem calls.
Hooks into init and does more than register hooks ■ Medium-high risk
Runs on every request. Check add_action('init', ...) callbacks.
Scans the filesystem to "discover" things ■ High risk
Scales with filesystem size. Pre-scanning, cache warming, drop-in detection.
Makes outbound HTTP requests on every page load ■ Medium risk
Network latency under load. External API calls, update checks, telemetry.

This isn't a WP-Optimize-only problem. The same audit applies to any plugin in the same category — W3 Total Cache, WP Super Cache, WP Rocket, and others all hook into init to varying degrees. WP-Optimize happened to be the one installed here, but the underlying dynamic is universal: filesystem scanning on every request, multiplied by concurrent traffic, produces the same disk-queue spike regardless of which plugin name is on the tin.

Plugins that register hooks on init but defer actual work to specific request paths — admin only, AJAX only, specific post types — are fine. Plugins that do work on init unconditionally are not.


Outcome

WP-Optimize were removed from all WordPress sites. The file_exists() bottleneck was eliminated. The server returned to baseline: load 0.02, memory 620 MB, PHP requests completing in 50–200 ms. Post-save operations in wp-admin returned to expected speed — which retrospectively explained why those had felt sluggish for months.

The next Googlebot slow-crawl will pass through unnoticed, because each request no longer carries a 10-second I/O penalty.


This took about an hours to diagnose from alert to root cause, and ten minutes to fix. The plugin had been installed for well over a year.

Technical Audit Summary

This article documents a live production incident diagnosed from PHP‑FPM slowlog stack traces, Monit alert thresholds, nginx access log analysis, fail2ban ban records, and atop process accounting — during an active investigation on May 15, 2026.

Last Audit: May 2026
Environment: Debian Trixie (13)
Nginx: 1.30.1
PHP-FPM: 8.5.6

Compatibility: While diagnosed on this specific stack, the core finding — filesystem-scanning plugins on init multiplying I/O under concurrent traffic — applies to any PHP application running on virtualized block storage, regardless of CMS or server software.

  • ← Previous
  • 1
  • 2
  • 3
  • Next →
©2026 KBeezie | Disclaimer | Privacy Notice