You were looking through your site’s files, perhaps on a host’s advice or because a scanner flagged something, and inside wp-content/uploads you found a file ending in .php. It might be called something harmless-sounding like cache.php, wp-info.php or index.php, tucked in a month folder between product photos.
That file deserves your attention. The uploads folder is meant to hold images, PDFs and other media, and PHP files there are one of the clearest signs that someone has placed code on your server. This post explains why, how to find every one, which few are usually harmless, and what to do about the rest.
What the uploads folder is for
When you add an image to a post or upload a PDF to the media library, WordPress stores it in wp-content/uploads, normally in folders by year and month. Plugins also use the folder: form plugins keep attachments there, page builders save generated CSS, backup plugins sometimes store archives, and WooCommerce keeps downloadable product files in its own subfolder.
What all of these have in common is that they are data. The web server is supposed to hand them to a browser as they are. A photo is sent as a photo. A PDF is sent as a PDF. Nothing in uploads is supposed to run.
A PHP file is different. When someone requests a .php file from a typical WordPress server, the server does not send the file. It runs it, and sends back whatever the code produces. That is how WordPress itself works. It is also why a PHP file in a folder full of images is such a problem.
Why attackers put PHP files there
The uploads folder has one property that makes it attractive: WordPress needs to be able to write to it. Every time you upload media, the web server creates new files there. So if an attacker finds any way to make the server write a file, uploads is often the easiest place for it to land.
Common ways a PHP file ends up there:
- A vulnerable upload feature. A plugin that lets visitors upload files, such as a form, a profile photo field or a file manager, may fail to check what type of file is being sent. An attacker uploads a PHP script instead of an image.
- A compromised admin account. With admin access, an attacker can install a plugin or use an existing file manager to drop files anywhere.
- A second-stage drop. Once an attacker has any foothold, the first thing they often do is copy extra backdoors into several places. Uploads, with its hundreds of folders, is an easy place to hide a few more.
Whatever got them there, the files tend to do one of a handful of jobs. Some are web shells, which give the attacker a browser-based control panel for your server. Some are small backdoors that accept and run any code sent to them. Others generate spam pages, send email, or host a phishing page. All of them let the attacker come back even after you change your passwords. That last point explains why a WordPress site keeps getting reinfected after each cleanup: the visible damage goes, but a file in uploads is left behind.
How to find PHP files in uploads
If you have SSH access, one command lists every PHP file in the folder and its subfolders. Run it from the WordPress root:
find wp-content/uploads -type f -name "*.php"
That catches the obvious ones. Attackers know owners look for .php, so also check for other extensions a server may run as PHP, and for names designed to fool a quick glance:
find wp-content/uploads -type f \( -iname "*.php*" -o -iname "*.phtml" -o -iname "*.phar" -o -iname "*.pht" \)
The *.php* pattern picks up variations like .php5 and .php7, and names like photo.php.jpg. Whether a double extension actually runs depends on the server configuration, but a file named that way in uploads is suspicious regardless.
Also look for .htaccess files inside uploads:
find wp-content/uploads -name ".htaccess"
An attacker can place an .htaccess file in a folder that tells an Apache server to treat files like .jpg or .txt as PHP. If you find one, read it. Lines containing AddHandler, AddType or SetHandler that mention php are a red flag.
No SSH? Your hosting file manager usually has a search. Search inside the uploads folder for “.php”. It is slower but works. An FTP client that can sort a folder by file type helps too.
The few PHP files that are usually harmless
Not every PHP file in uploads is malicious. Before you delete anything, check what it contains.
The most common harmless one is an index.php containing only a comment such as “Silence is golden”. WordPress ships files like that in some of its own folders, and many plugins add them to their subfolders in uploads. Their only job is to stop the server from listing the folder’s contents if someone visits it directly. The whole file is just a PHP opening tag and a single comment line.
Some plugins also keep small configuration or cache files with a .php extension inside their own subfolder in uploads, usually with a line at the top that stops the file doing anything when requested directly. These are less common and worth checking against the plugin’s documentation or a clean install.
Everything else should be treated as suspect until proved otherwise.
What malicious ones look like
Open the file in a text editor, not in a browser. Requesting it in a browser runs it. Signs of malicious code include:
- A long block of random-looking letters and numbers, often inside base64_decode, gzinflate or str_rot13.
- Functions that run text as code: eval, assert, create_function or preg_replace with an unusual modifier.
- Code that reads from $_POST, $_GET, $_REQUEST or $_COOKIE and passes it straight into one of those functions.
- A file that is a single very long line.
- A login form or a file browser built into one file.
- File names that copy WordPress’s own, such as wp-load.php or wp-settings.php, sitting where they do not belong.
Do not rely on a file’s date. Attackers often change modification dates to match the files around them, so a backdoor can look as old as the photos next to it.
What to do when you find one
- Copy before you delete. Download the suspicious files, or the whole folder, somewhere safe and offline. They can show what the attacker was doing and help date the break-in.
- Note the location and date of each. Clusters in the same month folder often point to when, and sometimes how, they arrived.
- Delete the malicious files, and any rogue .htaccess in uploads.
- Look beyond uploads. A PHP file here is rarely the only thing an attacker left. Check core files, plugin and theme folders, the site root, the database and the list of admin users.
- Find the entry point. Look at which plugins handle uploads, check them for known vulnerabilities, and update or remove them. Without this, the files come back.
- Change all credentials after the cleanup: WordPress admins, hosting, FTP and database.
The broader process for a compromised site, from first checks to cleaning, is set out in our step-by-step guide to fixing a hacked WordPress site. If you want help with the whole job rather than just this folder, our hacked website repair page explains how we take it on.
Stop PHP from running in uploads
Since nothing in uploads should run as PHP, you can tell the server not to run it at all. This does not remove existing malware, and it does not fix the hole that let it in, but it means a PHP file that lands there in future cannot run.
On Apache servers running a current version, which covers many shared hosts, an .htaccess file inside wp-content/uploads with these lines blocks access to PHP files:
<FilesMatch "\.(php|phtml|phar)$"> Require all denied </FilesMatch>
On Nginx, .htaccess files are ignored, so the equivalent rule has to go in the server configuration. On managed hosting, ask the host, because many already block PHP in uploads or can turn it on for you. Some security plugins, Sucuri’s among them, include a hardening option that does the same job.
Test afterwards. Upload a harmless test file, request it in a browser, and confirm you get a 403 Forbidden response rather than the file running. Then delete the test file. Also confirm that your images, PDFs and any plugin features that rely on uploads still work normally.
Correct file and folder permissions matter here too, because overly open permissions make it easier for files to be written where they should not be. Our post on WordPress file permissions covers the usual settings.
Keep an eye on it
Once uploads is clean, it is worth checking again regularly. A scheduled scan from a security plugin will flag new PHP files in uploads, and some hosts offer file change alerts. If you are comfortable with the command line, running the find command above every week or two takes seconds.
While you are in the folder, it is also a good chance to clear out old files you no longer use. Fewer files make it easier to spot one that should not be there. Our guide to a safe media library cleanup explains how to do that without breaking pages that still use the images.
If you found PHP files in uploads and they keep coming back, the cause is usually another backdoor elsewhere or an entry point still open. Our WordPress backdoor removal service is aimed at exactly that, and you can message us on WhatsApp, starting with the word HACKED, to describe what you found.
Next in this series: WordPress Core File Integrity Check With Checksums