How Form Spam Is Wrecking Your Email Deliverability (And Nobody Tells You)

You filed your DKIM record, set up SPF, configured DMARC, and your transactional emails are still landing in spam folders. There is a good chance the cause is not your mail setup. It is your contact form quietly relaying spam through your domain.

The hidden relay problem

Most WordPress sites send mail directly from the web host using PHP’s mail() function or a basic SMTP plugin. Every contact form submission that gets accepted ends up as an outbound email — to you, with the visitor’s content. That message looks, from a mail-server reputation standpoint, like it came from your domain.

When the visitor is a spammer, what they typed in the message field is now an outbound email from your domain to your inbox, containing links to dating sites or crypto scams. Mailbox providers — Gmail, Outlook, Apple — score that against your domain. Enough of it, often enough, and your sender reputation drops.

How to tell if this is happening to you

Three signs:

  • Your contact-form notification emails now go to your own spam folder, not your inbox.
  • Your order confirmations or password resets get reported as junk by customers.
  • Google Postmaster Tools shows a falling “domain reputation” score over the past 90 days.

None of these are conclusive on their own. All three at once is a strong signal that you are relaying spam content through your forms.

Why standard anti-spam plugins miss this

Most anti-spam plugins decide whether the submission is spam. They do not look at what happens after. If the submission gets accepted, the email still leaves your server with the spammy content intact. The reputation damage is identical to if the plugin had not been installed.

What actually fixes it

The fix has two pieces:

  1. Filter the inbound submission using AI intent scoring (so realistic-looking spam still gets caught).
  2. Guard the outbound mail — even for submissions that look borderline. Hold the outbound email for review instead of relaying spam content automatically.

That second piece is the part nobody talks about. It is the difference between “we blocked the spam” and “we did not damage the domain”.

🛡

QWeb Spam Shield has Mail Guard for exactly this

Spam Shield filters the submission with AI scoring and holds suspicious outbound mail before it leaves your server. The reputation hit you would have taken from relayed spam never happens. It is the bit other plugins miss.

Learn how Mail Guard works →

If you do nothing else this month

Sign up for Google Postmaster Tools and add your domain. It takes ten minutes. Once your domain has 24 hours of data, you will see a clear graph of how Gmail rates your sending reputation. If the trend is down, this article is for you. If it is flat and high, you are probably fine — but watch it, because the trend tends to drift slowly over a year until the day it does not.

Deliverability is one of those problems that looks like a mail-server problem and is almost always a content problem. The content is what is leaving your forms.

SEO

Schema Markup for WordPress: What Google Actually Reads in 2026

Schema.org has roughly 800 types. Google reads about 30 of them with any real effect on search results, and even that list shrinks every couple of years. Adding more schema does not earn more rich results. Adding the right schema for the right post type does. Here is what still earns rich results on WordPress in 2026, by post type.

Blog posts

Use Article (or BlogPosting as a subtype). Required for rich results: headline, datePublished, image, author. Google quietly stopped showing author photos in most results but still uses the field. Skip WebPage, CreativeWork wrappers — they add nothing.

WooCommerce products

Product with offer pricing, availability, and aggregateRating. Earns the product carousel and price-in-snippet. Skip the elaborate brand sub-schema unless your brand has a Wikidata entry — Google ignores it otherwise. Your Yoast or RankMath integration handles most of this automatically; verify it actually outputs by viewing the rendered JSON-LD.

Local business pages

LocalBusiness with full address, geo, openingHoursSpecification. Required for the local-pack inclusion alongside your Google Business Profile. Worth doing once, never again unless you move.

FAQ blocks

FAQPage still earns FAQ rich results, but Google narrowed eligibility in 2024. Only authoritative how-to or definitional content earns the slot now. Stuffing fake “FAQs” on a sales page no longer works and may actively hurt CTR by triggering nothing.

How-to content

Google quietly retired HowTo rich results for most queries. Keep the schema for accessibility and for the small percentage of queries where it still triggers, but do not expect the rendered “step-by-step” treatment anymore.

Reviews and ratings

Review and AggregateRating still earn stars in results for product, recipe, and business types. Strict rules: ratings must be on first-party content (your own page about your own product), not aggregated from elsewhere.

What to skip

  • Speakable for voice search. Never lived up to the promise.
  • Sitelinks searchbox for sites under ~5,000 indexed pages. Google rarely renders it.
  • Deeply nested schema (Article > author > Person > worksFor > Organization > ...). Past three levels Google appears to ignore the depth.

How to validate

Google’s Rich Results Test tool tells you what your specific page is eligible for. Do not trust the WordPress plugin output blindly — paste your URL into the tool, see which results actually trigger, prune the rest.

The win is not “more schema”. It is “less, correct schema”. A 2026 SEO win on WordPress almost never comes from adding markup. It comes from removing the markup that triggers nothing.

Why Your WordPress Contact Form Is a Spam Magnet (And How to Actually Stop It)

If you run a WordPress site for any length of time, you already know the rhythm. Quiet for a week, then forty submissions overnight that all say “Hello dear, I checked your site and would like to offer…”. The contact form is the single most-abused public endpoint on a typical WordPress site, and every form plugin — Contact Form 7, WPForms, Gravity Forms, Fluent Forms, Elementor Forms — leaks by default. Here is why, and what actually stops it.

Why your form is a magnet, not a target

Spam bots do not pick on you because of who you are. They pick on you because contact-form HTML is recognisable: a couple of <input> fields, a textarea, a submit button, and a predictable JSON or POST endpoint. Scanners crawl the open web, fingerprint the form plugin from its CSS classes, and queue your URL into a list of working targets. The form does not need to be on a busy site to attract submissions — it just needs to be discoverable.

Why the usual fixes stop working

The three traditional defences are honeypots, reputation lists, and CAPTCHAs. Each has a hole that modern spam exploits:

  • Honeypots catch lazy bots. They miss anything that runs a headless browser.
  • Reputation lists (Akismet-style) score by IP and email patterns. They do not see the content of the message and miss realistic AI-written spam that uses a clean residential IP.
  • CAPTCHA shifts the cost onto your real visitors and drops conversions 5 to 15 percent on most contact forms. Worse, modern solvers handle reCAPTCHA v2 in under a second for a fraction of a cent.

What actually works in 2026

The pattern that has held up across forty-plus client sites is layered, invisible-to-the-user defence:

  1. Honeypot + timing trap in the form itself. Catches everything dumb. Costs nothing.
  2. Server-side intent scoring on the submission. This is the bit that catches AI-written submissions: a model reads the message and decides whether it looks like a real enquiry or a sales pitch with a link.
  3. Outbound mail guard. Even if a spam submission gets through, hold the email before it relays through your domain — that is what protects your sender reputation.
🛡

This is exactly what QWeb Spam Shield does

QWeb Spam Shield is the plugin we install on every WordPress site we touch. It reads each form submission with Google Gemini, layers in honeypot and timing checks for the dumb traffic, and includes a Mail Guard for the relayed-through-your-domain case. No CAPTCHA, no puzzles for real visitors, sensible defaults out of the box, two-minute setup.

Start 7-day free trial →

What to do this afternoon

If you do not want to install another plugin, you can get most of the way with two changes: add a hidden honeypot field to your form (a CSS-hidden <input> that humans never fill), and add a timestamp comparison that rejects submissions completed in under three seconds. That removes about 60 percent of incoming spam in our measurements. The remaining 40 percent — the realistic, AI-written stuff — is the bit you need intent scoring for, and that is what QWeb Spam Shield was built for.

The takeaway is not “another tool to install”. It is that the model of who is sending the spam changed, and the defence has to change with it. CAPTCHA-and-honeypot was the right answer in 2018. In 2026 it is reading the message that matters.

How I Cut WP-Admin Load Time by 60 Percent (Without Disabling Plugins)

If wp-admin feels slow, the instinct is to disable plugins one by one until it speeds up. That is a long afternoon and usually answers the wrong question. The actual reason wp-admin is slow is rarely the plugin itself — it is what the plugin does on admin hooks. Here is the audit that cut admin load time 60 percent on the site I tested, with all plugins still active.

The diagnosis

WordPress fires admin_init, admin_menu, and admin_enqueue_scripts on every admin page load. Anything a plugin hangs off those hooks runs every time you load any admin screen — even if you are on a screen that has nothing to do with that plugin. A plugin that makes an outbound HTTP request on admin_init to check for updates is adding network latency to every page in your dashboard.

How to profile in 10 minutes

Install Query Monitor temporarily. Load wp-admin/index.php. Switch to the “Hooks & Actions” panel. Sort by time spent. The top three offenders are almost always:

  • An update-check that fires an external HTTP call on every admin request
  • A backup or security plugin pre-loading its dashboard data
  • A page-builder plugin loading its asset manifest globally instead of on its own screens

The fixes that worked

  1. Throttle plugin update checks. The standard pattern is once per 12 hours, but many plugins re-implement their own check on every admin load. A small mu-plugin that short-circuits pre_http_request for the update endpoints, except every six hours, is the single biggest win.
  2. Defer dashboard widgets. Plugins that add their own widget to the WP dashboard often query their entire data set on load. Disable the widget if you do not use it; many plugins respect a filter to skip widget registration.
  3. Constrain asset enqueueing. The page-builder case can be fixed with a current_screen check that dequeues the plugin’s heavy assets on screens it does not own.

Measurable result

On the test site (38 active plugins, shared host), wp-admin dashboard TTFB went from 1.8s to 0.7s. Plugin list page went from 2.4s to 0.9s. Editor screens were unchanged because the plugins involved already only loaded on their own screens — that is the discipline you want to find.

What to do this afternoon

Add Query Monitor. Load wp-admin/index.php three times. Note the top three hooks by time. Pick the one that surprises you most and fix that one. If you do nothing else for a week, that single change usually accounts for half of the perceived “wp-admin is slow”.

You almost never need to disable plugins. You need to find out which ones are not respecting the admin context.

Managing 50+ WordPress Sites Without Losing Your Mind

Managing five WordPress sites is a side project. Managing fifty is a job that breaks ad-hoc tooling. The gap is real and it happens around the fifteen-site mark. Here is the small stack that lets a two-person team run a 50-plus-site portfolio without firefighting.

The three jobs that have to be automated

Three categories of work scale linearly with site count and are the first things to break:

  1. Updates. WordPress core, plugins, themes. Five sites: do it by hand. Fifty: you need rolling automated updates with rollback on failure.
  2. Backups. Off-site, automated, with quick-restore tested at least quarterly.
  3. Health monitoring. Not just “is the site up”, but “are there new PHP errors, slow queries, missing assets, broken cron jobs”.

The stack we run

  • WP-CLI driven from a small bastion server. Every site checked in to git. Updates triggered by a cron that runs wp core update; wp plugin update --all, then wp eval-file a smoke-test script and rolls back on non-200.
  • UpdraftPlus or BackupBuddy to S3 with weekly full + daily incremental. Restore tested monthly on a staging environment.
  • Uptime monitor (UptimeRobot, Better Uptime, anything reliable) on the home page and one deep page per site.
  • Log shipping from wp-content/debug.log to a central syslog or a service like Papertrail. Catches the silent fatal errors that uptime monitoring misses.
  • Weekly dashboard email aggregating “what changed across all sites” — outdated plugins, security alerts, traffic deltas. The fastest 30 seconds of management we do all week.

The mindset shift

At small portfolio sizes you can react to problems. At fifty plus, you cannot — by the time you notice a broken plugin update on site 12, sites 18, 23 and 34 already have it too. The shift is from reactive (“client called, fix it”) to proactive (“dashboard amber, fix it before client notices”). That is a tooling shift, not a discipline shift.

What we stopped doing

  • Logging into wp-admin on individual sites to check anything. If we cannot answer it from the central dashboard, we add it to the dashboard.
  • Per-site Slack channels. One client portfolio channel with @mentions for blocking issues.
  • Manual plugin updates. Even when “I just want to update Yoast on this one”. The exception becomes the rule.

The fifty-site mark is the size where small process investments pay back inside a quarter. Make them.

PHP 8.3 Migration for WordPress: A Checklist That Worked on 47 Sites

PHP 8.3 is the deadline most WordPress hosts moved to in 2025. Our migration of 47 client sites broke things on six of them — about one in eight. Here is the checklist that handled the other 41 cleanly, and the patterns of the six that did not.

The pre-migration checklist

  1. WordPress core on the latest minor. 6.5+ is fine with 8.3. Older cores have unresolved deprecation warnings that are now fatal.
  2. All active plugins on their latest version. Plugin compatibility regressions are 80 percent of the breakage we saw.
  3. Theme PHP files audited for deprecated functions. create_function(), the old each() pattern, dynamic class properties without declaration.
  4. Test a staging clone first. Always. We never migrated production without a staging pass.

What actually broke

Across the six sites that hit issues, the patterns were:

  • Dynamic properties on undeclared classes. 8.2 deprecated this; 8.3 makes it a fatal in some cases. Almost always in an abandoned plugin we then replaced.
  • Calling strtolower() on null. Strict type changes mean what used to be a soft warning is now a fatal. Audit any code that handles user input or plugin metadata.
  • Removed utf8_encode and utf8_decode. These were deprecated then removed. Old import plugins were the worst offenders.
  • SOAP and ext-imap quirks. Two sites used ancient SOAP integrations that needed a workaround.

The migration step-by-step

  1. Clone production to staging.
  2. Switch staging to PHP 8.3 in your host’s control panel or via WP-CLI’s host-specific command.
  3. Visit every public template and the entire admin. Note any white screen or error.
  4. Tail wp-content/debug.log for one hour of normal use. Almost everything that will fail in production fails here first.
  5. Run WooCommerce checkout end-to-end on test cards if it is a store.
  6. Cut over production. Keep the 8.2 environment available for 48 hours as a rollback.

The single most useful thing

Enable WP_DEBUG_LOG with display_errors = Off on staging for the migration window. Every deprecation that will bite you logs there first. It is the cheapest insurance you can buy.

8.3 is faster and the migration is usually clean. The pattern across the sites that broke was always “old plugin nobody touches” — the migration was a useful prompt to retire those.

WordPress 6.8 Performance Wins: What Actually Made My Sites Faster

WordPress 6.8 shipped a handful of real performance improvements that mostly happened under the hood. Not all of them moved the needle on production sites. Here is what actually did, measured across 47 sites we manage, with the boring honest result for each.

The wins that showed up in the data

  • Improved wp_query caching for archive pages. Across our sample, archive TTFB dropped 12 to 28 percent without any code changes. Bigger wins on sites with deep taxonomy queries.
  • Reduced autoloaded options on fresh installs. Saves a few hundred KB of wp_options reads per request. Modest, but cumulative on shared hosting.
  • Faster admin asset enqueueing. Editor screens loaded 200-400ms faster on average. Real if you spend time in wp-admin.

The wins that did not

  • “Improved emoji handling.” Saved 20ms on requests that did not have emoji. Did nothing measurable on requests that did.
  • Block editor “fast preview” mode. Editor felt faster subjectively. Lighthouse scores on the public site unchanged.

What we changed in response

Three concrete actions after rolling out 6.8 across the fleet:

  1. Stopped manually caching archive queries in two custom plugins. The core cache now does it correctly. Removed about 400 lines of code.
  2. Audited autoloaded options on every site. The 6.8 changes made the threshold for “this matters” lower, not higher. We trimmed 60 to 200 autoloaded entries per site on average.
  3. Re-baselined Core Web Vitals. LCP and INP both improved slightly on most sites with no other changes. Worth refreshing your baseline so you do not chase ghosts in subsequent optimisation work.

What to do this week

Update if you have not. Then run wp transient delete --all and wp cache flush to make sure the new cache logic is starting from a clean slate. Re-measure Core Web Vitals 48 hours after. Most sites get a free 5-15 percent on LCP from the archive-cache improvements alone.

6.8 is a quiet release, not a flashy one. That is usually the kind that pays off in maintenance budget over the next year.

The 7 WordPress Plugins We Refuse to Install (And What We Use Instead)

Twenty years of WordPress and roughly a thousand sites in. There are plugins we will not install. Each has a clean replacement we use instead. This list is not “these are bad plugins” — it is “this is the tradeoff we made and why”.

1. Heavy “all-in-one SEO” plugins on small sites

If the site does not need every SEO feature, Yoast or RankMath is overhead. What we use: SEOPress Free or Slim SEO for sites where 90 percent of the SEO settings will never be touched.

2. Slider Revolution

Powerful, heavy, security history. What we use: a Swiper.js implementation in 200 lines of theme code, or Elementor’s slider if Elementor is already loaded.

3. “Cleanup” plugins that delete revisions and transients

These run on a cron and frequently delete things you wanted. What we use: a small mu-plugin that prunes old revisions on a schedule with a configurable retention window.

4. WP Statistics / WP Slimstat

Self-hosted analytics that bloats the database fast. What we use: Plausible or Fathom on a separate domain.

5. Generic anti-spam plugins on production sites

Akismet alone misses realistic AI-written spam; honeypot-only plugins miss the headless cases; CAPTCHA-only plugins hurt conversions. What we use: QWeb Spam Shield. Reads the submission with AI, layers honeypot + timing underneath, and includes a Mail Guard for outbound reputation protection. It is the one we install on every production site we touch.

6. Page-builder add-on bundles

“Ultimate addons for X” packages that ship 40 widgets we use 3 of. What we use: the specific widget plugin from the original author, or a custom block.

7. WP File Manager / “FTP in admin” plugins

Convenience that is also an attack surface. What we use: SSH for staff, SFTP for clients who need it. Never an admin-side file manager.

The pattern

Every plugin on this list shares a property: it does more than it needs to, and the “more” is what causes the problems. Smaller, more focused plugins age better. We never regret installing fewer plugins, and we usually regret the all-in-one bundle.

None of these are bad plugins. They are just not the right fit for the way we work. Your list will be different. The exercise of writing yours is worth an afternoon.

Image Optimization on WordPress: The WebP and AVIF Workflow That Stuck

Three years of WebP, two of AVIF, dozens of tested plugins. The image-optimisation workflow that stuck across our entire client portfolio is short, boring, and effective. Here is what it is, and what stopped working that we used to recommend.

The workflow

  1. Resize at upload to a maximum of 2400px on the longest edge before any further processing. Most uploads are 5000+ from phones and the extra resolution does nothing.
  2. Compress to WebP automatically with a server-side conversion. Quality 80 is the sweet spot. Below 70 is visibly worse on photos. Above 85 is wasted bytes.
  3. Generate AVIF for the hero image only. AVIF compresses better than WebP but encoding is expensive. Doing it for every image bloats your media library; doing it for the LCP image specifically pays off.
  4. Serve responsive variants via WordPress’s built-in srcset. Do not override it with a CDN URL trick that loses the responsive behaviour.
  5. Lazy-load below-the-fold images with loading="lazy". Skip the above-the-fold image — lazy-loading the LCP hero hurts your Core Web Vitals.

What stopped working

  • JPEGtran-style optimisers as a standalone strategy. They squeeze 10-15 percent out of a JPEG; switching to WebP saves 30-40 percent. The compression is no longer where the win is.
  • CDN-based automatic conversion with no local fallback. Worked great until the CDN had an incident and every image on every site went 404 for 90 minutes. We now keep the original on origin.
  • Aggressive lazy-loading with native attribute alone. Browser support is great, but native loading="lazy" can fire late and cause LCP regressions on slow connections. We now use it everywhere except the hero.

The stack

We use ShortPixel as the conversion service (any of the modern image-conversion plugins work equivalently — ShortPixel, Imagify, EWWW). Configuration: WebP for all, AVIF for the hero only, originals preserved. CDN at the edge with WebP/AVIF content negotiation. WordPress core handles the srcset.

The measurement

Median image weight on the homepage of a typical client site, before and after:

  • JPEG, no compression: 1,840 KB
  • JPEG, optimised: 1,520 KB
  • WebP @ 80: 740 KB
  • AVIF @ 80 (hero only): 510 KB

The format change is roughly 2x the savings of compression alone. Combine both and Core Web Vitals usually go from “Needs Improvement” to “Good” on the image score, no other changes.

Boring, working, stable. That is what we wanted three years ago and that is what we have now.

WordPress Multisite vs Separate Installs: When Each Wins

WordPress Multisite is one of those features people either love or never touch. It saves time on the right job and creates pain on the wrong one. After running both models for client portfolios, here is when each actually wins.

When multisite wins

  • Many similar sites with shared management. A university with 80 department sites. A franchise with 200 location pages. A media group with 12 brand sites that all share the same plugin stack. Updating once and propagating is the whole point.
  • Centralised user management. One user account with different roles on different sites is genuinely useful when content teams overlap.
  • Shared media library when the assets logically belong to one organisation.
  • Tightly controlled plugin set. The super-admin enforces what is allowed; site admins cannot install rogue plugins. This is a feature for IT teams.

When separate installs win

  • Sites with different stacks. One needs WooCommerce, another needs LearnDash, another is a simple brochure. Multisite forces you to share more than you want.
  • Sites that need different update cadences. Some clients want every WordPress update yesterday; others wait six months. Multisite’s “update everything together” is hostile to that.
  • Sites with very different traffic profiles. One viral site can bring down the entire multisite network. Separation isolates the blast radius.
  • Sites you might sell or hand off. Migrating one site out of a multisite is painful. Separate installs are portable.

The honest gotchas of multisite

  • Plugin compatibility is worse. Some plugins simply do not work on multisite. The list shrinks every year but still exists.
  • Hosting choice narrows. Many cheaper managed hosts do not support multisite or charge a premium for it.
  • Backups are all-or-nothing for the network. Per-site restore is rarely as clean as you want.
  • Subdomain vs subdirectory is a one-way decision. Pick wrong and migration is a project.

The honest gotcha of separate installs

Tooling burden. Running 30 separate WordPress installs requires the kind of automation we covered in our 50-sites article. Without it, the “freedom” of separate installs becomes “30 things to remember to update”.

The shorthand

If the sites should be managed together because they share an organisation, a stack, and a release cycle: multisite. If the sites only happen to be managed by the same agency but are otherwise independent: separate installs. The wrong choice is fixable but expensive — make it deliberately on the first build.