Why Contact Form 7 Spam Filters Don't Stop Sales Pitches (And What Actually Works)
reCAPTCHA, Akismet, and keyword blocklists are designed to catch automated spam. Hand-typed sales pitches walk past them. Here's why, and how LLM-based classification fixes both your inbox and your GA4 conversion data.
If you run a WordPress site with Contact Form 7, you've probably watched the same pattern unfold. The form works fine, you wire it up to email or Slack, and for the first few weeks the only submissions are real prospects. Then, slowly, the inbound shifts. SEO agencies, recruiters, "business development" pitches, English-language cold emails — and by month three, more than half of every form submission is some flavor of sales outreach.
The standard advice (turn on reCAPTCHA, install Akismet, build a keyword blocklist) does almost nothing about this. Below is why, and what actually works.
Sales pitches aren't "spam" in the technical sense
The defenses you've been installing — reCAPTCHA, Akismet, OpenCage / honeypot fields — exist to stop automated submissions. Bots filling forms en masse, mass-distributed link spam, scraped email harvesting. They're great at that job.
The cold sales emails arriving in your CF7 inbox are different in two important ways:
- A human typed them. Not a script. A sales rep is grinding through a list of company sites and pasting their boilerplate into each contact form.
- The text looks legitimate. "I came across your site recently and noticed an opportunity to…" — there are no broken character sets, no obvious red flags, no Cyrillic, no 30-link payload.
That means spam-detection systems, which are calibrated for bots and obvious garbage, simply do not engage. They are solving a different problem than the one you have. It's not that they're tuned wrong; the category mismatch is structural.
The usual workarounds, and why each one breaks
reCAPTCHA / hCaptcha
Effective against bots. Completely useless against humans pasting boilerplate. If you're on v3 and "still getting spam," you're not getting bot spam — you're getting human outreach that reCAPTCHA was never going to stop.
Akismet
Akismet's training data leans heavily on URL-laden distribution spam in English. A polite, well-formatted SEO pitch from a small agency in your country doesn't match any of those signatures, so Akismet waves it through. Worse: if you're outside the US/UK, Akismet has very little local pattern data to draw on at all.
NG-word / keyword blocklists
Tempting because it feels surgical. In practice, three failure modes:
- False positives on real inquiries. "We'd like to inquire about your SEO consulting services" → blocked because "SEO" is on the list. You just dropped a paying customer.
- The wording mutates. Salespeople A/B test their own boilerplate. Whatever phrase you blocklist today will be paraphrased next quarter.
- No diagnostic trail. A submission silently disappears and neither party knows why.
"Require a business email" / block Gmail addresses
This catches some cold senders, but it also catches solo founders, sole proprietors, and anyone reaching out from a personal account. If your audience includes small businesses or freelancers, this is a net loss. If you're strictly enterprise B2B, marginal upside.
The reason a human can spot these in five seconds
Here's the key insight. When you open one of these emails, you don't decide it's a pitch because of a specific word. You decide because of context:
- "I came across your site recently" — but no link, no specifics about what they saw
- A self-introduction URL embedded in the message body
- "Dear hiring manager," when your form isn't a careers page
- Vague benefit language without any explicit ask tied to your product
This is pattern recognition over the whole message, not a string match. It's exactly the thing a string-matching filter cannot do.
Why LLM-based classification finally fits the problem
Large language models — the engines behind ChatGPT, Claude, and Gemini — were built specifically to read a passage the way a human would. Hand a submission to a model with a prompt like "Classify this contact-form submission as a real inquiry or a sales pitch, and explain why," and you get:
category: sales_solicitation
confidence: 0.93
reason: Generic SEO improvement pitch. No reference to the
recipient's actual product or industry. Author's URL
embedded for self-promotion.
Now you have something you can actually act on. Programmatically:
- High-confidence "sales" → route to a sales-archive folder, skip Slack notification
- "Inquiry" → fire your normal pipeline (Slack ping, CRM contact, GA4 conversion event)
- Low-confidence (the model itself flagged ambiguity) → human review
The human-eyeball burden drops from "look at every submission" to "look at the handful the AI hesitated on."
The hidden second win: clean GA4 data
This is the part most teams miss. If you have a GA4 conversion event tied to contact_form_completed, every cold sales email is counted as a conversion. The number GA4 hands you is not a measure of inbound demand — it's a measure of inbound demand plus outreach noise.
That noise compounds. Marketing teams report conversion-rate lift to leadership while in reality the only thing that moved was the cold-outreach volume. Ad spend gets allocated against bad numbers. A/B test winners get called against bad numbers. The entire downstream analytics layer drifts.
Once you have a real-time classification on every submission, you can change the GA4 event to fire only when the classifier confirms a genuine inquiry. Suddenly the conversion rate is a real conversion rate again. We've seen this single change recover 30–60% of "missing" attribution clarity in B2B sites.
A pragmatic priority order
Putting it all together, here's a stack that actually works:
- Keep reCAPTCHA and Akismet on. They handle the bot tier well — leave that problem solved.
- Don't use keyword blocklists to fight sales. Reserve them for truly toxic content (adult, phishing). Maintaining them against pitches is a treadmill that hurts your real inquiries.
- Add an LLM classifier in front of your notifications. This is the layer "spam filters" never were. Route, archive, and notify based on its judgment.
- Rewire GA4 conversions. Have the conversion event fire only on confirmed-genuine submissions. Your dashboards will start telling the truth.
YomuForm covers steps 3 and 4 as a single WordPress plugin. Steps 1 and 2 stay where they are — they just stop being asked to do a job they were never built for.
Wrap-up
- The "sales spam" problem on CF7 is not a spam problem. It's a category mismatch with the tools you've been using.
- A 5-second human read works because it weighs context. Bring an LLM into that exact spot and the classification becomes automatic.
- Cleaner inboxes are useful. Clean GA4 conversion data is transformative — it's the part that quietly fixes the rest of your marketing analytics.
Install the YomuForm Free tier on one site, watch the classification log for a week, and let your real submission volume sit in front of you for the first time in months. The path forward usually becomes obvious from there.