Strange redirects, spam pages in Google, a warning from your host, or a server that suddenly runs at 100% CPU: these are classic signs that a PHP or WordPress website has been hacked. After critical bugs like PHP2Shell, it's worth checking even if nothing looks wrong. Many attacks install a hidden backdoor and wait. This guide walks through a practical investigation with copy-paste commands, then shows how to clean up safely.

Warning signs
- Visitors (especially from Google or on mobile) are redirected to spam, gambling or "you won" pages.
- Google shows Japanese/pharma spam pages under your domain, or Search Console reports Security issues.
- New admin users you didn't create.
- Unknown
.phpfiles in upload folders, or files with random names. - High CPU usage (often crypto-miners) or your host suspends the account for sending spam.
Step 0: Don't destroy the evidence
Before deleting anything, take a copy of the site files, the database and the logs. You'll need them to understand how the attacker got in, and to restore anything you remove by mistake.
TS=$(date +%F)
sudo tar czf ~/site-files-$TS.tgz /var/www/example.com
sudo tar czf ~/logs-$TS.tgz /var/log/nginx /var/log/apache2 /var/log/php* 2>/dev/null
mysqldump -u root -p example_db | gzip > ~/db-$TS.sql.gz
Step 1: Check WordPress core and plugin files
If you use WordPress, WP-CLI can compare every core and plugin file with the official versions. It's the fastest way to find modified files:
cd /var/www/example.com
wp core verify-checksums
wp plugin verify-checksums --all

Any file reported as "doesn't verify against checksum" or "should not exist" needs a closer look. Note: premium plugins from outside WordPress.org can't be verified this way.
Step 2: Find recently changed and suspicious PHP files
# PHP files modified in the last 30 days
find /var/www/example.com -name '*.php' -mtime -30 -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort -r | head -50
# PHP files inside upload folders (there should be none)
find /var/www/example.com/wp-content/uploads -name '*.php'
# files containing common backdoor functions
grep -rlE --include='*.php' 'eval\(base64_decode|eval\(gzinflate|eval\(str_rot13|assert\(\$_(POST|GET|REQUEST)|shell_exec\(\$_' /var/www/example.com
Legitimate plugins occasionally use these functions, so a match isn't proof. But code like eval(base64_decode($_POST['x'])) in a file called wp-cache.php inside uploads/ is almost certainly a web shell.
What a typical web shell looks like
<?php
// Found in /wp-content/uploads/2026/10/cache.php
// Takes code from a POST parameter and runs it. Delete files like this.
@eval(base64_decode($_POST['k']));
Step 3: Check users and the database
# WordPress administrators
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
# recently registered users
wp user list --orderby=registered --order=DESC --number=10 --fields=user_login,user_registered
Also look for spam links or scripts injected into posts and options:
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' AND post_status='publish';"
wp option get siteurl
wp option get home
A changed siteurl or home value causes redirects for everyone.
Step 4: Look for persistence
Attackers make sure they can come back. Check scheduled tasks and running processes:
# cron jobs for the web user and root
sudo crontab -l -u www-data
sudo crontab -l
ls -la /etc/cron.d/
# WordPress scheduled events with odd hook names
wp cron event list
# processes and outbound connections run by the web server user
ps -u www-data -o pid,etime,cmd
sudo ss -tunp | grep -E 'php|www-data'
A PHP process holding a connection to an unknown IP, or a process named like kworker under www-data, is a strong sign of compromise.
Step 5: Read the access logs
# POST requests to unusual PHP files (shells are usually called via POST)
sudo awk '$6 ~ /POST/ {print $7}' /var/log/nginx/access.log | grep '\.php' | sort | uniq -c | sort -rn | head -20
# who accessed a suspicious file
sudo grep 'cache.php' /var/log/nginx/access.log*
The first request to a web shell usually shows how it was uploaded: a vulnerable plugin, a stolen password, or a server bug like PHP2Shell.
Cleaning up
- Take the site offline or put it in maintenance mode if it's serving malware.
- Restore from a clean backup taken before the first malicious change, if you have one. That's safer than hand-cleaning.
- Otherwise reinstall WordPress core and every plugin and theme from official sources. Keep only
wp-content/uploads(after removing any PHP files) and your database. - Update everything: PHP, WordPress, plugins and themes. Remove plugins you don't use.
- Change every secret: hosting, SSH, database and admin passwords, API keys, and the WordPress salts:
wp config shuffle-salts # logs everyone out
wp user update admin --user_pass="$(openssl rand -base64 24)"
- Check Google Search Console > Security issues, request a review once the site is clean, and remove spam URLs with the Removals tool.
- Keep watching logs for a few weeks. Attackers often try their old backdoors again.
Prevention
- Keep PHP and your CMS updated. Turn on automatic security updates on your server.
- Block PHP execution in upload folders. For Nginx:
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}
- Use two-factor authentication for admin accounts and SSH keys instead of passwords.
- Keep daily off-site backups and test restoring them.
Found something suspicious and not sure what it is? Describe it in the comments (don't paste live passwords or full malicious code) and I'll help you identify it.
Comments
Post a Comment