Working out what you have

Google is showing Japanese pages you never wrote

Your site looks fine in a browser and Google shows Japanese titles for URLs on your domain. That gap is the infection working as designed. Four separate things are keeping it alive and only two of them are files.

Checked against a live WordPress install on .

What Google is showing you

Most people meet this hack through a notification rather than through their own site. A Search Console message about new pages you did not publish, or a customer forwarding a screenshot of your brand sitting next to Japanese text and a counterfeit handbag.

Search site:example.com with your own domain and you see URLs you have never created, carrying Japanese titles and descriptions. Click one and you get your ordinary 404 page, or your homepage, or nothing unusual at all. Load your site normally and everything works.

Nothing was translated. Your pages are exactly as you left them. The attacker generated new URLs on your domain, filled them with machine-written Japanese, and monetised them with affiliate links to stores selling fake brand merchandise. That is Google's own description of what the campaign sells.

The two URL shapes

Google's documentation gives the older shape: pages in randomly named directories, like http://example.com/ltjmnjp/341.html. A directory name nobody would type, and a numbered HTML file under it.

The shape Sucuri documented in January 2025 is quieter and harder to find. The spam URLs were plain query strings on the domain root, example.com/?m=123456789, with the number changing per page. There is a reason that works. m is the first entry in WordPress core's list of public query variables, in wp-includes/class-wp.php, where it means a date archive. So every one of those URLs reaches WordPress, returns 200, and corresponds to no file anywhere on your server. Searching the disk for them finds nothing, because there is nothing to find.

What Search Console calls it

Google's Security Issues report has four hacked categories. This family normally lands as Hacked: URL injection, which Google defines as a hacker creating new pages on your site containing spammy words or links. Where the attacker also edited your existing pages, you get Hacked: Content injection alongside it.

A manual action or a security issue is a label on your whole property. It does not tell you which file did it, and clearing it is the last step of this page rather than the first.

Confirming the cloak

Before touching a file, prove that the site serves different content to Google than it serves to you. Four checks, cheapest first.

1. Ask Google what it has

Run site:example.com on your own domain and page through the results. Add a term to narrow it, such as a directory name you spotted or a word in the spam titles. Write down a handful of the spam URLs. You will need that list twice more before you are finished.

Search Console gives you the same thing in bulk. The Pages report and the Performance report both list URLs Google knows about, and both export. Take the export.

2. Inspect one spam URL as Google

This is the check that settles it. In Search Console, paste one of the spam URLs into the inspection box at the top, then choose Test live URL. When it finishes, open View tested page and read the HTML tab.

That HTML is what Google's crawler received from your server a moment ago. If it is full of Japanese text while your browser shows a 404 on the same URL, the cloaking is confirmed and you know the payload runs on request rather than sitting in a static file. The Screenshot tab shows the same page rendered.

Older guides tell you to use Fetch as Google. That tool was retired with the previous Search Console. The URL Inspection tool replaced it, and the crawler it sends is named Google-InspectionTool.

3. Request the page as a crawler yourself

From a shell, ask for the URL with a Googlebot user agent and compare it against an ordinary request.

bash
curl -s -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" "https://example.com/?m=123456789"
curl -s "https://example.com/?m=123456789"

Two different bodies means cloaking. Two identical bodies means very little, and this is where people stop too early. Plenty of these payloads check the source address as well as the user agent, and Google publishes the crawler ranges as a JSON file that anyone can fetch. A request from your laptop with a borrowed user agent fails that check and gets the clean page. Treat a clean answer here as inconclusive and go back to the live test in Search Console, which comes from a real Google address.

4. Read the property itself

Two screens in Search Console carry evidence that no file on your server can show you.

  • The Sitemaps report. It lists only sitemaps submitted through that report or through the API, which is Google's own wording. So an entry there that you did not submit was submitted by somebody with owner access to your property. Note the exact filename.
  • Settings, then Users and permissions. Look at the verified owners. An address you do not recognise, often a throwaway Gmail account, is part of the infection and not a coincidence.

The same cloaking mechanism drives the pharmacy variant of this campaign, and the confirmation steps there are worth reading alongside these if the injected text is English rather than Japanese: pharmacy spam in your pages and search results.

The four places it lives

This is the part that makes the infection survive an ordinary cleanup. People remove the PHP file, watch the spam keep appearing, and conclude the cleanup failed. It did not. Three other layers were still running.

Layer What it is What removes it
Files under wp-content The generator that builds a spam page per request, plus a backdoor that rewrites it after you delete it A file manager, FTP or SSH
Rows in wp_posts and wp_options Spam posts published under your own permalinks, and a loader row that runs on every page load The WordPress admin or the database
A rewrite rule in .htaccess Routes whole URL patterns into that one PHP file, and can manufacture a Search Console verification file on demand A text editor
A verified owner in Search Console An account inside Google that can submit sitemaps and change settings for your property Google's console. Nothing on your server reaches it

The files

The generator is a single PHP file, usually somewhere nothing should be executing PHP at all. Paths Sucuri has published from real cases include wp-content/uploads/cache/spam.php and news/index.php, with the working files beside them in wp-content/uploads/.spam/ and wp-content/cache/sitemap.xml. Google's documentation names index.php, wp-load.php, 404.php and view.php as the files most often injected rather than added.

One hiding place deserves its own paragraph. wp-content/mu-plugins/ holds must-use plugins, which load on every request, have no activation record, and cannot be deactivated from the admin. Sucuri documented a payload there as wp-content/mu-plugins/.tott.log. Read that filename again. A leading dot hides it from a directory listing and from most file managers, and a .log extension keeps it out of every search for *.php you are about to run.

The sitemap that is not yours

WordPress core does not write a sitemap file. It registers a rewrite rule for wp-sitemap.xml and builds the XML from the database on request. Yoast and Rank Math work the same way for their own sitemap URLs. So a physical file called sitemap.xml, sitemap_index.xml or something less obvious sitting in your web root did not come from WordPress and did not come from your SEO plugin.

Sucuri's January 2025 case is the clearest published example. A file named spamurl.txt sat in the site root, listing spam URLs in sitemap form, and it had been registered as a sitemap inside the site's own Search Console property. Google crawled over 3,000 spam URLs from it. The site's files had already been cleaned once. The spam kept indexing, because the list feeding Google was a plain text file nobody had thought to look at and a Search Console entry nobody had opened.

The rewrite rule that regrows a verification token

Google's own hacked-spam documentation publishes this marker, and it is worth understanding rather than just deleting.

apache
RewriteEngine On
RewriteRule ^google(.*)\.html$ dir/file.php?google=$1 [L]

Search Console's HTML file verification method works by you uploading a file named after a token Google gives you, and Google fetching it from your web root. That rule makes any request for google<anything>.html return a valid verification file generated on the fly. The attacker can therefore verify ownership of your property whenever they like, with no file for you to find. Google's test for whether you have removed it is direct: request example.com/google followed by a random string and .html, and a 404 means the rule is gone.

Read every .htaccess on the site, not only the one in the root, and treat any rule that routes a whole URL pattern into a single PHP file as hostile.

The parts that are not files at all

Injected posts sit in wp_posts as ordinary published content, which is why they get permalinks and appear in your own sitemap. The loader that runs the payload is often a row in wp_options, autoloaded on every request. Neither is visible to anything that walks your filesystem.

Alongside them there is usually an administrator account you did not create, which is how the attacker gets back in after you clean the files and which is often how they verified themselves in Search Console. Finding those, including the ones the user list does not show you, is a procedure of its own.

Taking it out by hand

Work through this in order. Every command in this section was run against WordPress 7.0.4 with WP-CLI 2.12.0, PHP 8.2, MySQL 8.0 and Apache on 18 August 2026, and the output is quoted as it appeared.

  1. Save the spam URL list first. Export it from Search Console before you change anything. Once the generator is gone the URLs stop resolving and you lose the ability to enumerate them, and you need the list to check your work later.
  2. List what changed recently. Run this from your WordPress root and widen the window until the results stop being interesting.
    bash
    find . -name "*.php" -newermt "-7 days" -printf "%T+ %p\n" | sort
    Modification times are forgeable and attackers who bother do forge them, so an odd timestamp is strong evidence and a normal one proves nothing.
  3. Look where PHP does not belong. Uploads should contain media. Must-use plugins should contain things you put there.
    bash
    find wp-content/uploads -name "*.php"
    ls -la wp-content/mu-plugins/
    wp plugin list --status=dropin --fields=name,title
    Use ls -la rather than a plain listing. That is what shows you a file whose name starts with a dot.
  4. Search for the obfuscation. Google's documentation lists the functions this family uses to hide its payload, and they hold up.
    bash
    grep -rl --include="*.php" "base64_decode" wp-content/
    grep -rlE --include="*.php" "eval\(|gzinflate|str_rot13|assert\(" wp-content/
    Expect legitimate hits. Minified libraries and licence checks in paid plugins use the same functions. If you are unsure whether a hit is real, three checks settle most flagged files.
  5. Search for the cloak itself. The payload has to recognise a crawler, so it names one.
    bash
    grep -rliE --include="*.php" "googlebot|bingbot|yandex" wp-content/
    This grep is noisy and you should know how noisy before you run it. On a clean install with nine plugins it returned nine files, every one of them legitimate: Akismet, a file manager, a bundled logging library and some coding-standard fixtures. Open each hit and read the code around the match. Deleting on the strength of a grep is how people remove Akismet.
  6. Read the injected code without running it. Decode a base64 block in a text editor or with a decoder that only prints. Do not execute the file to see what it does, and do not load it in a browser. Reading is enough to identify it, and running it is how a cleanup becomes a second incident.
  7. Clean every .htaccess.
    bash
    find . -name ".htaccess"
    Delete any rule routing a URL pattern into a single PHP file, and any rule matching ^google(.*)\.html$. Keep the block between # BEGIN WordPress and # END WordPress, which WordPress manages itself.
  8. Delete the rogue sitemap file. List your web root and remove any sitemap or plain text file of URLs that WordPress and your SEO plugin did not create. Note the exact filename before deleting it, because you need it in Search Console in a moment.
    bash
    find . -maxdepth 1 -type f \( -name "*.xml" -o -name "*.txt" -o -name "*.html" \)
  9. Put the modified files back. Injections into core, plugin and theme files are repaired by restoring the published copy of that exact version, which is its own procedure with three different sources. Restoring core does not remove files the attacker added, so the earlier steps are not optional.
  10. Find the injected posts. This is database work. Two queries find the same thing and the second avoids the regular-expression engine.
    sql
    SELECT ID, post_title, post_type, post_status, post_date
    FROM wp_posts
    WHERE post_title REGEXP "[^ -~]";
    
    SELECT ID, post_title, post_type, post_status, post_date
    FROM wp_posts
    WHERE LENGTH(post_title) <> CHAR_LENGTH(post_title);
    Both ask the same question: does this title contain a character outside plain ASCII. On MySQL 8.0, "日本語ページ" REGEXP "[^ -~]" returns 1 and "Hello world!" REGEXP "[^ -~]" returns 0. The second query works because a multibyte character counts as several bytes and one character, so LENGTH and CHAR_LENGTH disagree only when the title is not pure ASCII.
  11. Read the results before deleting anything. Both queries also match legitimate titles containing an accent, a curly quote or an emoji. Run them through WP-CLI, which handles the connection for you.
    bash
    wp db prefix
    wp db query "SELECT ID, post_title, post_type, post_status, post_date FROM wp_posts WHERE LENGTH(post_title) <> CHAR_LENGTH(post_title);"
    wp db prefix printed wp_ on the install used here, and yours may differ. Read $table_prefix in wp-config.php and substitute it into the queries. A query that matches nothing prints nothing at all and exits 0, so silence is a result.
  12. Delete the confirmed spam posts. The admin works fine for a handful. For hundreds, delete by ID and skip the trash.
    bash
    $ wp post delete 12 --force
    Success: Deleted post 12.
    Without --force the post moves to the trash, keeps its row and stays reachable by ID.
  13. Remove the administrator you did not create, and reassign their content rather than deleting it. Do that carefully, because deleting a user with content attached can take posts with it.

When the files and the rows are gone, load one of the spam URLs from your saved list. You should get your own 404 page from every request, whatever user agent you send.

Search Console and the index

Everything in this section happens inside Google's console. None of it is on your server, none of it is affected by anything you deleted, and skipping it is why cleaned sites keep indexing spam.

Remove the owner, and remove their tokens first

Open Settings, then Users and permissions. Next to the owner you do not recognise, open Verification details, which lists the tokens that account used to prove ownership. Delete every one of them, then remove the owner. The order matters: a token you leave in place is a route straight back to owner status.

A token can be any of the methods Google supports, so check all of them against your own site.

  • An HTML file in your web root, named after the token. Remember the .htaccess rule from earlier: the file may not exist on disk at all and still be served.
  • A <meta name="google-site-verification" content="..." /> tag in your homepage head. Read the page source and check every one you find against your own account.
  • A DNS TXT or CNAME record at your registrar, which survives anything you do to the site.
  • Google Analytics or Google Tag Manager access, which verifies ownership without touching your server at all.

Google's warning on this is worth quoting: "If you have been hacked, this is only a temporary solution. Even if you remove the token, a hacker can probably add it back." The next section is what stops that.

Remove the sitemap, and understand what that does not do

In the Sitemaps report, click the sitemap you did not submit, open the more options button, and choose Remove sitemap. Then read what Google says happens next: "Deleting a sitemap removes the sitemap from this report, but Google won't forget the sitemap or any URLs listed in it."

So the removal tidies the report and changes nothing about the index. The URLs leave the index when Google recrawls them and finds them gone, which is why deleting the generator and the posts had to come first. Once those URLs return 404 or 410, the index drains on its own over weeks.

Check your work the way Google will

Take three or four URLs from the list you saved and run Test live URL on each. The HTML that comes back is what Google's crawler gets right now. A 404 there means the URL is genuinely dead rather than dead only for you.

If you need the spam out of results today

The Removals tool hides a URL or a prefix from Google results quickly. Google is clear about the limit: a successful request lasts about six months, and it hides the result rather than removing the page. Use it to keep counterfeit ads off your brand name while the recrawl catches up, and do not treat it as the fix.

Request the review

Once the site is clean, open the Security Issues report and choose Request Review. Write what you found and what you did, per category. A line like "for the URL injection, I removed an injected PHP file under wp-content, deleted 340 spam posts, removed a rogue Search Console owner and updated the vulnerable plugin" is the kind of detail that gets reviewed rather than bounced.

Two things Google says about the review, and both catch people out. It takes several days to a few weeks. And fixing the issue on some pages earns no partial return to search results, so a half-cleaned site fails the review and puts you back in the queue. Do not resubmit while a review is pending.

Closing the way back in

This infection has two ways back. The hole that let the attacker in, and the Search Console ownership that lets them keep pointing Google at whatever they generate next. Close both or you do this again.

Find how they got in

The generator file is the payload. Something wrote it, and that something is still there unless you found it. An administrator account you did not create, an outdated plugin with a public exploit, a stolen password, or a hosting account shared with another compromised site are the usual answers. Working backwards from the timestamps to the entry point is the whole subject of a separate guide, and it is worth reading if this is the second time you have cleaned this site.

One documented route deserves naming, because it explains the Search Console owner without involving your Google password. CVE-2020-8934 in Site Kit by Google let any authenticated user, at any role, read a setup URL out of the admin page source and complete Google's owner verification with it. A subscriber account was enough to become an owner of the property. It was fixed in Site Kit 1.8.0, released on 7 May 2020, so the bug itself is long patched. It is here as the clearest published example of how a low-privilege WordPress account turns into control of your Search Console property.

Rotate what the attacker saw

  • Every administrator password, and force a logout so existing sessions die.
  • The salts in wp-config.php. Changing them invalidates every login cookie on the site at once.
  • The database user's password, updated in wp-config.php at the same time. Sucuri puts this in their standard cleanup for good reason: the credentials were sitting in a file the attacker could read.
  • Any API key or token stored in the database, including the ones your SEO and mail plugins hold.

Take away the places it likes

Stop the uploads directory executing PHP. On Apache, a .htaccess in wp-content/uploads/ does it:

apache
<Files "*.php">
  Require all denied
</Files>

Check that nothing legitimate on your site relies on running PHP from uploads before you deploy that, then confirm it by requesting a known PHP file under uploads and expecting a 403.

Then keep an eye on the directory nobody watches. Must-use plugins load with no activation record and no way to switch them off from the admin, so a file dropped there runs until somebody looks at the folder.

bash
wp plugin list --status=must-use --fields=name,title
wp plugin list --status=dropin --fields=name,title

Audit ownership the way you audit users

Add the Users and permissions screen to whatever routine you already have for checking your WordPress administrator list. The two lists get attacked together, only one of them is on your server, and the one that is not is the one that survives a full site restore.

Questions

My site looks completely normal. Why does Google show Japanese?
Because the infected pages are served to crawlers and not to you. The injected code reads the request, decides whether it came from a search engine, and returns spam to the crawler and your ordinary page or a 404 to everyone else. Google calls this cloaking. Your browser is the one visitor guaranteed never to see the hack, which is why the site can sit infected for months.
Did somebody translate my pages into Japanese?
No. Your own pages are untouched. The attacker created new URLs on your domain, filled them with generated Japanese text, and pointed Google at them. Google's Security Issues report files this as Hacked: URL injection, which it describes as a hacker creating new pages on your site.
I deleted the malicious files and the Japanese results are still there.
Two different things keep them alive. Google will not drop a URL from the index until it recrawls and finds nothing, which takes weeks for thousands of pages. And if the attacker registered a sitemap in your Search Console property, removing that sitemap from the report does not make Google forget the URLs in it. Google says so in its own documentation. The URLs have to start returning 404 or 410 on their own.
There is a verified owner in my Search Console that I do not recognise.
That is part of the same infection, and it is the part that lives inside Google rather than on your server. An owner can submit sitemaps, change geotargeting and request removals for your property. Removing them takes two steps: delete every verification token they hold, then unverify the account. If you leave one token in place, they re-verify themselves on it.
Do I have to remove the injected posts myself?
Yes. Injected posts are rows in wp_posts, and the loader that publishes them is often an autoloaded row in wp_options. Take the generator and the backdoor off disk first, or the posts come back on the next page load. Then use the two queries in this guide to list the posts, read what they return, and delete the confirmed ones with wp post delete --force.
How long before the Japanese results disappear from Google?
Weeks, sometimes longer. After the URLs return 404 or 410, Google drops them as it recrawls. A security review, requested from the Security Issues report, takes several days to a few weeks and clears the warning label rather than the URLs. If you need the spam gone from results today, the Removals tool hides a URL for about six months while the real fix propagates.

Next

What the file half of this looks like without the manual work

The file half of that procedure is one job repeated: fingerprint every file, work out which ones are not what they should be, and take the injected code out without breaking the file it was hiding in. Segurium runs that job for you. It walks the filesystem, checks each file against what that exact version should contain, and where a legitimate file has injected code in it, writes the cured bytes back so the file keeps working rather than handing you a deletion to recover from.

Then work the rest of it. Re-run the two post queries after the file cleanup, look for the autoloaded row in wp_options that was publishing them, confirm the administrator list holds only accounts you recognise, and go back through Search Console for verification tokens and sitemaps you did not submit. That last one sits inside Google, and it is where this infection keeps its foothold once your files are clean.