Skip to content

How to check whether your WordPress site has been hacked

Check from outside first, with Google's Security issues report, its Safe Browsing page, a site search and the page as Google is served it. Then check inside, from administrators and checksums to uploads and the access log. Clean results lower the odds and do not prove the site is clean.

By
WP Ministry
Published

In short

  • Start with the checks that need no login. See what Google has flagged, what it has indexed, and what the site serves a stranger.
  • Failed logins in a log, a blank page after an update and an emailed demand for money are not a break-in by themselves.
  • Checksums cover WordPress itself and plugins from WordPress.org. They say nothing about themes, paid plugins, uploads or the database.
  • An administrator nobody created, or a PHP file in uploads that no plugin accounts for, is enough to act on.
  • Clean results lower the odds. They do not prove the site is clean.
  • When a check comes back positive, stop, write it down and follow the first-hour runbook. Do not delete what you found.

You check for a break-in in two passes. From outside, look at what Google has flagged, what it has indexed under your domain, and what the site serves to someone who is not you. From inside, compare the site with what should be there: the administrators, WordPress's own files, the plugins, the uploads folder, the scheduled events, .htaccess and the access log.

One positive result is enough to act on. A run of clean results lowers the odds and does not prove the site is clean, because each check covers only part of it. None of the checks on this page changes the site. If one comes back positive, write down what you found, leave it where it is, and follow the runbook for the first hour.

What counts as a sign

WordPress's documentation asks you to name the specific symptom, because "hacked" on its own says little. It lists these as clear indicators. Each one has a place where you can confirm it.

The signWhere to confirm it
A search engine has blacklisted the site, or it is flagged for distributing malwareThe Security issues report and the Safe Browsing page, below
Visitors say their antivirus flags your pagesThe checks from outside, below
Your host has disabled the site, or says it is being used to attack other sitesThe host's own message. Ask which files or requests it is about.
Something happened that nobody authorized, such as a new user appearingThe administrators check, below
You can see it when you open the siteFetch the page without a browser, as shown below, and keep a copy of what came back

A hack can also show you nothing. Google's guidance for hacked sites describes cloaking: the site gives one page to its owner and another to a search engine, or to a visitor who arrives from one. That is why the outside checks do not rely on what you see when you open your own home page.

What is not a hack

  • Failed logins in a log. Password guessing is automated and often comes from many addresses at once. An attempt that failed opened nothing. How to protect WordPress from brute force attacks covers what to do about it.
  • A blank page straight after an update. WordPress's documentation gives a plugin or a theme with a compatibility problem as a cause of the white screen. That is a fault, and how to fix the WordPress white screen of death has the steps.
  • A site that is down and nothing else. WordPress's documentation notes that your host may be able to confirm whether you are looking at a hack or a loss of service. For the second, use the runbook for a site that is down.
  • An email that says the site was hacked and demands money. A message is not one of the signs above. Run the checks on this page. If they find nothing, you have an email and nothing more. If they find something, the runbook says what to do with the demand.

Check from outside

These need no access to the server, so do them first.

  1. Step 1: Open the Security issues report in Google Search Console

    The report says whether Google has found hacked content, malware and unwanted software, or social engineering on the site. With no issues, it shows a green check mark and a message. With an issue, it lists example addresses, and Google says that list is a sample, not every affected page. If the site is not in Search Console yet, add it first.

  2. Step 2: Check the Safe Browsing site status

    Safe Browsing is the service Chrome and other browsers use to warn a visitor before a dangerous site opens. Enter your address on Google's site status page. It says whether the site currently contains content that Safe Browsing has determined to be dangerous.

  3. Step 3: Search Google for pages you did not write

    Type the line below into Google Search, with your own domain. The site: operator limits the results to one domain, and Google's guidance for hacked sites uses it to uncover pages a hacker added. Look for titles in a language you do not publish in, products you do not sell, and addresses in folders you never made. Add a word your site would never use after the domain to narrow the search. Google notes that the operator does not necessarily return every indexed address, so an ordinary set of results is not the full picture.

    text
    site:example.com
  4. Step 4: See a page the way Google is served it

    In Search Console, type a page's full address into the inspection bar at the top. Select "Test live URL", then "View tested page". You get the page's code as Google's own crawler received it just now, and a screenshot. Spam words and links that are in Google's copy and not in the page you see are cloaking. Do this for the home page and for any address that looked wrong in the search above.

  5. Step 5: Fetch a page as a visitor arriving from Google

    Google's guidance advises against opening a page you suspect in a browser, because malware often spreads through browser vulnerabilities. It recommends fetching the page with and without a Google referrer, since some malware acts only on visitors who come from search results. Run this in a terminal on your own computer. If diff prints nothing, the two copies are the same. If it prints lines, read them. A block of links, a script from a domain you do not know, or a different page altogether is a positive result.

    bash
    curl -s -o plain.html https://example.com/
    curl -s -o from-google.html --referer "https://www.google.com/" https://example.com/
    diff plain.html from-google.html

curl does not follow a redirect unless told to, so a page that sends visitors elsewhere saves as a short file or an empty one. The redirect checker shows where an address leads, hop by hop, without your browser opening anything.

The hacked-site check makes the same two requests from our server, compares where each one ends, and reads the page for links hidden from readers and code written to hide what it does. It reports signs, not a verdict.

Check from inside

The first check needs only the dashboard. The rest need SSH access and WP-CLI. Ask your host whether your plan has them.

  1. Step 1: Look for administrators you did not create

    In the dashboard, open the Users screen and select the link for the Administrator role above the table. Then list the same accounts from the command line, with the plugins and the theme left out. Every name on both lists should be a person you can account for. Check the email address on each real account too, because a password reset is sent to that address. An administrator nobody created is a positive result.

    bash
    wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --skip-plugins --skip-themes
  2. Step 2: Compare WordPress with its checksums

    This downloads the checksums WordPress.org holds for your version and compares every WordPress file with them. A clean run prints "Success: WordPress installation verifies against checksums." Otherwise it warns about each file that does not match, and about each file that should not exist. With --include-root it also warns about anything in the site's main folder that is not part of WordPress. Some of those files will be yours. It does not look inside wp-content, and it skips wp-config.php and .htaccess.

    bash
    wp core verify-checksums --include-root
  3. Step 3: Compare the plugins with their checksums

    This compares each plugin's files with the checksums WordPress.org holds for that plugin and version. A clean run ends with "Success: Verified" and a count. For a failure it lists the plugin, the file, and a message such as "File was added" or "Checksum does not match". Changes to readme.txt are ignored unless you add --strict. A plugin that did not come from WordPress.org cannot be checked this way: the command warns that it could not retrieve the checksums and skips it. WP-CLI has no checksum command for themes.

    bash
    wp plugin verify-checksums --all --skip-plugins --skip-themes
  4. Step 4: List the PHP files that changed recently

    -mtime -14 matches files whose contents changed less than 14 days ago. Set the number to reach back to when you last knew the site was fine. Updates change files too, so compare the list with what you updated and when, and open any file that changed on a day when nobody updated anything. An old date clears nothing, because the touch command lets a file's owner set its modification time to any value.

    bash
    find . -type f -name "*.php" -mtime -14
  5. Step 5: Look for PHP files in the uploads folder

    WordPress's hardening guide describes wp-content as the place for user-supplied content, writable by the web server. The uploads folder inside it is for media, so a PHP file there needs explaining. Open each one this lists. A file that no plugin of yours accounts for, or one filled with long unreadable strings, is a positive result. Leave it where it is.

    bash
    find wp-content/uploads -type f -name "*.php"
  6. Step 6: List the scheduled events

    WordPress runs timed jobs through its own scheduler, which it checks on every page load. WordPress uses it for update checks and scheduled posts, and plugins add events of their own. The table shows each event's hook and when it runs next. wp_version_check, wp_update_plugins and wp_update_themes are WordPress's update checks. For a hook you do not recognize, find the file that names it with grep -rl "hook_name" wp-content, using the hook's name. A hook that no plugin or theme of yours accounts for goes in your notes.

    bash
    wp cron event list --skip-plugins --skip-themes
  7. Step 7: Read every .htaccess file

    .htaccess is a file Apache reads, one folder at a time. If your site runs on another web server, skip this check. WordPress's documentation calls it one of the files most often changed in a break-in, and notes that copies can sit in other folders as well as the main one. List them all, then compare the one in the main folder with the standard block WordPress writes, shown in how to fix the 403 Forbidden error. Look for a rule that neither you nor a plugin added, above all one that sends visitors to another domain or applies only to visitors arriving from a search engine.

    bash
    find . -name ".htaccess"
  8. Step 8: Search the access log

    Ask your host for the access log, or download it from the control panel if yours offers one. In Apache's format, each line holds the visitor's address, the time, the request and the status code the server answered with. Search it for requests to PHP files in uploads. A line with status 200 means the file was there and the server answered for it. Look also at requests for dashboard pages such as /wp-admin/plugins.php. WordPress sends anyone who is not logged in to the login page, so a 200 for one of those, from an address none of your people use, is someone logged in.

    bash
    grep -E "/wp-content/uploads/[^ ]*\.php" access.log

What a scanner adds, and its limits

WordPress's documentation describes two kinds of scanner: remote ones that read the site from outside, and plugins that run on the site. It says each looks for and reports on different things, that no one solution is the best approach, and that using them together improves your odds.

  • A scanner that reads from outside sees only what the site serves it. Google's guidance notes that malicious content may be served only to certain user agents or referrers, to avoid detection by site owners and by malware scanners.
  • A scanner plugin reads the files on the server, so it can report on code that is never shown to a visitor. It reports what it was built to recognize.
  • Free versions differ in what they scan. WordPress security plugins compared sets out what each plugin scans and where it does the work.

Run one as a further check. Its clean result is the same kind of evidence as the others: that scanner found nothing it looks for.

What these checks cannot tell you

  • The database. Apart from the users and the scheduled events, nothing here reads it. Google's guidance describes code injected into the database through SQL injection, which no file check sees.
  • Themes and paid plugins. The checksum commands do not cover them.
  • When something happened. Modification times can be set by hand, and a log reaches back only as far as your host keeps it.
  • Anything beyond this one WordPress. WordPress's documentation warns that a hack may reach more than one site, above all on shared hosting, and that many break-ins begin on the owner's own computer.

If everything comes back clean and the doubt remains, run the checks again in a week. WordPress's hardening guide recommends monitoring the site's files for changed and added files, and some of the security plugins in the comparison above do that from then on.

When a check comes back positive

Stop looking. Do not delete the file, the user or the rule you found: the site as it stands is the record of what was done to it. Write down what you found, where, and the time. Then follow the hacked site runbook, which covers the first hour in order. Cleaning comes after that and has its own guide, how to remove malware from a hacked WordPress site.

Get help when:

  • a check found code you cannot read, and you cannot tell whether it belongs;
  • you cannot run the inside checks and the outside ones leave you unsure;
  • the site holds customer accounts, orders or payment details.

Our malware removal service is a one-time clean-up with hardening, a blacklist removal request and a written report.

Common questions

Can a site be hacked with no visible sign?

Yes. Google's guidance describes cloaking, which exists to keep a hack out of the owner's sight: you see your page, and a search engine or a visitor from one sees something else. That is the reason to check what Google has indexed and what the site serves to a request that is not yours.

Google shows no problem. Is the site clean?

It means Google has detected nothing. The Security issues report and the Safe Browsing page tell you what Google found, and neither reads the files on your server. Treat both as one check among the others.

A file fails its checksum. Is that proof of a hack?

It is proof that the file differs from the original. It does not say who changed it. Someone may have edited a plugin by hand. Download the same version of the plugin from WordPress.org and compare the two files. Code that nobody on your side can explain is a positive result.

What can I check without SSH or WP-CLI?

All of the outside checks, the Users screen, and the uploads folder and .htaccess through your host's file manager. For the rest, send your host the commands on this page and ask them to run them.

More on this subject

Malware removal, done for you

Malware removal is $99. Full clean-up, hardening, blacklist removal request and a written report. 30-day re-clean guarantee. It starts with a free diagnosis.