A hacked website rarely announces itself. More often it redirects some visitors to a pharmacy, sends spam quietly, or serves a phishing page at a URL nobody normally visits. Here is how to confirm an infection, remove it thoroughly, and keep it from coming back — in that order, because cleaning without closing the hole is a weekend wasted.
Signs of an infection
- Google Search Console reports "Social engineering" or "Hacked content", or Chrome shows a red warning.
- Visitors from Google are redirected, while typing the URL directly works (malware often targets search referrals only).
- Unknown admin users, plugins or scheduled tasks.
- Outbound mail queue full of messages you did not send.
- Files with recent modification dates you did not touch, or PHP files inside
wp-content/uploads. - The host's scanner (Imunify360 on VPSPioneer plans) reports findings.
1. Take a copy and lock the doors
Before changing anything, take a full backup of the infected state — you may need it for forensics — and note the time. Then reduce the damage while you work:
- Put the site in maintenance mode or, if it is serving phishing pages, take it offline.
- Change every password: hosting panel, SFTP, database, WordPress admin, and the email accounts on the domain.
- Revoke API keys and application passwords.
2. Find the malicious files
Run a scanner first. On Plesk, Imunify360's Malware Scanner scans the whole account and lists findings with paths. WordPress Toolkit's Security → Check and the Wordfence plugin also scan core and plugin files against known-good checksums.
Then look by hand, because scanners miss new variants. Recently changed PHP files:
cd /var/www/vhosts/example.com/httpdocs
find . -name "*.php" -mtime -14 -not -path "./wp-content/cache/*" | head -50PHP where it does not belong:
find ./wp-content/uploads -name "*.php"The functions almost every web shell uses:
grep -rlE "eval\(|base64_decode\(|gzinflate\(|str_rot13\(|preg_replace\(.*/e" --include="*.php" . | headExpect some false positives in legitimate plugins; open the file and look. Malware is usually a long single line of encoded text, often at the top of an otherwise normal file, or in a file with a name mimicking WordPress core (wp-conf.php, class-wp-cache.php).
Check the things that survive file cleanup: .htaccess for redirect rules, wp_options in the database for injected scripts (siteurl, home, and anything in active_plugins you do not recognise), and wp_users for extra admins.
grep -n "RewriteRule\|Redirect" .htaccess3. Clean — or, better, replace
For WordPress, the reliable method is not to edit infected files but to replace them:
- Download a fresh copy of the same WordPress version and overwrite
wp-admin,wp-includesand the root PHP files. - Delete each plugin and theme directory and reinstall it from the official source. Delete anything you are not using.
- Keep
wp-content/uploadsandwp-config.php, after checking both by hand (uploads for PHP files, config for injected code at the top or bottom). - Check the database as above.
WordPress Toolkit does step 1 in a click (Reinstall core). If you have a clean backup from before the infection and know when it happened, restoring that backup is faster than all of the above — but only if you also do step 4 next, or it will reinfect within days.
4. Close the hole
The infection came through something. The likely candidates, in order:
- An outdated plugin or theme with a known vulnerability. Update everything; remove what you do not use.
- A weak or reused password, or no two-factor auth on the admin.
- A neighbouring site on the same account — infections spread within an account. Check every site under the same login.
- A compromised computer with saved SFTP passwords.
- File permissions allowing writes where they should not be — see file permissions explained.
Read the access log for the days before the infection: requests with POST to odd URLs, or to a plugin's file directly, show the entry point.
grep -h "POST" logs/access_ssl_log* | grep -v "wp-admin/admin-ajax\|wp-login\|wp-cron" | tail -505. Rescan and request review
Run the scanner again; it should be clean. Then in Google Search Console, Security & Manual Actions → Request review, describing what you found and fixed. Reviews typically clear within a day or two. If the domain was blacklisted for spam, check mxtoolbox and request delisting from each list.
Keeping it clean
Automatic core and plugin updates, two-factor auth, a scanner that runs daily, off-server backups, and no unused plugins. Our WordPress hardening checklist is the full list.
Every VPSPioneer shared plan runs Imunify360 with automatic cleanup, and on any plan you can ask us to do the removal above for you — it is the most common job our security team does.