Backdoors Hidden in .user.ini and auto_prepend_file
By Glenn Lyvers · Updated · 4 min read
You replaced WordPress core, reinstalled every plugin, swapped the theme for a fresh copy and cleaned the database. The malicious behaviour is still there. At that point it is worth stepping outside WordPress entirely, because PHP itself has a feature that loads a file before every single request, and configuring it takes one line in a file most people have never opened.
What auto_prepend_file does
PHP has a setting called auto_prepend_file that names a file to be included automatically at the start of every PHP request. Whatever you point it at runs before the requested script does. There is a matching auto_append_file for the end. Both are legitimate PHP configuration, intended for things like shared bootstrapping or profiling instrumentation.
The consequence when abused is total. The prepended file runs before WordPress loads, on every request, regardless of what WordPress itself contains. You can delete every file in your site and reinstall from scratch, and if that setting survives, the attacker's code still runs first.
Where the setting lives
On most shared hosting running PHP as FastCGI, per-directory PHP settings go in a file called .user.ini. It behaves rather like .htaccess for PHP configuration: drop one in a directory and its settings apply to that directory and below. It is a plain text file, it is easy to create, and the vast majority of WordPress sites do not have one.
The setting can also come from a php.ini file, from .htaccess directives on some server configurations, or from panel-level PHP settings for the account. Any of those will do the job. The common factor is that none of them are part of WordPress, which is exactly why a WordPress-focused cleanup walks straight past them.
Why scanners and reinstalls miss it
Security plugins scan WordPress. They check core file integrity, they read plugin and theme files, they look at the database. A configuration file sitting in your web root that contains one line of PHP configuration is not malware by any signature definition — it contains no code at all, just a setting.
The file it points at is the payload, and that can be placed anywhere, including outside your web root where it is not reachable by URL and not covered by a site scan. That combination — a harmless-looking configuration line plus a payload stored out of scope — is why this survives cleanups that feel exhaustive. My guide on reinfection loops covers the broader category this belongs to.
Finding it
Look for .user.ini and php.ini files throughout your site, starting at the web root and working down. They begin with a dot, so make sure your FTP client or file manager is showing hidden files — plenty of people have looked straight at a directory containing one and not seen it.
The fastest confirmation is to ask PHP what it is actually using. A temporary script calling phpinfo() will show the effective value of auto_prepend_file along with which configuration file set it. That removes all guesswork about where the setting is coming from. Delete that script immediately afterwards, since phpinfo output is itself useful reconnaissance for an attacker.
Removing it
Read the prepended file before deleting it, because it tells you what else to look for. Then remove both the file and the configuration line pointing at it. If the setting came from your hosting control panel rather than a file, change it there.
Then treat the rest of the site as compromised, because something wrote that configuration. Do the full pass — core, plugins, theme from clean sources; database checked; administrator accounts audited; credentials rotated — and look specifically for the other persistence mechanisms that travel with this one, particularly mu-plugins backdoors and scheduled tasks.
Keeping it from coming back
After cleanup, verify with phpinfo once more that no prepend is configured, then check again a few days later. A setting that reappears means something on the server is still writing it, and that changes the problem from a site cleanup to an account-level one — my guide on hosting account compromise covers that escalation.
It is also worth asking your host whether any other site under the same account is infected, since cross-contamination is a common path for this. If you have already cleaned the site twice and it keeps coming back, that is precisely the scenario my malware removal service is built for.
Common questions
What is a .user.ini file?
A per-directory PHP configuration file used on most shared hosting running PHP as FastCGI. It works rather like .htaccess but for PHP settings, applying to the directory it sits in and everything below. Most WordPress sites have no legitimate need for one, so finding an unexplained one is significant.
Why did reinstalling WordPress not fix this?
Because the setting is not part of WordPress. Reinstalling replaces WordPress files; it does not touch a PHP configuration file in your web root, and it certainly does not touch a payload stored outside the site directory. The prepended code keeps running before WordPress even starts.
How do I check what is actually configured?
Create a temporary PHP file that calls phpinfo(), load it once, and look at the effective value of auto_prepend_file and the list of additional configuration files parsed. That tells you both the setting and its source. Delete the file immediately afterwards — it exposes a great deal of useful detail about your server.
Can the payload be outside my website folder?
Yes, and it often is. The prepend setting takes a filesystem path, not a URL, so the file can sit anywhere the PHP process can read. Placing it above the web root keeps it unreachable by browser and outside the scope of most site scanners, which is a large part of the appeal.
Does this mean my hosting account is compromised, not just my site?
Not necessarily — a site-level compromise is usually enough to write a .user.ini in the web root. But if the setting comes from account-level PHP configuration, or keeps reappearing after you remove it, that points at broader access and should be treated as an account compromise.
More than malware
Most people meet me in an emergency. It isn’t all I do.
I’ve been building and repairing systems since 1995. Whatever brought you here, there’s a good chance I can help with the rest of it too — and you’ll be dealing with the same person either way.
Hacked, but not WordPress?
Joomla, Drupal, Magento, Shopify, PrestaShop, Laravel, Node, IIS and plain HTML — cleaned the same way, priced the same way.
Take a look →Custom builds & AI systems
Plugins, custom applications, website chatbots and automation — built to do exactly what you need, maintained by the person who wrote them.
Take a look →Servers, speed, SEO & accessibility
Migrations, faster load times, technical SEO and accessibility fixes. Measured improvements, with the numbers to show you.
Take a look →Better web hosting
Fast, secure hosting with SSL and backups included at no extra charge. Clear pricing, no long-term contracts, no surprises.
Take a look →Classes & free tools
Rather learn to handle it yourself? I teach this, and I give away the tools I built for my own cleanups.
Take a look →Something else broken?
Half my work is untangling what someone else started, gave up on, or broke. Describe it in plain words and I’ll tell you honestly.
Take a look →Tell me what’s wrong. I’ll tell you what it takes.
No queue, no call centre, no sales pitch — one person who answers, quotes honestly, and does the work.