How to run a nightly malware scan with cron and get an email report
Updated October 2026
A scan you run once tells you about yesterday. A scan that runs every night tells you about today, which is what matters when a plugin vulnerability is being exploited across the internet. WPSniffer has no daemon and no account, so scheduling it is a crontab line.
Prepare
Install WPSniffer as described in the docs, run one scan by hand so the rule package is cached and you know the command works, and create a directory for reports:
$ sudo mkdir -p /var/log/wpsniffer && sudo chown deploy /var/log/wpsniffer
$ /home/deploy/wpsniffer/bin/wpsniffer malware-scan --output-path /var/log/wpsniffer/test.csv /var/www
Note the full path to the wpsniffer binary inside the virtualenv. Cron does not load your shell profile, so the job must use it.
The cron job
This entry scans everything under /var/www at 03:00 every night and writes a dated CSV. Open the crontab of the user that can read the sites with crontab -e and add:
# m h dom mon dow command
0 3 * * * /home/deploy/wpsniffer/bin/wpsniffer malware-scan --output-path /var/log/wpsniffer/$(date +\%F).csv /var/www > /var/log/wpsniffer/last-run.log 2>&1
Two cron details catch people out: % must be escaped as \%, and the job's working directory is the home directory, so use absolute paths throughout. The rule package cache refreshes itself daily, so every night's scan uses current rules with no extra step.
For a faster nightly run that covers only files changed in the last day, with a full scan on Sundays:
0 3 * * 1-6 find /var/www -type f -ctime -1 -print0 | /home/deploy/wpsniffer/bin/wpsniffer malware-scan --output-path /var/log/wpsniffer/$(date +\%F).csv
0 3 * * 0 /home/deploy/wpsniffer/bin/wpsniffer malware-scan --output-path /var/log/wpsniffer/$(date +\%F)-full.csv /var/www
Email the results
WPSniffer can send an email summary at the end of a scan, with the suspicious files, the number of files scanned, bytes processed and elapsed time; the options are listed under wpsniffer malware-scan --help. If you prefer to control delivery yourself, or want mail only when there is something to see, wrap the scan in a short script:
#!/bin/sh
# /usr/local/bin/wpsniffer-nightly
WPS=/home/deploy/wpsniffer/bin/wpsniffer
REPORT=/var/log/wpsniffer/$(date +%F).csv
TO=ops@example.com
"$WPS" malware-scan --match-all --output-path "$REPORT" \
--output-columns filename,signature_id,signature_description,rule_reference /var/www \
> /var/log/wpsniffer/last-run.log 2>&1
if [ -s "$REPORT" ]; then
mail -s "[wpsniffer] $(wc -l < "$REPORT") suspicious files on $(hostname)" "$TO" < "$REPORT"
fi
Make it executable and point cron at it. mail comes from mailutils or s-nail; any server that already sends WordPress notifications has an MTA you can reuse. Sending only when the report is non-empty means an email is always worth opening.
0 3 * * * /usr/local/bin/wpsniffer-nightly
Alternative: a systemd timer
On systemd hosts a timer gives you logs in journalctl and no missed runs after reboots. Create two units:
# /etc/systemd/system/wpsniffer.service
[Unit]
Description=Nightly WPSniffer malware scan
[Service]
Type=oneshot
User=deploy
ExecStart=/usr/local/bin/wpsniffer-nightly
# /etc/systemd/system/wpsniffer.timer
[Unit]
Description=Run WPSniffer nightly
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now wpsniffer.timer
$ systemctl list-timers wpsniffer.timer
Housekeeping
- Rotate reports. Delete CSVs older than 90 days with a second cron line, or add the directory to
logrotate:30 3 * * * find /var/log/wpsniffer -name '*.csv' -mtime +90 -delete - Exclude known noise. When a rule fires on the same legitimate plugin every night, add
-e RULE_NAMEto the command rather than learning to ignore the email. - Pin for stability, refresh for coverage. The daily rule refresh is right for production monitoring. If you need identical results across runs, for example to compare servers, pin a YARA Forge release.
- Act the same day. When the email arrives, follow the cleanup guide.