Finding and removing a WordPress backdoor
A backdoor is the door the attacker keeps open to come back after every cleanup. Here's where they hide in WordPress and how to track them down for good.
You cleaned up the spam, deleted the fake pages, removed the unknown admin account, and three days later it all came back. That’s the classic sign of a WordPress backdoor still in place. A backdoor is a piece of code the attacker leaves behind to regain control whenever they want, without needing your password. As long as it’s there, your site isn’t clean. It’s on borrowed time.
Why removing the visible spam is never enough
The spam you see (casino pages, redirects, SEO injections) is only the output. The backdoor is the input. When you delete the parasite pages but leave the malicious .php file, the attacker simply runs their script again and regenerates everything in seconds. Often it’s automated: a cron job or scheduled task recreates the content after every deletion.
That’s exactly why knowing whether your site is hacked isn’t enough. You have to locate the persistence mechanism, not just observe the damage. A serious intrusion rarely leaves a single backdoor. There are often several, scattered across different locations, precisely so they survive a partial cleanup.
The classic backdoor hiding spots
An attacker puts their code where you don’t look and where WordPress checks nothing.
The .php files in wp-content/uploads
The uploads folder should only hold media: images, PDFs, videos. Never PHP. A .php file sitting in wp-content/uploads/2024/03/ is an almost certain sign of compromise. Attackers love this spot because it’s writable by WordPress and rarely inspected.
Fake files that mimic the core
WordPress has well-known file names: wp-load.php, wp-blog-header.php, wp-settings.php. Attackers create copies with nearly identical names to slip by unnoticed: a wp-load.php placed in the wrong directory, a wp-conftig.php, a wp-cron-php.php, or a legitimate core file whose contents have been modified. At a glance in an FTP client, these names look normal.
The wp-content/mu-plugins folder
Must-use plugins are run automatically by WordPress, without appearing in the plugin list in the admin and without being deactivatable from the interface. It’s a prime hiding spot. If you never created this folder yourself, any .php in it deserves immediate inspection.
Code injected into functions.php and wp-config.php
Rather than adding a file, the attacker inserts their code at the top (or bottom) of a legitimate file. The functions.php of the active theme and the wp-config.php at the root are favorite targets, because they’re loaded on every request. Look for long unreadable lines, encoded code, or a block pasted after the closing ?> tag.
Files with random names
x7f3a9.php, wp-jx82.php, lock360.php… A file with a name that resembles nothing standard, especially at the root or in wp-includes, is suspect by default. WordPress has a finite list of files; anything outside it has to be justified.
The code patterns to look for
Backdoor code is almost always obfuscated to evade reading and antivirus tools. A few functions come up constantly:
eval(— runs a string as PHP code. Very rare in legitimate code, everywhere in backdoors.base64_decode(— decodes a payload hidden in base64, often passed toevalafterward.gzinflate(,gzuncompress(,str_rot13(— other obfuscation layers, frequently stacked.assert(— runs code likeevalin older PHP versions.$_POST,$_REQUEST,$_GET,$_COOKIEpassed directly to an execution function — that’s the channel the attacker uses to send commands.preg_replacewith the/emodifier — a classic trick to run code through a regular expression.create_function(,call_user_func,system(,shell_exec(,passthru(— dynamic execution or access to the system shell.
None of these functions is forbidden on its own, but combining them with encoding and user input leaves no room for doubt.
Searching in practice over SSH
If you have SSH access, the search is far faster than doing it by hand. Go to the site root, then run these commands.
List files containing the most common obfuscation patterns:
grep -rEl "eval\(|base64_decode\(|gzinflate\(|str_rot13\(" wp-content/
Find any PHP file that has no business being in the media folder:
find wp-content/uploads -name '*.php'
Spot recently modified PHP files (useful if you know roughly when the intrusion happened):
find . -name '*.php' -mtime -15 -ls
Inspect the contents of the must-use plugins folder:
ls -la wp-content/mu-plugins/
Search for direct execution of user input:
grep -rEl "\\\$_(POST|REQUEST|GET|COOKIE)\[" wp-content/ wp-includes/
For each file that comes up, open it and read it before deleting. Some results will be false positives (legitimate plugins use base64_decode for valid reasons). The test: code that’s unreadable, encoded, has no comments, no plugin header, and runs external input.
The limits of manual cleanup
Here’s the truth few tutorials state plainly: you can never be 100% sure you found everything by hand. A backdoor can be:
- split across several files that are only dangerous once assembled,
- hidden in the database rather than in the files,
- encoded in a way your
grepdoesn’t catch, - tucked into a file whose modification date has been faked to look old.
You can remove ten backdoors and miss an eleventh. And one is enough for everything to start over.
That’s why the reliable approach isn’t to “clean” but to replace. In practice:
- The WordPress core: don’t repair it. Delete it and reinstall the exact version from wordpress.org. That way all core files become known and clean.
- Plugins and themes: reinstall each one from its official source rather than hunting injected code line by line. Remove the ones you no longer use.
- The only files to keep:
wp-config.php(after checking it line by line), thewp-content/uploadsfolder (after purging any.php), and any customizations you have a clean source for.
By replacing everything that’s replaceable, you shrink the area to inspect manually down to a tiny footprint, instead of auditing thousands of files.
Regenerate every access credential
A backdoor isn’t just a file. The attacker may have grabbed credentials. After replacing the files, change all passwords (WordPress admin, database, FTP/SFTP, hosting), change the salt keys in wp-config.php, revoke application passwords, and review the list of administrator accounts to remove any intruder. Without this step, an attacker who knows your credentials doesn’t even need a backdoor to come back.
Preventing the next backdoor
Once the site is clean, close the doors that let the attacker in. Keep the core, plugins, and themes up to date, since most intrusions exploit a known vulnerable extension. Remove what you don’t use: every inactive plugin is still an entry point. Block PHP execution in wp-content/uploads at the server level. Set up regular external backups so you can compare and restore. The full procedure is laid out in our guide on securing WordPress to avoid a repeat.
FAQ
How do I know if a backdoor is still active after cleanup? Watch the days that follow: if spam, files, or accounts reappear without any action from you, a backdoor survived. A server-side security scan and a comparison of core files against a clean install confirm the real state.
Does a security plugin remove every backdoor? No. Scanners detect known signatures, but obfuscated or fragmented code regularly slips under the radar. They’re useful as a complement, not a guarantee. Replacing files in full remains safer.
Do I need to rebuild the site from scratch? Rarely. Reinstalling the core, plugins, and themes from official sources, while keeping the cleaned database and media, is enough in the vast majority of cases. A full rebuild is only necessary if the database itself is heavily compromised.
At WP-Detox, hunting down backdoors and injected code is exactly what we do: we look for what most automated tools miss, we replace rather than patch, and we regenerate every access credential to cut off persistence. The scan is free, the cleanup takes about 30 minutes, it’s €149 all-in, and you’re refunded if the site isn’t clean. If you’d rather see the big picture before acting, start with the complete guide to cleaning a hacked WordPress.