Skip to content
All posts

Seven signs your WordPress site has been hacked, and how to check each one

Redirects, spam mail, unfamiliar admins and mystery cron jobs. A practical checklist for confirming a WordPress compromise with actual evidence.

The Talos team10 min read

The hardest part of a suspected WordPress compromise is the uncertainty. Something is off — a visitor mentions a redirect you cannot reproduce, your mail starts landing in spam — but you have no idea whether you are looking at an attack, a misconfigured plugin, or a caching artefact.

This is a checklist for resolving that uncertainty. Each sign comes with what actually causes it and how to confirm it with evidence rather than a hunch.

1. Redirects that only some visitors see

What you notice: A customer reports being sent to a spam domain. You visit the same page and it is fine.

This is the most common signature of a real compromise, and the inconsistency is deliberate. Injected redirect code typically checks the user agent, the referrer, or a cookie before firing. It skips logged-in users and often skips known crawler user agents, specifically so the site owner does not see it.

How to check: Visit in a private window with a Google referrer, and try a mobile user agent. But the reliable method is to search the source rather than reproduce the behaviour. Look for conditional logic keyed on HTTP_USER_AGENT or HTTP_REFERER near a redirect, and for obfuscated blocks:

eval(base64_decode('...'))

eval combined with base64_decode has almost no legitimate use in a WordPress plugin. It is the single highest-signal pattern to grep for.

2. An administrator account you do not recognise

What you notice: A user in the admin list you did not create, often with a plausible name and a throwaway email.

How to check: Users → All Users, filter by Administrator, and sort by registration date. Anything created around the time symptoms started deserves scrutiny. Be aware that sophisticated attacks hide the account from the user list entirely by filtering pre_user_query, so an empty list is not proof. Counting administrator rows directly is more reliable than trusting the admin screen.

Do not simply delete it. Deleting the account removes your evidence and does nothing about the mechanism that created it — which will just create another. Demote and lock it, find the entry point, then clean up.

3. Mail suddenly going to spam

What you notice: Contact form notifications stop arriving. Customers say order confirmations went to junk.

Compromised WordPress sites are valuable primarily as mail relays. A site sending thousands of spam messages gets its IP and domain reputation destroyed within days, which is why your own legitimate mail stops being delivered.

How to check: Two things point at this. First, check your mail queue or your host's outbound mail logs for volume you cannot account for. Second, check wp_mail usage — a compromise often hooks into WordPress's own mail function so the spam inherits your configured SMTP credentials.

Worth ruling out first: this symptom also has an entirely innocent cause, which is sending mail from your host's IP with no SPF or DKIM record. Check your DNS before assuming the worst.

4. Cron jobs you did not schedule

What you notice: Nothing, usually. This one is invisible unless you look.

This is the sign I would check earliest, because it is both high-signal and easy to verify. Persistence is the attacker's main problem: you might clean the injected file, so they need a mechanism to put it back. A scheduled WordPress event is the standard solution.

How to check: Inspect your WP-Cron events and look for hooks that do not belong to any plugin you recognise. Randomised or vaguely-plausible hook names are typical. An unrecognised recurring event is close to conclusive on its own.

This also explains the most demoralising experience in WordPress cleanup: you remove the malicious code, verify the site is clean, and it is back the next morning. You removed the payload and left the mechanism.

5. File modification dates that make no sense

What you notice: A theme file modified last Tuesday when nobody touched the site.

How to check: Sort plugin and theme directories by modification time. Injected code is usually appended to the top or bottom of an existing legitimate file rather than added as a new file, because a new file is more conspicuous. wp-config.php, functions.php and index.php are the usual targets.

WordPress core is easier: Site Health reports on modified core files, and core is checksummed against the official release. Unexpected core modification is unambiguous.

6. Error log entries from paths that should not exist

What you notice: PHP warnings referencing files or directories you do not recognise.

Malicious code is often written carelessly and throws warnings. That makes your error log one of the better detection tools available, and almost nobody reads it.

How to check: Read the log tail and group entries by type rather than scanning chronologically — a thousand repeated lines collapse into one finding with a count, and the unusual entry becomes visible. Look for warnings referencing paths in uploads, which should never contain executable PHP.

7. Search results showing pages you never published

What you notice: A site: search on your domain returns pharmacy or casino pages.

This is spam injection, and it is often invisible when browsing normally because the injected content is served only to crawler user agents. By the time it appears in search results it has usually been running for weeks.

How to check: Search site:yourdomain.com and look for anything unfamiliar. Check Google Search Console for a sudden jump in indexed pages, and for any manual action notice. Then fetch a page with a Googlebot user agent and compare the response to what you see in a browser.

Confirming it properly

Individually, most of these signs have innocent explanations. A modified file might be a legitimate update. Spam-foldered mail might be a missing DNS record. What distinguishes an actual compromise is several signs corroborating each other, usually clustered around the same dates.

That is the case for reading the source rather than trusting a scan verdict. A signature scanner tells you something matched a pattern. It does not tell you what the code does, whether it is a false positive, or what else in the site is connected to it — and false positives are common enough in commerce plugins that a lone match proves very little.

What you want, for each finding, is the file, the line number, the surrounding code, and an explanation of what it does. That is enough to make a judgement yourself.

Talos scans plugin and theme source directly and reports findings with file and line evidence, then captures every cleanup step in a reversible change set — so an over-aggressive cleanup is a rollback rather than a restore from backup.

If it is real

Order matters here.

  1. Do not delete anything yet. Take a backup of the compromised state. It is your evidence, and you may need it to work out the entry point.
  2. Find the persistence mechanism first. Cron events, mu-plugins, wp-config.php includes. If you clean the payload and leave the mechanism, it comes back.
  3. Clean reversibly. Aggressive cleanup causes its own outages. Track what you remove so you can put it back if the site breaks.
  4. Rotate every credential. Database password, WordPress salts in wp-config.php, all admin passwords, API keys, hosting panel access.
  5. Update everything. The entry point was almost certainly a known vulnerability in an outdated plugin.
  6. Then scan server-side. Anything running inside WordPress can only see what WordPress can see. A compromise outside the WordPress directory — or in another site on the same shared account — needs your host's tooling.

That last point is worth taking seriously. If the same hosting account runs several WordPress installs, assume all of them are compromised until you have checked each one. Cross-site infection through a shared parent directory is one of the most common reinfection routes there is.

Put an agent to work in WordPress.

Spend less time clicking through admin and more time moving your site forward.