
Introduction: Real Talk from a Real Developer
Hey there, I’m Adnan Buksh, a freelance WordPress developer who’s been building, fixing, and securing WordPress sites for doctors, lawyers, ecommerce stores, coaches, clinics, and even recruitment agencies.
Over the years, I’ve handled everything from building a landing page to removing malware, cleaning up infected CPanels, and saving websites that were literally blacklisted by Google.
If you’re a developer, agency, or even a business owner trying to secure your site from hackers, you’re in the right place. This is not just theory. It’s the exact security stack and SOPs I use on every client project.
Because cleaning a hacked WordPress site costs 10x more than securing it from the start.
How Hackers REALLY Hack WordPress (Real Stories from My Clients)
1. Brute Force on /wp-login.php and xmlrpc.php
Bots run nonstop scripts trying 1000s of passwords. One of my yoga studio clients had 15,000 login attempts in 1 day. No 2FA, no CAPTCHA, no limits. Just open gates.

2. CPanel Brute Force (The Real Estate Nightmare)
A client in real estate had no 2FA on CPanel. Hacker used a brute force bot, got in, uploaded PHP shells, and started sending out spam from the domain.

3. SQL Injection via Vulnerable Plugins
Many cheap or free plugins (sliders, popups, contact forms) don’t sanitize input. I’ve seen entire sites compromised due to a single plugin exploit.


4. REST API User Leak + Author ID Exposure
URLs like ?author=1 expose usernames. Combine that with weak passwords? Easy target. REST API leaks can also show user IDs and usernames by visiting:
/wp-json/wp/v2/users

This publicly returns all author slugs (often the login username) unless restricted. Hackers use this info to target specific usernames in brute-force login attacks.
5. Plugin Theme Backdoors & nulled scripts
Many sites install premium themes or plugins from non-official sources. These often come with obfuscated PHP backdoors that execute remote code or create new admin accounts silently.

6. wp-config.php Exploits via File Permissions
Improper file permissions (777) or shared hosting environments allow attackers to inject or read sensitive credentials from wp-config.php. I’ve seen attackers modify database credentials and redirect sites using this file.
7. Malware in Uploads via Contact Forms
If contact forms or upload fields are not validated, attackers can upload PHP shells disguised as images or PDFs. Once uploaded, the shell gives full access to the server environment.
The Security Stack I Use on Every WordPress Project
Whether I’m building a clinic website, ecommerce site, or agency blog, here’s my bulletproof stack:Before You Use the Honeypot Trap Code
Make sure you’ve changed the default login path using the WPS Hide Login plugin. This ensures real users can’t reach wp-login.php or wp-admin, and bots are redirected to your honeypot trap.

1. Honeypot Trap + Smart XML-RPC Blocker (Custom Code)
I create a fake login page (/site-login). If a bot submits login data or triggers a fake reset, I block the IP. After 2 failed attempts, they’re gone.
- Doesn’t interfere with WooCommerce or real login pages
- Doesn’t return 404 (no SEO penalty)
- Auto-disables
xmlrpc.phpafter 7 days of inactivity
Add WPCode Lite Plugin and add this script on PHP code type and Select Location Run Everywhere
/**
* Plugin Name: Honeypot Trap + Smart XML-RPC Blocker
* Description: Blocks brute-force bots via fake login trap and disables unused XML-RPC endpoint after 7 days. Safe fallback (no 404). Safe for WooCommerce & SEO.
* Version: 1.0
* Author: Adnan Buksh
*/
$honeypot_slug = 'site-login';
$option_name = 'honeypot_blocked_ips';
// Get Real Visitor IP
function abp_get_real_ip() {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) return $_SERVER['HTTP_CF_CONNECTING_IP'];
if (!empty($_SERVER['HTTP_X_REAL_IP'])) return $_SERVER['HTTP_X_REAL_IP'];
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) return explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
return $_SERVER['REMOTE_ADDR'];
}
// Honeypot login trap (no 404 fallback)
add_action('parse_request', function ($wp) use ($option_name, $honeypot_slug) {
$ip = abp_get_real_ip();
$now = time();
$path = trim($wp->request, '/');
// Skip WooCommerce safe routes
$safe_paths = ['my-account', 'cart', 'checkout', 'shop', 'account'];
foreach ($safe_paths as $safe) {
if (stripos($path, $safe) === 0) return;
}
// Remove expired blocked IPs (after 3 days)
$blocked_ips = get_option($option_name, []);
foreach ($blocked_ips as $blocked_ip => $timestamp) {
if ($now - $timestamp > 3 * DAY_IN_SECONDS) unset($blocked_ips[$blocked_ip]);
}
update_option($option_name, $blocked_ips);
// Blocked or trapped cookie? Deny access
if (isset($blocked_ips[$ip]) || isset($_COOKIE['honeypot_trapped'])) {
status_header(403);
exit('403 Forbidden — You are blocked.');
}
// Trigger honeypot page
if ($path === $honeypot_slug) {
$is_bot = (
($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['log'], $_POST['pwd'])) ||
isset($_GET['lost'])
);
if ($is_bot) {
$attempts = isset($_COOKIE['honeypot_attempts']) ? (int)$_COOKIE['honeypot_attempts'] : 0;
$attempts++;
setcookie('honeypot_attempts', $attempts, time() + HOUR_IN_SECONDS, '/');
if ($attempts >= 2) {
$blocked_ips[$ip] = $now;
update_option($option_name, $blocked_ips);
setcookie('honeypot_trapped', '1', time() + YEAR_IN_SECONDS, '/');
status_header(403);
exit('403 Forbidden — You have been blocked.');
}
}
// Send proper headers
nocache_headers();
// Show trap login page (fake)
echo '<!DOCTYPE html><html lang="en-US"><head><meta charset="UTF-8"><title>Log In ‹ Example Site — WordPress</title>
<meta name="robots" content="noindex, nofollow" />
<link rel="icon" href="https://s.w.org/favicon.ico" sizes="32x32" />
<link rel="stylesheet" href="https://s.w.org/wp-includes/css/dashicons.min.css">
<link rel="stylesheet" href="https://s.w.org/wp-admin/css/forms.min.css">
<link rel="stylesheet" href="https://s.w.org/wp-admin/css/login.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>body {background:#f1f1f1;font-family:sans-serif;}
#login {width:320px;padding:40px;background:#f1f1f1;}
.login .button-primary {float: right; padding: 6px;}
input, textarea { background: #2271b1; border-color: #2271b1; color: #fff; }</style>
</head><body class="login no-js login-action-login wp-core-ui">
<script>document.body.className = document.body.className.replace("no-js","js");</script>
<div id="login"><h1 class="wp-login-logo"><a href="https://wordpress.org/">Powered by WordPress</a></h1>
<form name="loginform" id="loginform" action="" method="post">
<p><label for="user_login">Username or Email Address</label><input type="text" name="log" id="user_login" class="input" value="" size="20" autocomplete="username" required></p>
<div class="user-pass-wrap"><label for="user_pass">Password</label><div class="wp-pwd">
<input type="password" name="pwd" id="user_pass" class="input password-input" value="" size="20" autocomplete="current-password" required>
<button type="button" class="button button-secondary wp-hide-pw hide-if-no-js" data-toggle="0">
<span class="dashicons dashicons-visibility"></span></button></div></div>
<p class="submit"><input type="submit" class="button button-primary button-large" value="Log In"></p></form>
<p id="nav"><a href="?lost=1">Lost your password?</a></p>
<p id="backtoblog"><a href="#">← Go to Example Site</a></p></div>
<script>document.querySelector(".wp-hide-pw").addEventListener("click", function(){
var i=document.getElementById("user_pass"),e=this.querySelector("span");
"password"===i.type?(i.type="text",e.classList.add("dashicons-hidden")):(i.type="password",e.classList.remove("dashicons-hidden"))});</script>
</body></html>';
exit;
}
});
// Admin IP Unblocker Page
add_action('admin_menu', function () use ($option_name) {
add_management_page('Honeypot Unblocker', 'Honeypot Unblocker', 'manage_options', 'honeypot-unblocker', function () use ($option_name) {
if (!current_user_can('manage_options')) wp_die('Unauthorized');
$blocked_ips = get_option($option_name, []);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['unblock_ip'])) {
$ip = sanitize_text_field($_POST['unblock_ip']);
unset($blocked_ips[$ip]);
update_option($option_name, $blocked_ips);
echo '<div class="updated"><p>Unblocked IP: ' . esc_html($ip) . '</p></div>';
}
echo '<div class="wrap"><h1>Honeypot Unblocker</h1>';
if (empty($blocked_ips)) {
echo '<p>No blocked IPs.</p>';
} else {
echo '<ul>';
foreach ($blocked_ips as $ip => $ts) {
echo '<li>' . esc_html($ip) . ' (blocked ' . human_time_diff($ts) . ' ago)</li>';
}
echo '</ul>';
}
echo '<h2>Unblock an IP</h2><form method="post">
<input type="text" name="unblock_ip" placeholder="Enter IP" required>
<button class="button button-primary">Unblock</button></form></div>';
});
});
// Prevent REST API user leak
add_filter('rest_endpoints', function ($endpoints) {
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/users/(?P<id>[\\d]+)']);
return $endpoints;
});
// Block ?author=1 scans
add_action('init', function () {
if (!is_admin() && isset($_GET['author'])) {
wp_redirect(home_url(), 301);
exit;
}
});
// XML-RPC Smart Auto Block after 7 days of inactivity
add_filter('xmlrpc_enabled', '__return_true');
add_action('init', function () {
$flag_used = 'xmlrpc_used_once';
$flag_started = 'xmlrpc_check_started';
$now = time();
if (get_option($flag_used)) return;
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], 'xmlrpc.php') !== false) {
update_option($flag_used, 1);
$ip = $_SERVER['REMOTE_ADDR'];
$ua = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$log = sprintf("[%s] XML-RPC accessed by %s | UA: %s\n", date('Y-m-d H:i:s'), $ip, $ua);
error_log($log, 3, WP_CONTENT_DIR . '/xmlrpc-access.log');
return;
}
if (!get_option($flag_started)) {
update_option($flag_started, $now);
return;
}
if (($now - (int)get_option($flag_started)) < 7 * DAY_IN_SECONDS) return;
// Disable XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
add_filter('xmlrpc_methods', function ($methods) {
unset($methods['system.multicall']);
return $methods;
});
add_action('init', function () {
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], 'xmlrpc.php') !== false) {
status_header(403);
exit('403 Forbidden: xmlrpc.php has been auto-blocked after 7 days of no use.');
}
}, 99);
});
This code is safe, production-ready, and built for real-world projects.
2. Add Google reCAPTCHA to Login Page (via Wordfence)
Wordfence (free version) makes it easy to add reCAPTCHA v3 to login, registration, and even forgot password forms.
- Stops bots
- Doesn’t affect real users

3. Login Limits + Lockouts
With Wordfence or iThemes Security:
- After 3 failed attempts, IP is blocked for 24 hours
- Protects against brute force

4. Weekly Malware Scans (Plugins, Themes, DB)
- Wordfence scans plugins, themes, and even database entries
- iThemes or Sucuri can also help detect modifications

5. Disable REST API User Endpoints
Add this snippet:
add_filter('rest_endpoints', function ($endpoints) {
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/users/(?P<id>[\\d]+)']);
return $endpoints;
});
6. Prevent Author Enumeration
Block URL scans like ?author=1 which reveal usernames:
add_action('init', function () {
if (!is_admin() && isset($_GET['author'])) {
wp_redirect(home_url(), 301);
exit;
}
});
Hosting & CPanel-Level Security
This is where most freelancers go silent. But if you’re serious about site protection, go beyond WordPress:
1. Enable Imunify360 via Hosting Provider
Ask your hosting support to turn on Imunify360 (many shared hosts offer it by default):

- Blocks malware uploads
- Real-time file protection
- Smart firewall
2. Always Enable 2FA for CPanel Logins
If 2FA is supported, enable it immediately. Most CPanel hacks happen because:
- No 2FA
- Default passwords
- Admin panel URL (
domain.com:2083) is exposed
3. Server-Level Malware Detection (e.g., ClamAV + E-Mail Notifications)

Ask your host to install malware scanners like ClamAV or Monarx and configure them to send reports daily.
Security Tools I Personally Trust in 2025
| Tool | Purpose |
|---|---|
| Wordfence | Firewall + login security + scanning |
| WPScan | Plugin/theme vulnerability scan |
| Sucuri Scanner | Malware + blacklist checker |
| iThemes Security | Brute force protection + hardening |
| Imunify360 | Server-level AI firewall |
Final Thoughts from Adnan Buksh
Security isn’t a plugin. It’s a freelancer mindset. Every WordPress site I develop includes full security hardening:
- Whether it’s a clinic website, custom ecommerce build, or blog for a lawyer
- Security is part of the package — not an upsell
Because in this world, bots don’t sleep — and your website shouldn’t be their playground.
If you’re a developer, feel free to reuse this workflow for your own clients.
If you’re a business owner? Contact me to secure or clean your WordPress site.
Stay sharp.
— Adnan Buksh, Freelance WordPress Developer
Leave a Reply