Your site is clean but Google still lists thousands of spam URLs under your domain. Cleaning files does not remove them. This is the 7 step order we used on a network wide WordPress compromise where the index fell from roughly 880,000 URLs to roughly 469,000 once the injected URLs were drained, and the single status code decision that decides how long it takes.
Last updated ยท Reviewed by Ali Yasin Jatoi
Order matters more than tooling. Doing step 4 before step 1 means the attacker republishes faster than Google can drop what you removed.
Removing indexed spam while the dropper still runs is wasted work. Find and kill the loader first. On the estate we cleaned, the loader sat in wp-content/mu-plugins under a security sounding filename, so it never showed in the plugins screen and every dashboard looked clean. Check mu-plugins, wp-blog-header.php, and any recently modified core file before anything else.
Use the Search Console index coverage export and a site: sample, not your sitemap. Your sitemap lists what you meant to publish. The index holds what the attacker published. On the main property in our incident the gap was enormous: roughly 880,000 indexed URLs against a real page count a fraction of that size.
Most injections share a shape. Ours was query string based, the ?p= post ID form, which is invisible from inside WordPress because no post, page, or media item exists for it. Sort the export by pattern and confirm the exact parameter, path prefix, or language folder the attacker used. You cannot serve the right status code to a pattern you have not defined.
This is the step almost everyone gets wrong. A 301 to the homepage tells Google the content moved, so it keeps the URL alive, keeps recrawling it, and can pass the spam association onto the target. A 404 says the page might return. A 410 says the resource is permanently gone and is the fastest legitimate removal signal Google honours. We were serving 301s to the homepage and an external technical consultant flagged it as the critical mistake in the whole recovery.
Ranked hacked URLs in our incident had been deliberately flipped to 302. That is an old affiliate era trick: a temporary redirect keeps a URL indexed far longer than removal would, so the spam keeps earning after the cleanup starts. Crawl a sample of the hacked pattern and confirm what the server is actually returning, rather than trusting what your rules say it should return.
Counterintuitive, and it works. Submit a sitemap that lists nothing but the injected URLs. You are inviting Google to recrawl exactly the URLs now returning 410, which drains the index in visible steps instead of waiting months for organic recrawl. Remove that sitemap once the count settles, and never leave it referenced in robots.txt.
A rogue admin account in our incident kept coming back after every deletion. The account was never the problem, the dropper recreating it was. If the same username reappears, stop deleting users and go hunting the file. Then re pull the index count weekly. Ours settled at roughly 469,000, meaning about 411,000 of the indexed URLs had been spam.
| Response | What Google reads it as | Verdict |
|---|---|---|
| 410 Gone | Permanently removed, do not ask again | Correct answer for every injected URL |
| 404 Not Found | Missing, might come back | Acceptable but slower than 410 |
| 301 to homepage | This content moved here | Wrong. Keeps the URL alive and ties spam to your homepage |
| 302 anywhere | Temporary, keep the original indexed | Wrong, and often what the attacker set on purpose |
| 200 with a soft error page | This page exists and is fine | Worst case. The spam URL stays fully indexed |
The full incident writeup, including the malware filenames and the obfuscation style that defeated signature scanning, is in the network hack field notes.
410 Gone. A 301 tells Google the content moved to the redirect target, so the hacked URL stays in the crawl queue and the spam association can follow the redirect onto the page you sent it to. A 410 declares the resource permanently gone and is the fastest legitimate removal signal. Only use a 301 when a hacked URL replaced a genuine page that still exists at a new address.
With 410s served and a temporary sitemap of only the hacked URLs submitted, the index count usually starts dropping within days and drains in visible steps over 4 to 10 weeks depending on volume. On a very large injection, hundreds of thousands of URLs, expect a multi month tail. Without the temporary sitemap you are waiting on organic recrawl, which can take far longer.
Because injected URLs were ranking. Spam pages generate impressions and clicks that look like growth in Search Console, which is why hack driven reporting is so misleading. If a traffic report shows an unexplained hike, check the landing page report for URLs you never created before you celebrate.
Because something on the server is recreating it. Deleting the account treats the symptom. The cause is a dropper, typically in wp-content/mu-plugins, in a modified core file such as wp-blog-header.php, or in a scheduled cron job. Find and remove that file, then delete the account once and confirm it stays gone.
It is a temporary suppression, roughly six months, and it only hides the URL in Google Search. It does not remove anything from the index. Use it to stop visible damage while the real fix, the 410 plus recrawl, works through. Never use it as the fix itself.
Usually not. Google is good at ignoring spam links to hacked pages once those pages return 410. Disavow only when a manual action mentions unnatural links, or when a large paid network was clearly built against the injected URLs.
Neighbouring accounts on the same server. In our incident, sites we had already cleaned were reinfected by untouched sites sitting beside them. Isolate the account, or move it, before you clean it. Otherwise you are cleaning into an open pipe.
Flat $249 for a standard hacked WordPress cleanup, index drain included, with a 30 day reinfection guarantee. A senior engineer runs the whole job end to end.
Clean or your money back, plus 30 days of reinfection cover.