Bots Arrive Within 24 Hours — Security P0 Before Domain Disclosure

Written by

in

Self-hosting guide, part 8. Essential 25 minutes to complete before launching the domain.

← Previous: Part 7 — Naming is Difficult (Choosing a Site Name)
→ Next: Part 9 — Invisible Bridge: Cloudflare Tunnel


TL;DR

  • Disclosing a WordPress domain = bots arrive within 24 hours for brute force attacks. A statistical fact.
  • A 25-minute task can block 99%: ① Avoid username admin ② Block xmlrpc ③ Change wp-login slug.
  • These three constitute security P0. Complete this before disclosing your domain.
  • Wordfence and 2FA are P1 (to be added after domain launch).

1. The Truth of the First 24 Hours

When a WordPress site is exposed to the internet, within the first 24 hours:

Attack Frequency Target
/wp-login.php brute force Thousands per hour Admin password
/xmlrpc.php pingback amp Continuous DDoS amplification
Scanning for known plugin vulnerabilities Continuous Outdated plugins
SQL injection attempts Automatic bots ?id=1 OR 1=1 URL
User enumeration (?author=1) Automatic bots Discovering usernames

The goal here is not just to block but to eliminate the surface itself, which is P0.


2. P0 — Three Security Setup

Security P0: the three doors bots knock on, and how to remove them Three columns. Column one: bots brute force passwords against the username admin; P0 step 1, avoiding the username admin, means the site has no admin account, so guessing goes nowhere. Column two: bots send POST /xmlrpc.php to try thousands of passwords in a single request; P0 step 2, blocking xmlrpc.php with a mu-plugin, returns 403 immediately so amplification is not possible. Column three: bots hit GET /wp-login.php, the login URL common to every WordPress site; P0 step 3, changing the login slug with the wps-hide-login plugin, sends them to a fake URL with a 301, and /wp-admin/ returns 404. Note below: user enumeration via ?author=1 is blocked automatically by Rank Math’s Disable Author Archives. Security P0: the three doors bots knock on first Finish before disclosing the domain. Not blocking attacks, but removing the surface itself. Bot attempt Guess passwords for admin Top brute force target Bot attempt POST /xmlrpc.php Thousands of guesses per request Bot attempt GET /wp-login.php Same login URL on every WordPress P0 step ① Avoid username admin Never type admin at install Already there? New admin, delete old P0 step ② Block xmlrpc.php Forced on via a mu-plugin xmlrpc_enabled → false P0 step ③ Change wp-login slug wps-hide-login plugin Login moves to a secret slug What the bot gets No admin account Guessing goes nowhere What the bot gets 403, blocked at once No amplification What the bot gets 301 → fake URL /wp-admin/ returns 404 User enumeration via ?author=1 is blocked by Rank Math (Disable Author Archives), see section 6
Figure 1. Security P0. Remove the three places bots knock on first before disclosing the domain, and all they get back is a dead end, a 403, or a fake URL.

3. ① Avoiding Username Admin

When creating the first user with the WordPress installation wizard, do not enter admin. Bots primarily target this username for brute force attacks.

Good examples: jamjam, sailor904, personal nicknames
Bad examples: admin, administrator, root, wpadmin

If you’ve already created an admin user, create a new user via wp-cli, then revoke admin rights or delete the old user:

wp user create newname [email protected] --role=administrator --user_pass=$(openssl rand -base64 24) --allow-root
wp user delete admin --reassign=newname --allow-root

4. ② Blocking xmlrpc.php — mu-plugin

/xmlrpc.php is an old API for WordPress, rarely used (only by Jetpack users). Bots exploit it for brute force amplification (attempting thousands of passwords with a single request).

Create /var/www/html/wp-content/mu-plugins/disable-xmlrpc.php:

<?php
/**
 * Plugin Name: Disable XML-RPC
 * Description: Block xmlrpc.php to reduce attack surface.
 */
if (!defined('ABSPATH')) exit;

add_filter('xmlrpc_enabled', '__return_false');
add_filter('wp_headers', function ($headers) {
    unset($headers['X-Pingback']);
    return $headers;
});
add_filter('xmlrpc_methods', function ($methods) {
    unset($methods['pingback.ping']);
    unset($methods['pingback.extensions.getPingbacks']);
    return $methods;
});
add_action('init', function () {
    $uri = $_SERVER['REQUEST_URI'] ?? '';
    if (stripos($uri, '/xmlrpc.php') !== false) {
        status_header(403);
        exit('xmlrpc disabled');
    }
});

Verification:

curl -I http://localhost:8090/xmlrpc.php
# HTTP/1.1 403  ← Success

5. ③ Changing wp-login Slug

/wp-login.php is the login URL common to all WordPress sites. Bots start automatically hitting this URL. It should be obscured with a different slug.

Install the plugin wps-hide-login:

wp plugin install wps-hide-login --activate --allow-root
wp option update whl_page starport --allow-root

starport is a secret slug chosen by me, difficult to guess yet easy to remember. For example: byeolgorae-gate, secret-door-2026, starport.

Verification:

curl -I -L http://localhost:8090/wp-login.php
# 301 redirect → Fake URL (bot fails there)

curl -I -L http://localhost:8090/wp-admin/
# 404 (Admin page appears nonexistent when logged out)

curl -I -L http://localhost:8090/starport/
# 200 (Login page known only to me)

⚠ If I forget the slug, I won’t be able to access it either. Bookmarking and note-taking are essential.


6. Results — From the Bot’s Perspective

Attack Response after Blocking
GET /wp-login.php 301 → Fake URL → Bot attempts there → Forever fails
POST /xmlrpc.php Blocked immediately with 403. Amplification not possible
GET /wp-admin/ 404. The admin page pretends to be nonexistent
Username brute force Attempting admin → Our site has no admin
?author=1 user enum Rank Math automatically blocks it (Disable Author Archives)

Resources expended by bots = Gains from my site = 0.


7. P0 vs P1 vs P2 — Prioritization

Stage Task Timing
P0 (Mandatory, before domain disclosure) Username, xmlrpc, wp-login slug This 25 minutes
P1 (Immediately after domain launch) Wordfence wizard + 2FA GUI operations by me
P2 (Monthly) Plugin security updates + WPA (vulnerability scan) Operational phase
P0, P1 and P2 in order, relative to domain disclosure A timeline running left to right. Before disclosure sits P0, mandatory (this 25 minutes: username, xmlrpc, wp-login slug). In the middle is domain disclosure; within 24 hours after it, bots arrive and start wp-login brute force. Right after launch comes P1 (Wordfence wizard and 2FA). On the right, in the operational phase, P2 (plugin security updates and WPA vulnerability scan) repeats every month. Launching without P0 means brute force attempts pile up in the logs and eat CPU. Spacing is not proportional to time. The order, relative to domain disclosure Spacing not to time scale Before disclosure Domain disclosed Bots arrive wp-login brute force begins Operational phase Within 24 hours Every month P0 Mandatory Before disclosure, this 25 minutes Username, xmlrpc, wp-login slug P1 Right after launch Wordfence wizard + 2FA P2 Monthly Plugin security updates + WPA vulnerability scan Launch without P0: bots start brute force within 24 hours, attempts pile up in the logs and eat CPU.
Figure 2. The order, relative to domain disclosure. P0 before disclosure, P1 right after launch, P2 every month while operating.

If I launch the domain without completing P0 — bots will arrive within 24 hours to start brute force attempts. Even without setup, it is impenetrable, yet logs will accumulate brute force attempts and consume mini-PC CPU. Preventing such occurrences is vital for mental well-being.


8. Traps

Trap 1. mu-plugin Auto Activation

mu-plugins run forced above regular plugins. Consequently, security policies, like blocking xmlrpc, that must never be disabled, are suitable for mu-plugins.

Trap 2. Forgetting WPS Hide Login Slug

Since the whl_page option is directly written into the DB, if I forget the slug, I won’t be able to access it either. Recovery method: wp option get whl_page --allow-root (via mini-PC SSH) or temporarily deactivate the mu-plugin.

Trap 3. Automatic Bypass After Authentication

WPS Hide Login only blocks those who do not know the slug. A logged-in session continues to use /wp-admin/. Once I log in, I have free access to the admin page for the next 30 minutes.

Trap 4. Entry with a New Slug Upon Logout

After I log out, the next login will be at /starport. Updating bookmarks is essential.


FAQ

Q. What if I already disclosed the domain but haven’t set up P0?
Immediately turn off cloudflared or disable the Tailscale Funnel → Set up P0 → Re-disclose. 5 minutes.

Q. Why is the Wordfence wizard (P1) not P0?
P0 removes the surface itself. Wordfence detects and blocks attacks while the surface is still present. It serves as a supplementary measure.

Q. Can I change the slug to /admin?
/admin is also a common slug targeted by bots. Not recommended. Choose a word that only I can think of, which is difficult to guess.

Q. Is P0 sufficient without 2FA?
Sufficient + entirely enough. 2FA acts as the final shield when passwords are compromised. P0 + 2FA = Effectively invulnerable.

Q. If I block xmlrpc, can I still use Jetpack?
Some Jetpack functionalities are disabled. It is irrelevant if Jetpack is not used at all. Our Star Whale does not utilize Jetpack.


Next Episode Preview

Part 9 — The Invisible Bridge: Cloudflare Tunnel. Finally connecting the domain to the mini-PC. No need to open router ports, without exposing the mini-PC IP, and employing a free WAF, DDoS protection, and CDN.


Summary

Disclosing a WordPress domain = Bots will arrive within 24 hours. A P0 setup (username, xmlrpc, wp-login slug) in 25 minutes can block 99%. Complete this before launching the domain.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *