# guide

How to scan a WordPress site for malware over SSH

Updated October 2026

A security plugin can only scan what WordPress lets it see, and only while the site still runs. From a shell you can scan every file on disk, including sites that are broken, offline or that you have never logged into. This guide takes about ten minutes and uses only free software.

Connect and find the site

Log in with the SSH credentials from your host, then find the WordPress document root. On most servers it is under /var/www, /home/USER/public_html or /srv/www. The quickest way to locate every install is to search for wp-config.php:

$ ssh deploy@example.com
$ find / -name wp-config.php -not -path '*/node_modules/*' 2>/dev/null

If you manage several sites, wpsniffer count-sites (installed in the next step) gives the same answer once the tool is in place.

Install WPSniffer

WPSniffer needs Python 3.8 or newer and nothing else. Install it into a virtual environment in your home directory, which works on shared hosting without root:

$ python3 -m venv ~/wpsniffer
$ ~/wpsniffer/bin/pip install git+https://github.com/CommanderWaterford/wpbastion

Check it runs:

$ ~/wpsniffer/bin/wpsniffer --help

If python3 -m venv fails on Debian or Ubuntu, install the python3-venv package first, or ask your host to.

Scan the site

Point malware-scan at the document root. The first run downloads the YARA Forge core rule package from GitHub and caches it; later runs start immediately.

$ ~/wpsniffer/bin/wpsniffer malware-scan /var/www/html

You get a live progress display and one line per suspicious file naming the YARA rule it matched. A site with a few thousand files finishes in well under a minute on a typical VPS. For a large media library, restrict the scan to PHP files, which is where WordPress malware lives:

$ find /var/www/html -type f -name '*.php' -print0 | ~/wpsniffer/bin/wpsniffer malware-scan

Read the results

For anything beyond a quick look, write a CSV with the columns that help you judge each hit:

$ ~/wpsniffer/bin/wpsniffer malware-scan --match-all --output-path ~/scan.csv \
    --output-columns filename,signature_id,signature_description,rule_author,rule_reference,matched_text \
    /var/www/html

Three columns do most of the work. signature_description says what the rule looks for, rule_reference links to the author's write-up, and matched_text shows the exact bytes that fired, so you can usually tell a webshell from a false positive without opening the file.

Red flags that almost always mean a real infection:

  • Any .php file under wp-content/uploads.
  • Files in wp-includes or wp-admin that are not part of the WordPress release, or core files that have been modified.
  • Rules whose name contains WEBSHELL, BACKDOOR or a known family name.
  • Files with a change time much newer than the rest of the install.

Broad rules can fire on minified or obfuscated code in legitimate plugins. Confirm before deleting, and silence a rule that is clearly noise on your sites with -e RULE_NAME.

What to do next

  • Matches found: follow the cleanup guide. Do not delete files until you have confirmed them and kept a copy.
  • Nothing found: a clean core scan is good news but not proof. Re-run with --yara-forge extended, then check recently modified files and WordPress core checksums.
  • Either way: put the scan on a nightly schedule with an email report so the next infection is caught the same day.