DEV Community

r-via
r-via

Posted on AI-assisted

We scanned 1,084 WordPress sites around a small French town. 1.13% were quietly serving spam to Google.

I run a small web agency in Vienne, a town of 30,000 people south of Lyon. Our clients are craftspeople, shops and small businesses, and a lot of them run WordPress sites that nobody has touched in years.

A hacked WordPress site often looks perfectly normal to its owner. So we wanted a number: how many local business sites are quietly compromised right now?

The dataset

  • 3,388 business domains pulled from OpenStreetMap, within 25 km of Vienne
  • 1,084 of them run WordPress, that is 41.8% of the sites that responded
  • 1,066 could be fully analysed

Everything was checked from the outside, the way any visitor or crawler sees the site. No logins, no plugins, no access to the servers.

Two checks, zero overlap

1. Cloaking: what does Googlebot see?

Most SEO spam injections are invisible to the owner. The hacked site serves normal pages to browsers and spam to search engines. So we fetched each page twice, once with a browser User-Agent and once as Googlebot, and compared the two.

The single best rule turned out to be the simplest one: does the <title> differ between the two responses? It caught Turkish, Indonesian, Spanish and Italian spam without any word list at all.

2. The sitemap: which pages did someone else add?

Injected pages often end up in the sitemap, because WordPress (or the SEO plugin) dutifully lists them. We tokenised every sitemap URL and looked for spam vocabulary.

The result: 12 compromised sites out of 1,066, or 1.13%. And the interesting part: the two methods have no overlap at all. Six sites were caught only by cloaking, six only by the sitemap. If you only run one of these checks, you miss half the infections.

Two caveats:

  • About 3.7% of sites block a fake Googlebot User-Agent at the firewall, so cloaking could not be tested on them. The real rate is probably a bit higher.
  • 1.13% is a snapshot, not a yearly rate. Sites get hacked, cleaned and hacked again; the number of sites affected over a year is certainly higher.

The false positives were the real work

Keyword matching on French small-business sites is a minefield. Every one of these was a real false alarm:

  • spé-CIALIS-te: "spécialiste" contains "cialis". Fix: strip accents first, then match whole tokens, never substrings.
  • Pornic and Pornichet are two seaside towns on the Atlantic coast.
  • Groupe Casino is a French supermarket chain.
  • Ford Escort is a car, and "tome XXX" is volume 30 of a book series.
  • "emporté par un torrent" means "swept away by a mountain stream".
  • Multilingual sites (WPML, Polylang) legitimately have /zh-hans/ paths full of Chinese characters.
  • Media attachments with Japanese file names are usually just stock photos.
  • Stylised stage names like GЯEG look exactly like homoglyph attacks.

So the lexicon is split in two tiers. Tier A words (think viagra, 1xbet, tramadol) have no legitimate use on a French business site, and one is enough. Tier B words (casino, escort, torrent, slot) never trigger on their own.

Signals that almost never lie

  • Cyrillic homoglyphs inside a Latin word, like torgеnt where the е is Cyrillic. Stage names use them as decoration; spammers use them to dodge filters. We only count them when something else corroborates.
  • Transliterated Russian in URLs: skachat, zerkalo, kazino, promokod... Two of them in the same URL is conclusive.
  • Spam in the default category (/uncategorized/, /non-classe/ in French): that is where injected posts land.
  • Backdated posts like /2000/01/01/, far older than anything the owner ever published.

Precision matters more than recall

The point of the exercise is to warn site owners, so a false alarm is worse than a miss: you would be telling a stranger their site is hacked.

The "automatic alert" tier reached 100% precision (10 out of 10). The "needs human review" tier was right only 2 times out of 6. So the rule is simple: never send an automatic alert on the second tier.

The other finding: old versions everywhere

Among sites that expose their WordPress version, 38% run a branch older than 7.0, and 14% older than 6.5. The tail goes all the way back to 3.5.1. Those sites are not hacked yet, but they are the pool the next infections will come from.

Check your own site

If you maintain WordPress sites for clients, two cheap habits catch most of this:

  1. Compare what Google sees with what you see: search site:yourdomain.com and use the URL Inspection tool in Search Console.
  2. Open your sitemap once in a while and scroll. Injected pages are usually obvious to a human.

We also turned these checks into a small free WordPress hack checker. Paste a URL and it returns the evidence links so you can verify each finding yourself. The interface is in French, but the evidence speaks for itself.

Happy to answer questions about the detection rules or the false positives in the comments.

Top comments (0)