This is a real cleanup from September 2026, on several WordPress sites we look after for small businesses. We’ve left out their names. Everything else is as we found it.
What kept happening
The sites carried malware in wp-content/mu-plugins, the folder for must-use plugins. Files there run on every page and don’t appear on the Plugins screen, which makes it a favourite hiding place. We deleted them and the scan came back clean. Then the same files were back.
What we found
- A small PHP file in the uploads folder called
wp-cache-stats.php. The name sounds like a caching plugin’s statistics file. Nothing like it belongs in uploads. - A copy of the malware in the database. An entry in
wp_optionscalled_wp_cron_lock_statusheld the malware’s PHP code, encoded in base64. The name is made to look like one of WordPress’s own housekeeping settings. - The link between them. When
wp-cache-stats.phpran, it read that database entry, decoded it and wrote the must-use plugin files back to disk. Whoever controlled it could trigger the rebuild whenever they wanted.
Why deleting files didn’t work
Each cleanup removed what was easy to see, the files in mu-plugins. The two pieces that rebuilt them were easy to miss: one sat in a folder people rarely open, and the other was a database entry, which scanners that only read files never look at. Across these sites we eventually identified eight different malware families, but this pair was the one that kept undoing our work.
Moatline grew out of this cleanup. It flags every PHP file in uploads and says what it can do (for this one, that it “reads restore payload from DB”), and it checks the database for code stored in settings, not only the files.
The cleanup order that works
- Find everything before you delete anything. Files in
mu-plugins, PHP files inuploads, plugins you don’t recognise, database entries holding long encoded text, scheduled tasks and administrator accounts. If you remove one piece while another is still running, it can put the first back. - Back up the files and database as they are, so you can look back later.
- Remove all of it in one go: the uploads file first, then the database copy, then the files it wrote, then fake plugins and unknown administrators.
- Block PHP in uploads so a new file there can’t run. Moatline does this with one click on Apache and LiteSpeed servers and shows the rule for nginx.
- Change every password and the keys in
wp-config.php, including hosting, FTP and database passwords, and log everyone out. - Watch for three days. Scan daily and compare the
mu-pluginsanduploadsfolders with the day before.
Check your own site
- Open
wp-content/uploadsand search it for.phpfiles, including names like.php5,.phtmlor images that end in.php. - Open
wp-content/mu-plugins. If you don’t know what a file there does, find out before you trust it. Some hosts add their own, so check with your host. - In phpMyAdmin, sort
wp_optionsby the size ofoption_valueand look at the biggest entries. Long blocks of random-looking letters under a name you don’t recognise deserve a closer look. - Or run the free Moatline Malware Scanner, which checks all three.
Related: how to remove malware from WordPress and fake CAPTCHA malware hidden in the database.
Don’t want to do this yourself? Our team cleans hacked WordPress sites every week: WordPress malware removal service, from $45, usually done within 24 hours.
Frequently asked questions
Why does my WordPress malware come back after I delete it?
Can a PHP file in wp-content/uploads be legitimate?
Will restoring a backup stop the reinfection?
How long should I watch the site after a cleanup?
Scan your site for free
Moatline checks files, the database and page-builder content. Nothing changes unless you click a button.
More guides
- Fake WordPress security plugins: a real case from four hacked sites
- How to remove malware from WordPress
- WordPress site hacked? What to do in the first hour
- How to scan WordPress for malware
- Your WordPress site sends visitors to a fake Microsoft support page
- WordPress files deleted: how we rebuilt a site from its database