You just found out your WordPress site is compromised — a browser warning, a client email, a strange admin user. The instinct is to start deleting files. Don't. The first hour is about preserving your options, not cleaning. A rushed deletion destroys the evidence you need to find the entry vector, and a site that gets cleaned without closing the door is a site that gets reinfected.
Here is the order I work in.
1. Don't delete anything yet
Every file the attacker dropped is a clue: when it was created, what it does, what else it touches. Delete it now and you lose the timeline. You also risk taking the site down in a way that makes recovery harder. Resist the urge to "just remove the bad file" until you have a snapshot.
2. Snapshot the files and the database
Before you change a single thing, take a full copy you can always roll back to:
- A complete archive of the WordPress directory (files and timestamps).
- A full database dump.
tar czf site-snapshot-$(date +%F).tar.gz /path/to/webroot
mysqldump -u USER -p DBNAME > db-snapshot-$(date +%F).sql
Store both off the server. This snapshot is your safety net and your forensic record at the same time.
3. Take it offline the right way
If the site is serving malware, redirecting visitors, or showing spam, you want it out of harm's way — but "out of harm's way" is not "deleted." Put up a static holding page or enable maintenance mode at the web-server level. That stops visitors and search engines from seeing the payload while the install stays intact for investigation.
Pulling the database or wiping wp-content to "make it stop" usually makes the cleanup slower, not faster.
4. Rotate every credential
Assume everything the site could reach is exposed: WordPress admin passwords, the database password in wp-config.php, SFTP/SSH keys, and any API keys stored in plugins. Rotate them. While you're in wp-config.php, regenerate the authentication keys and salts so existing login sessions are invalidated — a sleeper admin account can't ride an old cookie back in.
5. Preserve the logs
Your access and error logs are how the entry vector gets found. Copy them off the server now, before they rotate out:
- Web-server access logs (look for
POSTrequests to plugin files and towp-login.php). - PHP error logs.
- Any host or WAF logs you can export.
What not to do in the first hour
- Don't restore an old backup and call it done. If you don't know the entry vector, the restore is reinfected within days through the same hole.
- Don't install three "malware removal" plugins on top of a live compromise. They add noise and rarely find server-side backdoors.
- Don't email the attacker, pay anything, or negotiate. WordPress compromises are automated and opportunistic; there's nobody to negotiate with.
Find the blast radius
Once you have a snapshot, you can look without fear. Two quick checks that tell you a lot:
SELECT ID, user_login, user_email, user_registered
FROM wp_users ORDER BY user_registered DESC LIMIT 20;
find /path/to/webroot -name '*.php' -mtime -30 -printf '%TY-%Tm-%Td %p\n' | sort
A new administrator you didn't create, or fresh PHP in wp-content/uploads/, confirms the compromise and starts the trail. My full triage checklist walks through the rest of the signals.
When to hand it over
Do it yourself if it's a personal site, you have a clean backup, and the entry vector is obvious. Bring in help if money or customer data is involved, if the site has been reinfected before, or if you can't find how they got in. That last one matters most: a cleanup that doesn't close the door isn't a cleanup.
If you'd rather not work through this alone, WordPress incident response is a flat per-site engagement — manual cleanup, the entry vector identified and closed, and a written report. Open an engagement with what you know and I'll take it from the snapshot.