This guide is for anyone who installed a security plugin, watched TTFB climb, and could not tell whether the plugin was the cause.
A security plugin’s cost lands in three specific places: PHP executed on every request, database writes on every event it records, and table growth from the records themselves. A lightweight WordPress security plugin is not one that does less. It is one whose enforcement happens before PHP starts, so there is no per-request application work and nothing to write down about a request that was never processed.
The short version. If your
wp_optionstable is measured in hundreds of megabytes, something is autoloading data it should not. The fix starts with finding out which plugin, and the answer is a two-minute query, not a vendor claim.
The three places the cost actually lands
Most “plugin bloat” conversations stop at the number of plugins, which is close to useless as a diagnostic. Two plugins on the same site can differ by an order of magnitude in what they cost you, and the difference is architectural.
PHP executed per request. A firewall written in PHP has to load WordPress before it can evaluate anything. Core bootstraps, the plugin stack initialises, and only then does the rule run. The request gets blocked correctly, and you paid full application cost to block it.
Database writes per event. Every blocked attempt, login, file change and scan result that gets recorded is a write. On a site taking sustained automated traffic this is not incidental load, it is the dominant one.
Table growth from those records. Threat logs, activity logs and scan history accumulate. If any of it lands in wp_options with autoload set, WordPress loads it into memory on every single request, including requests that have nothing to do with security.
That last one is where the 200MB wp_options stories come from, and it is almost always a logging feature rather than a firewall.
The part most articles like this get wrong
A scanner writing heavily to your database is doing its job.
Detection is a record-keeping product. It has to observe traffic, evaluate files against known signatures, and store what it found, because a detection tool that does not remember anything cannot tell you what happened last Tuesday. Wordfence’s live traffic view and file-integrity monitoring are genuinely good, and they are expensive for exactly the reason they are useful. That is a trade, not a defect.
The same applies to the long-running PHP processes a scanner needs. A LiteSpeed developer posted a detailed critique on r/Wordpress this month arguing that the vendor’s recommended noabort rule is scoped too broadly, applying an exception to every PHP request when only the scanner’s own endpoint needs it. His scoping point is correct and worth reading. But the underlying requirement is not a flaw: reading and evaluating files inside the application takes time, so the process has to be allowed to finish.
The distinction that matters is not which vendor is careless. It is which layer a control operates at, because that determines what it can possibly cost.
Where each layer sits, and what it costs you
| Layer | Runs | Cost profile | What it can decide |
|---|---|---|---|
| Network / CDN | Before your server | Paid externally | Request patterns, rate, geography |
Rewrite layer (.htaccess, nginx conf) | Before PHP starts | A rule evaluation, no process | Whether a path resolves at all |
| PHP firewall | After WordPress loads | Full bootstrap per request | Request contents, plus WordPress context |
| Scanner | Scheduled or on demand | Sustained PHP plus storage | What changed on disk |
| Activity log | On every event | A write per event | Who did what, after the fact |
Read the last column. The rewrite layer can only answer questions that can be settled from the request itself, which is a real limitation: it cannot know who is logged in or whether a user has the capability they are claiming. Broken access control was the largest single category Patchstack’s RapidMitigate blocked in 2025 at 57% of attacks, precisely because it looks like normal authenticated traffic, and no rewrite rule catches that.
What the rewrite layer can do is decide that a path does not resolve. And that turns out to cover most of the volume.
One operator running fail2ban across two servers posted his jail counts on r/Wordpress this month: badly-behaved crawlers banned 915 and 1,129 addresses, while the wp-login jails banned 15,189 and 50,386 and the xmlrpc jails another 11,345 and 9,530. Those are his counters on his servers, not a general measurement. His conclusion is the useful part, and it is one I would sign: the bans happen at the server before PHP starts, so a security plugin never sees that traffic and cannot report it to you.
A plugin dashboard showing you a low number of blocked attacks might mean you are under less attack. It might also mean something upstream is absorbing it. Those look identical from inside WordPress.
Find out which plugin is expensive on your site
Stop reading vendor claims, including mine, and measure. Everything below is read-only.
Total autoloaded weight, which is what gets pulled into memory on every request:
sql
SELECT SUM(LENGTH(option_value)) AS autoload_bytes
FROM wp_options
WHERE autoload IN ('yes','on','auto','auto-on');The accepted values in the autoload column were expanded in recent WordPress releases, so the IN list keeps this working across versions. Anything past a few hundred kilobytes is worth investigating.
Then find the offenders by name:
sql
SELECT option_name, LENGTH(option_value) AS bytes
FROM wp_options
WHERE autoload IN ('yes','on','auto','auto-on')
ORDER BY bytes DESC
LIMIT 20;Option names are prefixed by the plugin that created them, so this names the culprit directly. The same thing through WP-CLI:
bash
wp option list --autoload=on --fields=option_name,size_bytes \
--orderby=size_bytes --order=desc | head -20And table sizes, where accumulated logs show up:
sql
SELECT table_name,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS mb
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC
LIMIT 10;Two things I would check before blaming security tooling. Transients that never expired are a common cause of wp_options growth and usually come from caching or import plugins. And orphaned tables from plugins deleted years ago keep their rows: uninstalling removes the code, not the data.
What changes when enforcement moves below PHP
WP Ghost’s 7G and 8G firewall rules are server-level rewrite directives in .htaccess, nginx config or web.config. SQL injection patterns, traversal attempts, file-inclusion probes and malformed requests are rejected before PHP starts and before any plugin code runs.
Path security works the same way. When 30+ default WordPress paths are reconfigured, a probe for /wp-login.php returns 404 at the rewrite layer. The brute-force phase never starts, because nothing at that address accepts a credential to fail against. There is no attempt to record, so there is no row to write, no table to grow, and no dashboard count to admire.
The honest limits, stated plainly. WP Ghost does not scan files, so it cannot tell you that you are already infected. The premium Security Threats Log does write to the database, because logging is logging and there is no version of it that does not. Nginx needs a config reload after path changes, which means shell access or hosting that reloads on file change. And no WordPress core files are modified: every path change is rewrite rules and output filters, so deactivating restores the defaults immediately.
I will not put a percentage on any of this. We do not have a published benchmark that would survive scrutiny across different hosts, and the queries above will tell you more about your own site than any number I could quote about somebody else’s.
When WP Ghost is the right choice
Your database grew and you traced it to security logging. If the autoload query above named a security plugin’s options, the layer you want is one that prevents rather than records. Decision trigger: your wp_options autoload weight is measured in megabytes and a security plugin’s prefix is at the top of the list.
You are on shared or low-resource hosting and bot traffic is the load. Rejecting a request before PHP starts is the cheapest possible outcome short of it never arriving. Decision trigger: your access log is mostly probes for paths you do not have.
You already run a scanner and want to reduce what it has to look at. Prevention narrows the surface the detection layer spends its time on. Decision trigger: your scanner is working correctly and its logs are full of attempts you would rather never happened.
WP Ghost protects 250,000+ active sites and does not modify WordPress core. The free version on wordpress.org includes the 8G firewall, path security and passkey 2FA if you want to test the approach on a staging copy first. If you want to know exactly what the plugin does and does not add to a page load, the KB answers that directly: is my site loading slower with WP Ghost and do I still need it if my host already has server-side protection.
FAQ
How do I tell which plugin is slowing down my WordPress site?
Start with the autoloaded options query above, which names the plugin by option prefix. Then check table sizes in information_schema. For per-request cost rather than storage, Query Monitor attributes queries and hook time to specific plugins on a live page load. Deactivating one at a time on a staging copy confirms it.
Does a security plugin slow down WordPress?
It depends entirely on where it runs. A firewall implemented as PHP evaluates rules after WordPress has loaded, so every request carries the full bootstrap. Rules implemented as server-level rewrite directives are evaluated before PHP starts. Logging features add database writes in both cases, which is usually the larger cost on a busy site.
What causes the wp_options table to grow to hundreds of megabytes?
Almost always autoloaded data that should not be autoloaded: expired transients that were never cleaned up, large serialised settings arrays, or logs written into options rather than a dedicated table. Security plugins are one source among several. Run the query before assigning blame.
Can I just delete the rows a security plugin created?
Not blindly. Deleting options a plugin actively reads will break it, and some log tables are referenced by the plugin’s own admin screens. Check whether the plugin has a retention or pruning setting first, since most logging features have one that defaults to keeping everything. Back up before touching anything.
Is WP Ghost a replacement for Wordfence?
No. Wordfence scans files and monitors integrity; WP Ghost changes the paths and rejects requests before PHP loads. They occupy different positions in the stack and most stacks worth copying run both. If you are choosing only one and your site may already be infected, take the scanner.
Does blocking requests at the rewrite layer mean I lose visibility?
Yes, partly, and it is a real trade. Traffic rejected before PHP starts is not recorded by anything inside WordPress. Your server access log still has it, so the visibility moves rather than vanishing, but you have to look somewhere other than a plugin dashboard to find it.
