
Fabian Wagner, founder of Blax
Published
The first hour: what not to do
- Do not delete files. They are evidence, and if personal data may have leaked, Google advises you to weigh your legal duties first.
- Do not restore an old backup while the hole is still open.
- Do not click "Re-install" in wp-admin. The WordPress FAQ warns that it often only overwrites existing files, while hacks often add new ones.
- Do not request a Google review yet.
- Do not open infected pages in a browser at all: malware often spreads through browser vulnerabilities. Look at them with the URL Inspection tool in Search Console or with cURL instead (Search Console Help).
- Do not log in from a computer that might be infected.
Instead, write down what you see, when you noticed it (with the time zone) and what changed recently, such as a new plugin or user. The WordPress FAQ calls documentation "the first actionable step", and these notes become your incident report.
Contain: take the site offline the right way
Google's guide to quarantining a site says to stop the web server, or point your DNS to a static page on another server that answers with HTTP status 503. That 503 has to come from outside the hacked installation. A maintenance plugin runs inside the infected WordPress, and robots.txt "only blocks search engine crawlers", not visitors. Your host may set up the 503 for you. Google calls it "unlikely" that a temporary outage during recovery affects your ranking.
Tell your host now. On shared hosting the hack may reach beyond your site, the host can check its other customers, and it may already have suspended your account.
Lock the attacker out: every password, from a clean device
The WordPress hardening guide is blunt: no amount of server security helps "if there is a keylogger on your computer." Google recommends several reputable antivirus scanners on every computer an administrator used to sign in. Then, from a device you trust, change every password that gives access to the site:
- WordPress: all administrators, then everyone else.
- Hosting panel, SFTP or FTP, and SSH.
- The database user. Put the new password into
wp-config.php(DB_PASSWORD), or the site goes down. - The mailbox behind the WordPress admin address. Password reset links land there.
The commands in this guide need SSH access with WP-CLI. Not every package has it: World4You includes SSH only in Webhosting Ultra, Hetzner only from Webhosting L. Without SSH, check the user list under Users in wp-admin or in the wp_users table in phpMyAdmin, and paste fresh keys from the WordPress key generator into wp-config.php. For the evidence copy below, export the database with phpMyAdmin and download the files over SFTP or FTP, or ask your host for a copy.
cd ~/public_html # your web root
wp user list --fields=ID,user_login,user_email,user_registered,roles --skip-plugins --skip-themes # every account: note unknown ones, delete them after the evidence copy below
wp config shuffle-salts # new keys: everyone is logged out--skip-plugins --skip-themes keeps WP-CLI from running the infected plugin and theme code. Files in wp-content/mu-plugins still load, so check that folder by hand. Never run wp as root.
New salts end logged-in sessions, nothing more. They do not stop someone with SFTP or database access or a backdoor. In each user profile, check the Application Passwords: you see when and from which IP each was last used, and can revoke them one by one. Turn on two-factor login for administrators, for example with the Two Factor plugin from WordPress.org. Once the site is clean, change every password again, as the WordPress FAQ asks.
Keep the evidence, and find the clean backup
Before you clean anything, copy files and database as they are now to a place outside the web root, and label the copy "infected":
cd ~ # the folder that contains your web root
mkdir -p ~/evidence
wp db export ~/evidence/infected-$(date +%F).sql --path=public_html
tar czf ~/evidence/infected-files-$(date +%F).tar.gz public_html # your web rootWithout SSH, export the database in phpMyAdmin and download all files over SFTP into a local folder named "infected". Now delete the unknown accounts you noted, under Users in wp-admin or with wp user delete.
Ask your host for the access and error logs. They show the attacker's IP, the time and the actions they may have taken.
Then find the earliest sign of the hack. Search Console's Security issues report shows when Google first detected it, and logs and file dates show more. The hardening guide's example: with weekly snapshots, a site hacked on May 1st and noticed on May 12th still has clean backups from before May 1st. Compare one with the current files, as in Google's malware guide:
diff -qr public_html/ backups/2026-10-01/Restoring a clean backup is not the end. Install the updates, close the hole and change the passwords once more.
Find the way in
Google's common causes and the hardening guide name these:
- An outdated core, plugin or theme with a published advisory. For WordPress itself, the hardening guide says that once a release fixes a hole, the information needed to exploit it is "almost certainly in the public domain."
- A stolen, guessed or reused password.
- An infected administrator computer.
- A "free" copy of a paid plugin or theme. Get both from WordPress.org or well-known companies only.
- Plugins and themes nobody uses or maintains any more.
There may be more than one hole: Google warns of "multiple, independent hacks". CERT.at writes that updating the affected software, especially all plugins and themes, "is often enough". If you cannot find the cause, write that into your incident report instead of guessing. Cleaning up without closing the way in only buys time.
Clean up: replace, do not patch by hand
Find out which WordPress version your site runs (wp core version, or $wp_version in wp-includes/version.php) and download exactly that version from wordpress.org. Delete /wp-admin and /wp-includes on the server, then upload the fresh folders over SFTP: uploading on top of the old folders leaves the attacker's extra files in place, the same problem as the Re-install button. Then check:
cd ~/public_html # your web root
wp core verify-checksums --include-root # also flags unknown files in the root
wp plugin verify-checksums --all --skip-plugins --skip-themes # WordPress.org plugins only
wp cron event list --skip-plugins --skip-themes # any scheduled job you do not recognise?
find wp-content/uploads -name '*.php' # uploads should hold media, not code--include-root needs WP-CLI 2.8 or newer (wp cli version). On older versions, drop the flag and compare the root folder with the fresh download by hand.
Reinstall plugins and themes from WordPress.org or the vendor. Delete what you do not use, files included: deactivating is not enough. Check .htaccess, index.php, the wp-content/mu-plugins folder and the theme's header.php, footer.php and functions.php by hand. In wp-config.php, look for included files you did not add. Google suggests searching for eval, base64_decode and unescape:
grep -rnE 'eval\(|base64_decode\(|unescape\(' wp-content wp-config.php .htaccessA hit is not proof, but each one deserves a look.
This line in wp-config.php turns off the dashboard file editor, "often the first tool an attacker will use":
define( 'DISALLOW_FILE_EDIT', true );Check the user list again. Only then update core, plugins and themes to current versions: clean with the same version, update once clean. For old PHP, see our PHP 8.2 guide.
Were personal data affected?
What does your site store? Contact form entries, shop orders, customer accounts, newsletter lists? The GDPR calls a security breach that affects such data a "personal data breach" (Art. 4(12)).
Article 33(1) says the controller notifies the supervisory authority "without undue delay and, where feasible, not later than 72 hours after having become aware of it", unless the breach "is unlikely to result in a risk to the rights and freedoms of natural persons." A later notice must give reasons for the delay. Information that is not available yet may follow in phases, without undue further delay (Art. 33(4)). Under Article 34, affected people must be told without undue delay if the breach is likely to result in a high risk to them. Every such breach has to be documented anyway (Art. 33(5)): facts, effects, remedial action.
In Austria the authority is the Datenschutzbehörde. It takes notices by online form, PDF form, email or post, and states that the controller assesses the risk itself. We cannot make that call for you. Ask your data protection contact or legal adviser.
Win back trust: Google, mail, visitors
Bring the cleaned site back online and lift the 503: Google lists "Brought your clean site back online" among the prerequisites for a review. Then verify your site in Search Console, open the Security issues report and fix every listed issue across the whole site: fixing some pages earns no partial return. Check pages from outside with cURL, once with a Google referrer and once without:
curl -v --referer "https://www.google.com" --user-agent "Mozilla/5.0" https://www.example.com/
curl -v --user-agent "Mozilla/5.0" https://www.example.com/robots.txt and noindex must not block the cleaned pages. Then click "Request review" and describe the problem, the fix and the result, as in Google's example: "For Content injection hacked URLs, I removed the spam content and corrected the vulnerability by updating an out-of-date plugin." Google's durations: up to several weeks for spam, a few days for malware, about a day for phishing. Once Google finds the site clean, the warnings are removed within 72 hours. Do not resubmit before the decision. Sites that keep flipping between clean and infected become Repeat Offenders and can request a review only after 30 days. Let pages the attacker created return 404: they drop out of Google's index over time, faster if you submit them to the URL removal feature in Search Console.
Check that mail from your domain still arrives. Blocklists flag the website's IP address, which is often the server your mail goes through. If CERT.at, the Austrian national CERT, has emailed you, its "Emails From Us" page explains the case. CERT.at coordinates the response to significant attacks on Austrian infrastructure, but is, in its own words, "not a public IT helpdesk".
When to call us
Fixing a site yourself takes reading code, command-line server tools and possibly web server configuration. If you are unsure, CERT.at recommends your IT service provider.
That is what our help after a breach is for. We contain the attack, clean up, close the hole it came through and explain in plain words what happened and what we changed. Call +43 7235 233 85, Monday to Friday 8:00 to 15:00, Saturday and Sunday 9:00 to 12:00, except on public holidays. The people who do the work pick up, and if we cannot answer straight away, we call back. Otherwise send us what you see through the contact form.
For the time after, our ongoing care under bugfixing, maintenance and optimization covers updates tested on a copy first, backups we test by restoring them, errors reported to our own error monitoring (GlitchTip) and a regular written report. Or you get in touch when something comes up, with no minimum commitment. Not hacked, just uneasy? For our free website check we need no access, and you get the 5 findings that matter most in writing within 3 working days.
More perspectives
Engineering
Why we build WebSocket-first
Guide
PHP 8.2 end of life: move WordPress to PHP 8.4 without the critical error
Guide
What a security check covers, and what it does not
Guide
Website relaunch: when it pays off and how existing URLs keep working
Operations
Docker by default, Traefik on top
Operations
Linux everywhere
Business model
Open source by default
Workflow
How we work with AI
Trust
Every minute, with a note
Product · Craft
We build for the morning user
How we engage
Built with startups
Curious how this plays out in practice?
These essays are the why. The how shows up in the projects we ship. Drop us a note and we can talk about your specific case.
