Malicious Code in functions.php: Finding It and Cleaning It Out

By · Updated · 5 min read

If you've found code in your theme's functions.php that you don't recognise, the short version is this: functions.php is executed on every single page load, front end and admin alike, which makes it the single most convenient place on a WordPress site to hide malicious code. Cleaning it is genuinely simple — you replace the file with a known-good copy rather than trying to surgically edit it — but the cleanup only sticks if you also find the other copies and close the door that let someone write to the file in the first place. This guide covers all three.

Why attackers pick functions.php

WordPress loads the active theme's functions.php before rendering anything, every time, with no caching layer skipping it and no user interaction required. Code placed there runs when a visitor loads a page, when Googlebot crawls one, and when you log into your own dashboard. It survives most plugin scans people run, because owners assume malware lives in odd files with odd names, and it survives content cleanups because it isn't in the database. For an attacker who has managed one small write to your filesystem, it's the highest-value real estate available — the same logic that makes .htaccess and wp-config.php the other two favourites.

What injected code looks like

You don't need to be able to read PHP to spot most injections, because they have a look. The tells I see over and over:

It's at the very top or very bottom. Injection scripts don't parse your file, they prepend or append. A dense block sitting above the theme's opening comment header, or dangling after the last line of legitimate code, went in mechanically.

It's one enormous unreadable line. Legitimate theme code is formatted for humans. Injected code is very often a single line hundreds or thousands of characters long, scrolling far off the right of your editor.

It's scrambled on purpose. Long blocks of random-looking letters and numbers in quotes, decoded and executed at runtime. Normal theme code has no reason to hide what it does; obfuscation in functions.php is close to a diagnosis by itself.

It has a plausible-sounding disguise. Function names like wp_temp_setup or theme_update_check, chosen to look like housekeeping. The name means nothing — what matters is whether the code was in the original theme.

It behaves differently for different visitors. Code that checks whether the visitor is a search crawler, or whether a user is logged in, and changes what it serves accordingly. That's cloaking infrastructure, and it's how a site stays visibly clean for its owner while serving spam to Google — the mechanic behind the Japanese keyword hack and the pharma hack alike.

The definitive test isn't reading the code, it's comparison. Download a fresh copy of your exact theme version from wordpress.org or the vendor and diff the two files. Anything present in yours and absent in theirs needs explaining.

Cleaning it without breaking the site

Take a backup first, infected as it is — if something goes wrong you want the option of stepping back. Then don't edit; replace. Hand-deleting the visible bad block invites two failures: you miss a second, subtler injection further down, or you clip a brace of legitimate code and take the site down. Replacing the whole theme with a pristine copy of the same version sidesteps both.

Do the work over SFTP or your host's file manager, not through the dashboard's theme editor — if the site is compromised, the dashboard itself may be watched, and the editor is disabled on many hardened sites anyway. If you run a child theme, its functions.php is yours rather than the vendor's, so there's no reference copy; that one you check by eye, against the tells above, or against an old clean backup.

Then check every other installed theme, active or not. Several malware families — WP-VCD most famously — infect all themes precisely so that cleaning the active one achieves nothing. While you're there, delete the themes you'll never use.

functions.php is rarely the whole story

A functions.php injection means someone had write access to your filesystem, and almost nobody with that access writes to one file. Assume companions until proven otherwise: standalone backdoor files tucked into wp-content/uploads or wp-includes, edits to .htaccess, scheduled tasks that recreate deleted files, admin accounts you didn't create, and code stored in the database rather than on disk — my database malware guide covers that last one. If you clean functions.php alone and the code is back tomorrow, one of these companions put it back; that loop is the subject of my guide to reinfection.

How it got written, and how to stop the sequel

Something let an attacker write to that file: usually a vulnerable plugin or theme, stolen or weak credentials, or an earlier backdoor that was never fully cleared. My guide to how WordPress sites get hacked walks through finding which. Whatever the route, three cheap habits make functions.php a much harder target: keep everything updated so known plugin holes close before they're used on you; add define('DISALLOW_FILE_EDIT', true) to wp-config.php so a stolen admin login no longer comes with a built-in code editor; and put the site under file-change monitoring so the next unexpected edit to a theme file pages you instead of sitting undiscovered for six months.

If you'd rather have the whole job done properly in one pass — the file, its companions, the entry point, and the hardening — that's exactly the shape of my malware removal service. And if you're staring at a block of code right now wondering whether it's malicious, send it to me; telling you takes me a minute and costs you nothing.

Common questions

Can I just delete functions.php to remove the hack?

No — functions.php is a required theme file, and deleting it outright will break or blank your site. The right move is replacement, not deletion: install a pristine copy of your exact theme version from wordpress.org or the vendor, which swaps the infected file for a clean one while keeping the site running.

Will updating the theme clean an infected functions.php?

Usually yes for that one file, because an update overwrites functions.php with the vendor's copy. But it does nothing about injected code in your other installed themes, backdoor files elsewhere on the server, rogue admin users, or the vulnerability that allowed the write. Sites that are "cleaned" by a theme update alone are commonly reinfected within days.

Why does the malicious code reappear after I remove it?

Because something else on the server is putting it back: a backdoor file in uploads or wp-includes, an infected copy in an inactive theme, a scheduled task, or code hidden in the database. The functions.php injection is an output, not the source. Find and remove the companion first, then clean functions.php again, and it will stay clean.

What does DISALLOW_FILE_EDIT actually do?

Adding define('DISALLOW_FILE_EDIT', true) to wp-config.php removes the theme and plugin code editors from the WordPress dashboard. Its value is damage limitation: if an attacker steals or guesses an admin password, they no longer get a built-in editor for writing malicious PHP into your theme with two clicks. It costs nothing in day-to-day use and I recommend it on effectively every site.

How do I compare my functions.php with the original?

Find your theme's exact version (listed in style.css or the dashboard), download that same version from wordpress.org or the commercial vendor, and compare the two files with a diff tool — or simply open them side by side and scan for blocks present in yours but not theirs. Anything added, especially obfuscated code at the very top or bottom of the file, is what you're looking for. For a child theme, compare against your most recent known-clean backup instead.