Requirements
| WordPress | 5.0 or higher |
|---|---|
| PHP | 7.4 or higher |
| Permissions | PHP must be able to read files across your WordPress installation (core, plugins, themes, uploads) |
| External Dependencies | None. No API keys, no third-party services. All scanning is done locally on your server |
Installation
- Download the ZIP file from your account
- Go to Plugins → Add New → Upload Plugin
- Upload the ZIP and click Install Now
- Click Activate
Uninstalling
Deactivate and delete from the Plugins screen. The scheduled scan cron job is removed on deactivation. Options (ssp_malware_options, ssp_malware_scan_history, ssp_malware_ignored_threats) and the quarantine folder remain in the database and uploads directory. Remove these manually if you want a clean removal.
Quick Start
- Go to Malware Scanner in your WordPress admin sidebar
- Click the Run Scan Now button in the top-right of the header
- Wait for the scan to complete — a progress bar and live log show what's happening
- Review the results on the Dashboard tab
- Set up automatic scans in the Schedule tab so your site is monitored continuously
- Configure Email alerts so you're notified when issues are found
Dashboard
The Dashboard tab is the main screen you see when opening the plugin. It shows a grid of status cards and your most recent scan results.
Status Cards
| Site Status | Shows “Clean” (green), “X Issue(s)” (red), or “Unknown” (yellow) based on the last scan |
|---|---|
| Last Scan | Date/time the most recent scan ran, with duration |
| Files Scanned | How many files were checked in the last scan |
| Total Scans | All-time scan count, broken down into clean vs. issues |
| Issues Found | All-time total issues detected across every scan |
| Next Scan | When the next scheduled scan will run |
| Quarantined | Number of files currently isolated in quarantine |
| Ignored | Number of issues you've chosen to skip |
| Avg Scan Time | Average duration and file count across all scans |
| Last Issue | When the most recent issue was detected (“Never” if all scans have been clean) |
Severity Legend
Below the cards is a color legend explaining the three severity levels: Serious (red), Suspicious (yellow), and Notice (blue).
Running a Scan
Click Run Scan Now from any tab. The Dashboard tab opens automatically and shows:
- Progress bar — fills from 0% to 100% as the scan runs
- Status text — shows what phase the scan is in (core files, plugins, themes, uploads, database, .htaccess)
- Live log — a dark terminal-style area showing real-time scan activity
When the scan completes, the page reloads automatically after 1.5 seconds to show the fresh results.
Understanding Results
Scan results appear in the Recent Results section on the Dashboard. Each result shows:
| Type | What kind of issue was found (e.g. “Malicious Code Detected”, “PHP File in Uploads”) |
|---|---|
| Severity Badge | Serious (red), Suspicious (yellow), or Notice (blue) |
| Location | The full file path or database reference where the issue was found |
| Details | A human-readable explanation of what was detected and why it's flagged |
Up to 50 results are shown on the Dashboard. If more were found, a note at the bottom tells you to check the History tab for the full list.
Bulk Selection
Above the results is a Select All checkbox and a selected count. Check multiple items and click Ignore Selected to bulk-dismiss notices you've reviewed and trust.
Severity Levels
Requires immediate action. These indicate code patterns actively used by hackers.
- Malicious Code Detected — Dangerous patterns like
eval(base64_decode(...)),shell_execwith user input, or nested obfuscation chains. These can give attackers full control of your server - PHP File in Uploads — Executable PHP files in the uploads directory where only images and documents should exist. Almost always injected by hackers
- Disguised File — Files with double extensions like
malware.php.jpgattempting to hide their true type - Suspicious Database Content — Injected
<script>or<iframe>tags in your published posts that load external scripts - Suspicious .htaccess Rules — Directives like
auto_prepend_fileorauto_append_filethat silently inject PHP into every page load
What to do: Quarantine the file immediately, then investigate. If you don't recognize it, it's likely malicious.
Needs review but might be legitimate.
- Encoded .htaccess Content — Long encoded strings in your .htaccess file. Sometimes used by security plugins or CDN configs, but can hide malicious redirects
- Unusual Database Entry — WordPress options containing
evalandbase64together. Some plugins use this legitimately for license validation or caching
What to do: Review the file or content. If it's from a plugin you installed, it's probably fine. When in doubt, contact the plugin developer.
Informational findings that are usually harmless.
- Complex Code Pattern — Lines over 8,000 characters that also contain
evalorbase64_decode. Almost always minified JavaScript from legitimate optimization. Only flagged in Thorough scan mode
What to do: Usually nothing. Use Select All and Ignore Selected to dismiss these in bulk.
Taking Action on Results
Each result has action buttons on the right side:
| Quarantine | Moves the file to a secure, non-executable folder. Only shown for Serious (red) threats. The safest first step — you can always restore later |
|---|---|
| Delete | Permanently deletes the file from your server. Cannot be undone. Use quarantine first if you're unsure |
| Ignore | Adds the issue to your ignore list so it won't appear in future scans. Use for false positives you've verified as safe |
Schedule Tab
Scan Schedule
| Enable automatic scans | Toggle scheduled scanning on/off. When off, the WordPress cron event is removed |
|---|---|
| Frequency | Hourly, Daily, or Weekly. The scan runs once per interval |
| Time | What time of day to run the scan (24-hour format). Schedule during low-traffic hours like 3:00 AM |
| Timezone | Which timezone the scan time is based on. Supports major US, European, Asian, and Pacific timezones plus UTC |
Performance
| Low resource mode | Makes scans slower but uses less CPU and memory. Enable this on shared hosting if scans affect site performance |
|---|---|
| Max file size (MB) | Files larger than this are skipped. Default: 5 MB. Increase if you have large PHP files from page builders |
| Memory limit (MB) | PHP memory limit the plugin requests during scans. Default: 256 MB |
| Keep scan history (days) | How many scan records to keep. Default: 30. Older scans are automatically removed |
| Auto-quarantine serious threats | When enabled, files flagged as Serious (red) during scheduled scans are automatically moved to quarantine |
Scan Options Tab
Sensitivity
| Quick | Fast scan of PHP files only. Good for regular checks. Catches obvious threats but skips deeper analysis |
|---|---|
| Standard (recommended) | Thorough scan of all code files. Best balance of speed and detection |
| Thorough | Deep scan of everything. Checks more file types, looks for subtle patterns like very long code lines. May show more Notice-level results. Use this if you suspect an active infection |
What to Scan
| WordPress core files | Scans /wp-includes/ and /wp-admin/ directories |
|---|---|
| Plugin files | Scans all installed plugins in /wp-content/plugins/ |
| Theme files | Scans all installed themes in /wp-content/themes/ |
| Uploads folder | Scans /wp-content/uploads/ for PHP files and disguised executables |
| Database content | Checks published posts and options table for injected scripts and suspicious encoded content |
| .htaccess file | Checks the root .htaccess for malicious server directives |
Email Tab
Email Recipients
Add one or more email addresses to receive scan alerts. Click + Add Email to add more addresses. Click the × to remove one. The admin email is pre-filled on first activation.
Alert Settings
| Email me when issues are found | Sends an alert email after any scheduled scan that detects threats. Default: On |
|---|---|
| Email me when scan is clean too | Sends a confirmation email even when no issues are found. Useful if you want proof that scans are running. Default: Off |
| From name | The sender name on alert emails. Default: “SSP Malware Scanner” |
| Send Test Email | Sends a test message to all configured addresses to verify email delivery works |
Email Templates
You can customize the email content for both threat and clean-scan emails separately. Use the dropdown to switch between the two template types.
For each type you can configure:
- Subject Line — supports placeholders
- Format — HTML or Plain Text
- Custom HTML Template — leave blank to use the built-in default template, or paste your own HTML
Template Placeholders
{site_name} | Your WordPress site name |
|---|---|
{site_url} | Your site URL |
{scan_time} | When the scan ran |
{duration} | How long the scan took (seconds) |
{threats} | Number of issues found |
{files_scanned} | Number of files checked |
{threat_list} | HTML-formatted list of all issues (threat emails only) |
{dashboard_url} | Direct link to your scanner dashboard |
Exclusions Tab
Skip These Paths
Enter file paths to completely skip during scans (one per line). Use this for directories that contain many false positives or are irrelevant to security, like cache or backup folders.
Example:
/wp-content/cache//wp-content/backups/
Trusted Plugins
Enter plugin folder names (one per line) to skip scanning entirely. Useful for large, well-known plugins that trigger false positives due to their use of encoding or complex code.
Pre-filled defaults: woocommerce, woocommerce-subscriptions, woocommerce-square, all-in-one-seo-pack, wp-mail-smtp.
Safe Upload Paths
Subfolder names inside /uploads/ that legitimately contain PHP files. Files in these paths won't be flagged as “PHP File in Uploads.”
ssp- and the simple-backup-restore folder are automatically excluded and don't need to be listed here.Ignored Issues
Shows the count of currently ignored issues. Click Clear All Ignored Issues to reset the ignore list so all previously ignored items will appear again in future scans.
Quarantine Tab
Quarantine is a safe holding area for suspicious files. When you quarantine a file:
- The file is moved to
/wp-content/uploads/ssp-quarantine/ - The folder is protected with
deny from allin .htaccess so files cannot be executed or accessed via the web - A
.metafile is created alongside it recording the original location and quarantine time - The file is renamed with a timestamp and
.quarantinedextension
For each quarantined file you can see the original name, original path, quarantine date, and file size.
Actions
| Restore | Moves the file back to its original location. The quarantine directory for the original path is recreated if it was deleted |
|---|---|
| Delete | Permanently deletes the quarantined file and its metadata. Cannot be undone |
History Tab
Shows a chronological list of all past scans, newest first. Each entry shows:
- Date and time of the scan
- Status: clean (green left border) or issues found (red left border) with count
- Duration and file count
Click any history entry to expand it and see the full list of issues found during that scan. Each issue shows the same type, severity, location, and details as the Dashboard results.
Use Clear History to delete all scan records. History retention is also controlled by the Keep scan history setting in the Schedule tab.
What Gets Scanned
File Scanning
The scanner recursively walks through the directories you've enabled (core, plugins, themes) and reads each file looking for malicious code patterns. In Standard mode it checks .php, .phtml, .php5, and .pht files. In Thorough mode it checks additional file types.
Files larger than the configured max file size are skipped. The scanner's own files and quarantine folder are always excluded.
Uploads Scanning
The uploads directory gets special treatment. Instead of checking for malware patterns inside files, it checks for files that shouldn't exist in an uploads folder at all — PHP executables and files with disguised double extensions like .php.jpg.
Database Scanning
The scanner queries the wp_posts table for published posts containing <script> tags with external sources, <iframe> tags, eval(atob(...)), and document.write(unescape(...)). It also checks wp_options for entries containing both eval and base64 together.
.htaccess Scanning
Checks the root .htaccess for auto_prepend_file/auto_append_file directives and long encoded strings.
Detection Patterns
The scanner checks for these specific malware patterns in PHP files:
eval(base64_decode(...))— the most common PHP malware patterneval(gzinflate(...))andeval(gzuncompress(...))— compressed malware payloadseval(str_rot13(...))— simple obfuscationeval($_GET[...])/eval($_POST[...])/eval($_REQUEST[...])/eval($_COOKIE[...])— direct execution of user-controlled inputassert($_GET[...])/assert($_POST[...])— assert used as eval alternativepreg_replacewith/emodifier and user input — deprecated code execution via regexcreate_functionwith user input — dynamic function creationfile_put_contentswithphp://input— writing raw POST data to filesshell_exec,system,passthru,popen,proc_openwith user input — command injection- Long base64 strings (500+ chars) followed by eval — obfuscated payloads
- Nested function chains like
@eval(@$a(@$b(@$c— heavily obfuscated backdoors
Performance
- Scans run as a single AJAX request (manual) or cron job (scheduled). They don't affect frontend visitor performance
- The plugin sets
set_time_limit(0)so scans don't timeout on slow servers - Memory limit is configurable (default 256 MB) and set via
ini_setat scan start - Files over the max size limit are skipped automatically
- Low resource mode reduces processing speed to avoid overwhelming shared hosting
- Schedule scans during off-peak hours (2:00–5:00 AM) to minimize any impact
Data Storage
What's Stored
ssp_malware_options | All plugin settings (wp_options table) |
|---|---|
ssp_malware_scan_history | Array of past scan results including file paths and threat details (wp_options table) |
ssp_malware_ignored_threats | Array of MD5 hashes for ignored issues (wp_options table) |
/uploads/ssp-quarantine/ | Quarantined files and their .meta files (filesystem) |
External Connections
The plugin makes zero external connections. No data is sent to third-party servers. All scanning, storage, and email delivery happens on your own server using WordPress core functions (wp_mail for emails, wp_options for storage).
Recommendations by Site Type
Personal Blog or Small Site
- Weekly scans on Standard sensitivity
- Enable email alerts for threats only
- Keep defaults for everything else
Business Website
- Daily scans on Standard or Thorough sensitivity
- Enable email alerts for both threats and clean scans (confirms scans are running)
- Add well-known plugins to the Trusted Plugins list to reduce noise
- Consider enabling auto-quarantine
eCommerce / WooCommerce Store
- Daily or hourly scans — you're handling customer payment data
- Standard sensitivity to avoid false positives from payment gateway plugins
- Add WooCommerce and payment plugins to the Trusted Plugins list
- Enable email alerts — you need to know immediately if something's wrong
- Enable auto-quarantine for serious threats
Membership or High-Traffic Site
- Enable low resource mode if scans affect site performance
- Schedule scans during the lowest traffic hours (2:00–4:00 AM)
- Consider hourly scans if users can upload content
- Add legitimate upload paths to Safe Upload Paths if plugins store PHP there
Troubleshooting
Scan times out or never completes
- Increase the Memory limit in Schedule → Performance (try 512 or 1024 MB)
- Enable Low resource mode
- Reduce Max file size to skip large files
- Add large plugin directories to Trusted Plugins to skip them
- Exclude cache and backup directories in Skip These Paths
- Ask your host if they have a PHP execution time limit and request an increase
Too many false positives / notices
- Switch from Thorough to Standard sensitivity — Thorough mode intentionally casts a wider net
- Add known-safe plugins to the Trusted Plugins list
- Use Select All + Ignore Selected to bulk-dismiss notices you've verified
- WooCommerce, SEO plugins, and page builders often trigger notices because they use base64 encoding and long minified code — this is normal
Email alerts not being received
- Click Send Test Email on the Email tab to verify delivery works
- Check your spam/junk folder
- Verify the email addresses are correct
- Test that WordPress can send emails (install WP Mail SMTP if needed)
- Confirm that Email me when issues are found is checked
- Scheduled emails only fire after scheduled scans — manual scans don't send email alerts
Scheduled scans aren't running
- WordPress cron depends on site traffic. If your site gets very little traffic, cron jobs may not fire on time
- Consider setting up a real server cron job that hits
wp-cron.phpon a schedule - Verify Enable automatic scans is checked in the Schedule tab
- Save the settings again — this re-registers the cron event
- Check that no other plugin or server config is disabling
wp-cron.php(check forDISABLE_WP_CRONin wp-config.php)
Quarantine failed or can't restore a file
- Check that PHP has write permissions to
/wp-content/uploads/ssp-quarantine/ - The quarantine directory must exist — if it was deleted, deactivate and reactivate the plugin to recreate it
- Restore requires the
.metafile to know the original path. If the meta file was deleted, you'll need to move the file manually via FTP or your hosting file manager - File path traversal is blocked — only files within your WordPress installation can be quarantined
I found real malware. What now?
- Don't panic. Quarantine the flagged files immediately
- Change all WordPress admin passwords and database passwords
- Check for any admin users you don't recognize in Users → All Users
- Update WordPress core, all plugins, and all themes to their latest versions
- Delete any plugins or themes you don't use
- Run another scan after cleanup to verify everything is clean
- If you have a clean backup from before the infection, consider restoring it
- Contact your hosting provider — they may have additional security tools or can check server-level compromises