You replaced every WordPress file with a clean copy and the problem is still there. The spam link is still in the footer, the pop-up still appears, the site still sends some visitors elsewhere. When that happens, the infection is very likely sitting in the database, where no amount of file cleaning will reach it.
WordPress keeps almost everything that makes your site yours in its database: posts and pages, menus, widgets, theme settings, plugin settings, users and their permissions. Attackers know that. This page covers how we clean a compromised database without breaking the site, and it forms one part of our hacked website repair and emergency help.
Why attackers like the database
File-based malware is what most scanners and most cleanups focus on. The database gets less attention because it is harder to read: rows of content, settings stored in a compact format, and thousands of entries on a busy site. Code placed there survives a fresh install of WordPress, a theme reinstall and a plugin update, because none of those touch the stored data.
The database is also how many attacks happen in the first place. A plugin flaw that allows SQL injection or stored cross-site scripting lets an attacker write straight into your tables without ever uploading a file. Our explainer on SQL injection and XSS in plain language covers how those flaws work.
What we usually find, table by table
Table names below use the default wp_ prefix. Many sites use a different prefix, and the same tables exist under it.
Posts and pages (wp_posts)
Script tags appended to the end of post content, hidden iframes, invisible blocks of spam links, or whole spam posts saved as drafts or under unusual post types. Revisions matter here: WordPress keeps earlier versions of content, and an infected revision can be restored by accident later. Injected code is often added to every post at once by an automated script, so a single sample tells us what to search for across the table.
Settings (wp_options)
This is where a lot of the damage hides. Changed siteurl or home values can send the whole site to another domain. The active_plugins entry can list a plugin you never installed. Widget settings, theme customiser values and the “header and footer scripts” fields of various plugins can carry code that prints on every page. Malicious scheduled tasks are stored here too, inside WordPress’s cron entry.
Users and permissions (wp_users, wp_usermeta)
An extra administrator is the classic find. Less obvious is an ordinary-looking account whose capabilities in wp_usermeta have been raised to administrator, or a real account with an added application password. If you have already spotted an account you do not recognise, our post on unknown admin users in WordPress explains what to check before deleting it.
Post meta, comments and plugin tables
SEO plugin fields that hold titles and descriptions, page builder data stored as meta, comment content with hidden links, and custom tables created by forms, shops or page builders. Plugin tables are easy to forget because they do not follow WordPress’s usual layout.
Clues that the problem lives in your tables
You do not need to read SQL to suspect the database. A few patterns point there fairly reliably:
- The same strange script or link appears in many posts, but only in content, not in the theme’s header or footer files.
- The site address in Settings, General has changed, or the site suddenly loads from a different domain.
- A widget area shows content you did not add, and the widget itself looks empty or harmless in the editor.
- Searching the post editor’s code view reveals something the visual editor hides, such as an invisible block at the bottom of a page.
- A security plugin flags posts or options by ID rather than by file name.
None of these prove the files are clean; they only show that the database is involved too. That distinction shapes the order we work in.
Why careless find-and-replace breaks sites
Much of WordPress’s stored data is serialized: a PHP format that records the length of every piece of text alongside the text itself. If you change a string inside that data and the recorded length no longer matches, PHP cannot read the entry any more. The visible result can be a widget that disappears, theme settings that reset, or a plugin that stops working.
So we never clean the database with a plain text replace in phpMyAdmin or a raw SQL UPDATE across serialized fields. We use tools that understand serialized data, such as WP-CLI’s search-replace, and for anything unusual we read the entry, rebuild it correctly and write it back. We also test each pattern on a copy of the database before applying it to the live one.
How we clean a hacked database
Back up and copy
Before any change, we export the full database and keep it. A second copy becomes our working database, where search patterns are tried and checked. Nothing is run against the live site until it has been tested there.
Map the infection
We search every table, not just the WordPress ones, for script tags, iframes, encoded strings, suspicious domains, hidden styling around links and known malware markers. The aim is a list of every affected row and what was injected, which also tells us whether one attack or several are involved.
Remove the injected content
Injected code is removed from posts, options, meta and plugin tables, with serialized entries handled correctly. Spam posts and spam revisions are deleted. Settings such as the site address are put back to their real values.
Fix accounts
Rogue administrators are removed after we have recorded their details, raised capabilities are put back, application passwords you do not use are revoked and passwords are reset for remaining administrators. The database user’s own password is also changed at the hosting level, and the site’s configuration updated to match.
Check from the outside
We load pages as a new visitor, on mobile and desktop, and from a search result, because some injected scripts only show in those conditions. If the database was carrying a redirect, the checks follow what our redirect hack removal page describes.
The database is rarely the whole story
Database injections usually come with file-based access too. An attacker who could write to your tables may also have left a script on the server that writes them again. That is why a database cleanup on its own is not a complete job. We pair it with the file search set out on our WordPress backdoor removal page, and where the injected content is mostly search spam, with the work on our SEO spam cleanup page.
What you can safely do now
- Export a database backup from your hosting panel and keep it somewhere off the server, even though it is infected. It is a record of the state of things.
- Do not run find-and-replace queries on the live database yourself, even if a forum post suggests one.
- Do not delete unknown user accounts yet. Note their username, email and creation date first.
- If posts you edit keep getting code added back, stop editing for now. Something is still writing to the database.
- Change your hosting panel and WordPress administrator passwords from a device you trust.
Cleaning is not the same as optimising
Some owners ask whether a database “clean-up” plugin will remove the infection. Those plugins remove old revisions, expired temporary data and leftover tables, which is useful for speed and size, as our post on WordPress database optimisation explains. They are not designed to find injected code, and some will happily keep it.
If you think your database has been tampered with, start a WhatsApp message to us with the word HACKED and include your domain and what you are seeing. We will look, give you an honest view of what is going on and when we can start, back up before touching anything, and keep you updated on WhatsApp. The database work fits within the full cleanup described on our WordPress malware removal service page.