performance · core web vitals

Why anti-spam plugins slow down your site

·7 min read

The anti-spam plugin is a deceptively expensive piece of software. The install pitch is "five minutes, no configuration." The hidden bill is a few extra database queries on every request, a third-party script in your critical render path, and a quietly worsening Core Web Vitals score that drags on rankings the plugin was supposed to protect.

This isn't an attack on any specific plugin — most of them are written by capable people doing their best inside the architectural constraints of a CMS plugin model. It's a structural problem with where the work happens. Below is what actually changes when you install one, why it's measurable in your Web Vitals, and the alternative pattern that avoids the cost.

Where the slowdown actually comes from

Database queries on every page

A typical anti-spam plugin registers itself into the plugin lifecycle, adds one or two custom tables (or a handful of options rows), and runs a config lookup on almost every request. Plugins also tend to read from the options table on the front-end to render plugin-specific notices, badges, or settings. None of those queries is expensive in isolation. Stacked across ten installed plugins on a budget host, they collectively account for a substantial chunk of TTFB.

The visible symptom is a TTFB that crept from 200ms to 800ms over a year of plugin accretion, with no single plugin obviously to blame. Anti-spam is rarely the worst offender, but it's almost always on the list.

Third-party scripts in the critical path

Plugins that include client-side anti-bot logic — the ones that ship JS to "verify the user is human" via mouse movement or keystroke timing — load that script on every page that contains a form. In practice that's most of your pages, because comment forms and contact forms are ubiquitous. The plugin doesn't usually defer the script, because deferring it would defeat its purpose.

Third-party scripts in the critical path show up directly in your Largest Contentful Paint (LCP) and Interaction to Next Paint (INP) numbers — both of which Google uses as page-experience signals. A 200KB unminified JS bundle on a 3G connection is real seconds of delay, and the user paying that delay didn't even submit a form.

Cron tasks firing inline

WordPress's wp-cron doesn't actually run on a cron schedule by default — it runs on a request that happens to land near a scheduled task time. Anti-spam plugins schedule cleanup tasks (purging the spam queue, rotating logs, syncing rule updates from a remote API). Whoever's request triggers the task pays its full cost in their TTFB. If your traffic is bursty, the user who triggers cleanup is also probably the user least likely to forgive a slow page.

API calls from PHP, on the request

Akismet is the canonical example, and it's worth understanding because the tradeoff is structural. Akismet makes an HTTPS call to its API for every comment submission, blocking the comment-submit request until the response arrives. The latency budget is whatever your host's network gives you to akismet.com — typically 150-400ms. That latency lands directly on the comment author's submit click. It's not an LCP cost (the page already rendered), but it is an INP cost on the interaction that submits the form.

The Core Web Vitals story

Google's page-experience signal weights three metrics: LCP (largest contentful paint, render speed), INP (interaction to next paint, responsiveness), and CLS (cumulative layout shift, visual stability). Two of the three are degraded by typical anti-spam plugin patterns:

  • LCP takes a hit when the plugin ships render-blocking CSS, an enqueued script in the <head>, or a stylesheet that paints over the hero image. Most plugins enqueue assets in the wrong place because correctly deferring them is fiddly and risk-averse plugin authors err on the side of "it works."
  • INP takes a hit when the form-submit handler does synchronous work — the API call to a remote classifier, a sync write to a custom table, a captcha challenge dialog spinning up — before the form visually responds.

CLS is usually unaffected, which is the small mercy.

The structural alternative

Move the classifier out of the plugin sandbox and onto your application's server. The pattern is one HTTP POST from your form handler to a classification API, performed asynchronously where possible (the form submission can return optimistically and the classification result writes back to the row). User-facing rendering does not depend on the classifier being reachable.

What this buys you concretely:

  • No client-side script. The classifier never touches the browser. LCP unaffected. No third-party asset in your render budget.
  • One round trip on submit, not on render. INP is paid only on the form-submit interaction, not on every page that contains a form.
  • No DB schema additions. The classifier response is one number per row; a single column on your existing table holds it. No plugin tables, no options churn.
  • Fail-open is a config decision, not a plugin quirk. If the classifier API is down for 30 seconds, your form submit returns successfully (with the row marked unclassified) and a moderator handles it later. Most plugins fail closed and reject submits when their API is unreachable, which produces the worst possible user experience for a transient outage.

What the migration looks like

For most sites it's a one-evening change. The comment-submit handler (or contact-form handler, or signup handler — same shape) calls the classification API inline, gets a probability back, and routes to one of three outcomes based on thresholds. The plugin gets uninstalled and the database tables it added get dropped after a verification period.

The comments use-case and contact-form use-case show the integration as runnable code in a few common stacks. You're swapping a few hundred kilobytes of plugin for a 15-line server-side function.

The shorter version

Anti-spam plugins are slow for structural reasons that aren't fixable inside the plugin model: extra DB queries, render-blocking scripts, inline cron tasks, synchronous API calls on submit. The plugin pattern co-locates the classifier with the front-end, which is the wrong place for it. Server-side classification on a single API endpoint moves the work out of the request path your users actually experience.

See pricing

10,000 classifications / month free, $9 + $0.0001/req after that. The API docs have the full integration shape; see also the Siftfy vs. Akismet comparison if you're migrating away from a plugin today.