We Cleaned a 880,000 URL WordPress Hack: The 410 Recovery Playbook
Quick answer
When a WordPress site is hacked with injected spam URLs, serve 410 Gone on every hacked URL rather than redirecting them to your homepage. A 301 to the homepage passes the spam signal into your most important page and keeps the junk indexed for months. Clean the dropper first, remove every rogue admin account, rotate all credentials and salts, then publish a temporary sitemap containing only the hacked URLs so Google recrawls them quickly and drops them.
What actually happened
In August 2025 a network of WordPress sites under one roof got hit at the same time. Not one site. The whole estate, sharing a server. Our founder was head of web development on that estate and sat in the recovery room for it, alongside the internal SEO team and an external technical SEO consultant brought in specifically for the Google side of the damage.
The numbers are the part people do not believe. The main property had grown to roughly 880,000 indexed URLs. Almost half of that was not real content. It was injected spam, published under query string URLs, ranked quietly, and then monetised. When the cleanup finished, the index count settled back to around 469,000. That gap is the size of the hack.
Symptom one: URLs you never created, ranking for things you never sold
The injected pages were served under plain query string URLs, in the shape of a post ID parameter appended to the domain. No pretty permalink, no menu entry, no post in the dashboard. From inside WordPress the site looked normal. From Google's side there were hundreds of thousands of pages about products and services the business had never touched.
This is the standard shape of a parasite injection. The attacker does not want to break your site. A broken site gets fixed on day one. They want your domain authority quietly rented out for as long as possible, so they hide the payload behind conditional serving: real visitors get the real page, crawlers get the spam.
- Search Console shows a sudden climb in indexed pages with no publishing activity behind it.
- Impressions rise for queries that have nothing to do with your business.
- Your real pages start slipping, because crawl budget is being spent on junk.
- A site: search on your own domain returns titles you have never written.
Symptom two: the admin account that would not die
There was a rogue administrator account on the estate. We deleted it. It came back. We deleted it again, rotated every administrator password, and it came back again. That loop is the single most useful diagnostic signal in a WordPress compromise, because it tells you something conclusive: you are removing symptoms, not the cause. Code is still executing on every request and recreating the account.
Where that code sits is predictable. The persistence layer on this estate included a file dropped into the must use plugins folder, disguised with a security sounding filename so a tired engineer scrolling a file list would read it as legitimate hardening. Files in that folder load automatically, before regular plugins, and they do not appear in the normal plugin screen where you would deactivate them.
The rest of the payload followed the usual pattern: a modified blog header file, a tampered core class file in the admin area, and a scattering of randomly named PHP files with heavily obfuscated contents. Every one of them rebuilt its function calls at runtime from character indexes of junk strings, which is how they slip past signature scanners looking for the literal words eval or base64_decode.
The decision that saved the recovery: 410, not 301
The instinct of every developer handed a list of dead URLs is to redirect them to the homepage. It feels tidy. On a hacked site it is the worst available option, and it was the first thing the consultant flagged when he reviewed our work.
A 301 says the content permanently moved to the new location. Point a hundred thousand spam URLs at your homepage and you have told Google, in the clearest language the protocol has, that your homepage is the new home of a hundred thousand spam pages. You have merged the attacker's payload into your most valuable asset.
| Status code | What Google reads it as | Use on a hacked URL? |
|---|---|---|
| 301 to homepage | The spam page permanently became your homepage | Never |
| 302 | Temporarily moved, keep the old URL indexed | Never. Attackers use this deliberately |
| 404 | Not found, may be a mistake, recheck later | Weak. Slow to drop |
| 410 | Gone on purpose, remove it | Yes. This is the correct answer |
Status code behaviour on injected URLs, as applied during the recovery.
There is a second layer to this. Part of the injected estate had been deliberately switched to a 302 by the attacker. That is an old affiliate trick, not an accident. A temporary redirect keeps the original URL in the index for far longer than a removal would, so the ranked spam URL keeps earning while the site owner believes it has been dealt with. If you find 302s on hacked URLs, treat them as evidence of an active operator rather than leftover debris.
Forcing the recrawl without waiting six months
Serving 410 is only half of it. Google has to come back and see the 410 before anything changes, and it has no reason to prioritise recrawling a hundred thousand low value URLs it discovered months ago.
The move that broke the deadlock was counterintuitive: build a temporary sitemap containing only the hacked URLs, submit it in Search Console, and leave it live for a few weeks. You are explicitly asking Google to go and look at the pages you want removed. Each crawl hits a 410, each 410 drops a URL, and the index count falls in visible steps rather than drifting.
- Export the full list of injected URLs from Search Console and your server logs, not just the ones you can see.
- Confirm every one of them returns 410 before you publish anything.
- Publish a separate sitemap file containing only those URLs.
- Submit it in Search Console as its own sitemap so you can watch it drain.
- Remove the sitemap once the index count stabilises. It is a tool, not a permanent fixture.
The recovery order we would repeat
Sequence is most of the skill here. On the estate we cleaned the same sites more than once because parts of the work happened out of order, on a shared server where a clean site could be reinfected by a neighbour that had not been touched yet.
- Contain at the server, not the site. On shared infrastructure, one dirty neighbour reinfects everything you just cleaned. Isolate before you clean.
- Find and remove the persistence layer. Must use plugins folder, modified core files, unfamiliar PHP in the web root. Nothing else counts until this is done.
- Delete every rogue account and rotate all administrator credentials, database passwords, hosting logins, and the authentication salts. Rotating passwords without rotating salts leaves active sessions alive.
- Verify with a clean scan and a 48 hour watch window. If the rogue account respawns, go back to step two.
- Apply 410 to every injected URL. Audit for attacker placed 302s while you do it.
- Publish the temporary sitemap of hacked URLs and monitor the index count weekly.
- Patch what let them in. Cross reference every installed plugin against a public vulnerability database and remove anything abandoned.
"If the rogue admin comes back after you delete it, you have not finished step two. Everything after that is wasted effort until you do."
What we changed permanently afterwards
Post incident, the monitoring model on that estate flipped from reactive to proactive, and those habits are now baked into how we run every site under a care plan.
- Index count is monitored as a security metric, not a marketing one. An unexplained jump in indexed pages is treated as a possible compromise.
- The must use plugins folder is fingerprinted, so any new file there raises an alert the same day.
- Backups are retention checked. A single day of retention, which is what that estate had at one point, is useless against a hack you discover on day three.
- Every administrator account is inventoried monthly. Accounts nobody can name get removed.
- Plugin inventory is cross referenced against public vulnerability data rather than trusting the update badge.
Common questions
Should hacked URLs be 301 redirected to the homepage?+
No. A 301 tells Google the spam page permanently became your homepage, which merges the attacker's content into your most important URL. Serve 410 Gone on every hacked URL instead.
What is the difference between 404 and 410 for hacked pages?+
A 404 means not found, which Google treats as possibly temporary and recrawls for a long time. A 410 means gone on purpose. On injected URLs 410 removes them from the index considerably faster.
Why does the rogue admin account keep coming back after I delete it?+
Because code is still executing on the site and recreating it. The account is a symptom. Look for a dropper in wp-content/mu-plugins, in modified core files, or in randomly named PHP files in the web root.
Why do attackers use a 302 redirect on hacked URLs?+
Because a temporary redirect keeps the original URL indexed far longer than a removal. It is a deliberate affiliate era tactic that lets a ranked spam URL keep earning after the site owner believes it was cleaned.
Is it safe to submit a sitemap of hacked URLs to Google?+
Yes, once every URL in it returns 410 and the site is confirmed clean. You are asking Google to recrawl the pages you want dropped. Remove the sitemap once the index count stabilises.
How long does index recovery take after a WordPress hack?+
With 410s in place and a targeted recrawl sitemap, large volumes of injected URLs typically drain over several weeks rather than months. Without either, the same URLs can linger in the index for the better part of a year.
Can one hacked site infect the others on my hosting account?+
Yes. On shared hosting a compromise in one account or one site folder commonly spreads across neighbours. Clean the whole estate in one window, or isolate first, otherwise you will clean the same site twice.
Do I need to rotate WordPress salts as well as passwords?+
Yes. Passwords stop new logins. Salts invalidate existing sessions. If you only rotate passwords, an attacker with a live session stays logged in.
Want help with this?
The pages below go deeper, by service and by city.
Want this handled for you?
Send the site and the symptom. A senior WordPress engineer opens it, tells you what is actually broken, and replies within one business hour. No card, no sales call, no obligation.
