How to protect a site from being hacked
Most site break-ins are automated: bots scan millions of domains looking for known vulnerabilities in specific CMS and plugin versions, they are not targeting you personally. Closing typical holes before a scanner finds them is cheaper than restoring a site after an infection.
How attackers get initial access
A break-in almost never starts with brute-forcing the administrator's password directly — that is too slow once login attempt limits are on. The real vectors are usually different: stolen FTP credentials from a developer's infected computer, a known vulnerability in a specific plugin version that was not updated in time, or a phishing email that put the control panel password into the wrong hands. The defence against each of these vectors is different, so one measure alone is not enough — you need a set of barriers.
Basic rules
- Use strong passwords for cPanel, FTP and the database
- Update the CMS, themes and plugins regularly
- Remove unused plugins and themes
- Enable two-factor authentication in billing
What an attack looks like in server logs
Before changing anything on the site, bots scan it for typical entry points. In access.log this looks like a series of requests for files you might not even have:
1.2.3.4 - - [30/Sep/2026:03:14:11] "GET /wp-config.php.bak HTTP/1.1" 404
1.2.3.4 - - [30/Sep/2026:03:14:12] "GET /.env HTTP/1.1" 404
1.2.3.4 - - [30/Sep/2026:03:14:13] "GET /xmlrpc.php HTTP/1.1" 200
1.2.3.4 - - [30/Sep/2026:03:14:14] "POST /wp-login.php HTTP/1.1" 200
A 404 on the first two lines is fine — the files are protected by not existing. But a 200 on xmlrpc.php and wp-login.php means those entry points are reachable and can be brute-forced — they are exactly what you need to close first.
WordPress protection
- Install the Wordfence or Sucuri plugin
- Change the default /wp-admin login URL
- Limit the number of login attempts
- Disable XML-RPC if you do not use it
Wordfence versus Sucuri: how they differ
| Feature | Wordfence (free plan) | Sucuri (free plan) |
|---|---|---|
| File antivirus scanner | Yes | Yes |
| Application-level firewall | Yes, runs inside WordPress | Limited without the cloud proxy |
| Country-level blocking | Yes | No on the free plan |
| File change notifications | Yes | Yes |
Wordfence runs inside WordPress itself and cannot stop an attack before the request reaches PHP; a full WAF in front of the server, such as ModSecurity or Cloudflare, filters traffic earlier.
Both plugins consume server resources on every request: the file scanner regularly rereads the site's files, and the firewall inside WordPress adds a check before the page's main code runs. On a modest plan with high traffic this can show up in response time — it is offset by moving the filtering to the CDN level, where it does not consume your server's resources at all.
File protection
# Deny access to .htaccess
<Files .htaccess>
Order allow,deny
Deny from all
</Files>
If you suspect the site is already infected, the first practical step is to find files changed in the last few days, when normally nobody was touching the code:
# Files changed in the last 7 days
find /var/www/site -type f -mtime -7 -name "*.php"
# Suspicious constructs in PHP files (not a verdict, but worth a manual check)
grep -rl "eval(base64_decode" /var/www/site --include="*.php"
A match on eval(base64_decode does not always mean an infection — some legitimate libraries use similar constructs for license checks, but in the vast majority of cases on a real site it is a sign of a web shell left behind after a break-in.
When plugins alone are not enough
- A zero-day vulnerability in the CMS itself — a security plugin will catch suspicious activity after the fact, but it will not close a hole for which no signature exists yet.
- A compromised administrator password — two-factor authentication and strong passwords matter more than any file scanner.
- Vulnerable code in the site's custom part — antivirus plugins look for known malicious-code signatures, not logic errors in your own PHP.
Checking that the protection works
- Request
curl -I https://your-domain/wp-config.php.bak— it should return404, not200. - Check that
/wp-adminreally redirects to the new login address instead of serving the old form. - Try entering a wrong password 5-6 times in a row — an IP-based lockout or a delay before the next attempt should kick in.
- Request
curl -I https://your-domain/xmlrpc.php— if XML-RPC is disabled, the response should be403or404, not200.
What to do if the site does get hacked anyway is covered in recovering a hacked site, and the full baseline server security checklist is in server security checklist.