Most WordPress compromises are quiet on purpose. The attacker doesn't want you to notice, because cloaked spam, sleeper administrators, and skimmers only pay if they run for weeks. By the time the site is visibly broken, the breach is already old.

This guide is the triage checklist we work through on every engagement before we touch anything. You can run it yourself.

1. Browser or Search Console says so

If any of the following is true, treat the site as compromised until proven otherwise:

  • Chrome, Firefox, or Safari shows a red interstitial: "Deceptive site ahead", "The site ahead contains malware", or "This site has been reported as unsafe".
  • Search Console has a Security issues notice — even if it's "resolved" you should still review what was flagged.
  • Google search results for your domain include This site may be hacked.

For each of these, the underlying compromise is real even if you can't see it in your browser. The malicious payload often serves only to Googlebot or only to specific referrers. See Remove 'Deceptive site ahead' warning for the cleanup path.

2. Redirects you didn't put there

Open an incognito window. Search Google for your brand. Click the result. Do you land on your site, or on a casino, a pharmacy, or a "you've won an iPhone" page?

If you land somewhere else, the site has a cloaked redirect — JavaScript or .htaccess that fires only for Google-referred traffic. This is one of the highest-revenue attacker payloads, because most owners don't notice. They visit the site directly, see their own home page, and assume everything is fine.

3. Search results show content you didn't write

Search Google for site:yourdomain.com. Scroll through the results. Anything in Japanese, anything about Viagra or Cialis, anything that looks auto-generated?

That's Japanese SEO spam or pharma hack — synthetic pages an attacker generated using your domain's authority. Even if you don't see them on your own site (cloaked), Google sees them and indexes them.

4. An admin user you don't recognise

SELECT ID, user_login, user_email, user_registered
FROM wp_users
ORDER BY user_registered DESC
LIMIT 20;

Look at the most recent rows. Anyone you don't recognise? Email addresses from unfamiliar domains? Logins that look auto-generated (admin2, wp_admin_98, random hex)?

A sleeper administrator is the most reliable persistence technique: even if you clean every file, a sleeper account lets the attacker walk back in through wp-login.php and re-plant everything.

5. PHP execution in wp-content/uploads/

The uploads directory should serve images, PDFs, videos. It should almost never execute PHP.

find wp-content/uploads -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.php5' -o -name '*.pht' \) -mtime -180

Anything found in the last 180 days deserves a closer look. The filename usually doesn't tell you what's inside — small random PHP files, .cache.php, wp-login.php inside uploads — those are almost never legitimate. Read the file.

6. An autoloaded option that's suspiciously large

SELECT option_name, LENGTH(option_value) AS sz
FROM wp_options
WHERE autoload = 'yes'
ORDER BY sz DESC
LIMIT 20;

Look for unfamiliar option names with very large values. Pharma payloads, sleeper persistence, and stolen credentials sometimes live in wp_options because that table is loaded on every WordPress page request. If you see a row you don't recognise and it's autoloaded and it's tens of kilobytes, treat it as suspect until you've decoded it.

What to do before you "clean"

Two things, in this order:

  1. Snapshot. A full filesystem tarball plus a mysqldump of the WordPress database. Compressed, dated, kept offline. This is the only forensic record you'll have once you start changing things.
  2. Stop guessing. If you've found one piece of malware, assume there is at least one more. Successful intrusions almost always drop persistence in multiple places: a file loader, a database hook, a cron job, a sleeper user. Removing the first one without the others guarantees the site comes back compromised in days.

If any of the six signals above are true and you'd rather not work through this yourself, open an engagement — we'll triage and quote before changing anything.