• Indonesian
  • English
  • How I Blocked 5000+ WordPress Login Attempts per Hour —

    Kecepatan:

    Difficulty: Intermediate-Advanced
    Last Updated: July 20, 2026
    Tested On: LiteSpeed Enterprise, Apache 2.4, cPanel/VestaCP, CentOS 7/8, CloudLinux

    Introduction

    3:38 PM. I was sitting relaxed in the NOC room, sipping my coffee, when the Telegram monitoring went off: “Warning — Brute force detected, throttle active”. I checked the LiteSpeed log — two IPs were being throttled. But that was just the tip of the iceberg. When I opened the Apache error log, I found something way worse: “Too many open files: could not open transfer log file”.

    And it didnt stop there. I ran to the server and grepd through the WordPress login activity for the last hour. The result? Over 5,000 wp-login.php login attempts in one hour — from a single IP. And that was just one server.

    That wasnt a human. That was a bot. Thousands of automated login requests trying to brute force your WordPress admin account. And if you dont have extra protection, someday that bot will succeed — because even a “strong” password can be cracked given enough time.

    This story is about how I stopped this massive brute force attack, fixed the file descriptor exhaustion that was choking the error logs, and set up HTTP Basic Auth as a first line of defense before the WordPress login.

    Symptoms — What Was Happening?

    Three symptoms showed up simultaneously on the server that day:

    Symptom 1: LiteSpeed Brute Force Throttle

    The LiteSpeed log showed several IPs already detected doing brute force and automatically throttled:

    [root@mr-x ruak3711]# tail -f /usr/local/apache/logs/error_log
    2026-07-20 15:32:38.263607 [WARN] [3402173] [T0] [10.0.0.1:40442#APVH_client-a.com:443] Brute force detected for IP [10.0.0.1], throttle.
    2026-07-20 15:32:38.869189 [WARN] [3402173] [T0] [10.0.0.2:62269#APVH_site-lama-client.com:443] Brute force detected for IP [10.0.0.2], throttle.

    LiteSpeed does have a built-in brute force protection feature. But the problem is: throttling only slows requests down — it doesnt block them. The bots keep trying — slower, but still trying. And when you have thousands of bots from different IPs, throttling isnt enough.

    Symptom 2: Apache “Too Many Open Files”

    The Apache error log showed a different problem — file descriptor exhaustion:

    [Mon Jul 20 15:32:46.712781 2026] [log_config:error] [pid 3486344:tid 3486344] (24)Too many open files: AH00649: could not open transfer log file /etc/apache2/logs/domlogs/client-b.com.
    AH00015: Unable to open logs
    
    [Mon Jul 20 15:32:50.986574 2026] [log_config:error] [pid 3486614:tid 3486614] (24)Too many open files: AH00649: could not open transfer log file /etc/apache2/logs/domlogs/client-b.com.
    AH00015: Unable to open logs
    
    [Mon Jul 20 15:33:08.009242 2026] [log_config:error] [pid 3490205:tid 3490205] (24)Too many open files: AH00649: could not open transfer log file /etc/apache2/logs/domlogs/portal-news-client.com.
    AH00015: Unable to open logs
    
    [Mon Jul 20 15:36:00.576506 2026] [log_config:error] [pid 3521150:tid 3521150] (24)Too many open files: AH00649: could not open transfer log file /etc/apache2/logs/domlogs/portal-news-client.com.
    AH00015: Unable to open logs

    The (24)Too many open files error means Apache has hit the maximum file descriptors allowed by the operating system. When this happens, Apache cant open new log files — and in the worst case, cant serve requests at all.

    Affected domains: client-b.com, portal-news-client.com. Meaning those websites were inaccessible or at least their logging was broken.

    Symptom 3: Massive wp-login.php Brute Force

    This was the scariest one. When I grepd WordPress login activity over the last hour:

    [root@brantas ruak3711]# grep -r POST /wp-login.php /usr/local/apache/logs/domlogs/ | grep -E "$(date +%d/%b/%Y:%H:)|$(date -d 1 hour ago +%d/%b/%Y:%H:)" | awk {print $1} | cut -d: -f2 | sort | uniq -c | sort -nr | head -n 5
       5046 10.0.0.2
       4734 10.0.0.3
       4652 10.0.0.4
       4642 10.0.0.5
       4166 10.0.0.6

    Look at those numbers: 5,046 login attempts in one hour from a single IP. Thats an average of 84 login attempts per minute, or nearly 1.5 per second. And that was just the top 5 IPs — there were likely dozens or hundreds more doing the same thing.

    Total login attempts from just these 5 IPs: 23,240 attempts in one hour. If 100 IPs were doing the same thing, thats 464,800 login attempts per hour. At this rate, brute force will crack a weak password within hours.

    Root Cause — What Went Wrong?

    1. Brute Force: No Protection in Front of WordPress Login

    WordPress login (wp-login.php) is the main gateway to the WordPress dashboard. Without extra protection, anyone can access this page and try to log in as many times as they want. LiteSpeed does have throttling, but throttling only slows things down — it doesnt block. And when the attack comes from many IPs (distributed brute force), throttling becomes ineffective.

    2. File Descriptor Exhaustion: Too Many Simultaneous Requests

    When brute force hammers the server with thousands of requests per minute, Apache has to open many connections at once. Each connection requires a file descriptor (for sockets, log files, etc.). If the number of requests exceeds the ulimit -n limit configured for Apache, the OS refuses to open new files — and the Too many open files error appears.

    This isnt just a logging problem. If Apache cant open log files, it might also fail to open config files, SSL certificates, or even serve HTTP requests. In the worst case, every website on the server can go down.

    3. The Combination of Both Problems

    Massive brute force → thousands of simultaneous connections → file descriptors exhausted → Apache cant open log files → cascading errors → websites down. This is a chain reaction that starts from one single point: no protection in front of wp-login.php.

    Step-by-Step Solution — HTTP Basic Auth for wp-login.php

    SECURITY WARNING: Before Changing Apache Config

    Modifying pre_virtualhost_global.conf is a high-risk action. If the config is wrong, every website on the server goes down. Before proceeding:

    1. Back up the existing configcp pre_virtualhost_global.conf pre_virtualhost_global.conf.bak
    2. Test config before restarting — always run apachectl configtest before restart
    3. Make sure you have console/terminal access — if the config is wrong, websites go down and you cant access via browser
    4. Use HTTPS — HTTP Basic Auth sends username/password in base64 (not encryption). Without HTTPS, credentials can be intercepted
    5. Note down your htpasswd password — if you forget it, youre locked out of wp-login.php

    Step 1: Create the htpasswd File

    The htpasswd file stores the username and password for HTTP Basic Auth. Create it in a safe location (outside the document root):

    # Create htpasswd file with username admin
    htpasswd -cb /home/wp-admin-attack-htpasswd-file admin123
    # Enter password when prompted

    Example htpasswd file content (encrypted):

    admin123:$apr1$xxxx$x.xxxxxxxxxxxxxxxxxxxx

    Important: Dont place the htpasswd file in the document root (e.g. /home/user/public_html/). If this file is publicly accessible, an attacker can read the username (even though the password is encrypted).

    Step 2: Edit pre_virtualhost_global.conf

    This file is located at /usr/local/apache/conf/includes/pre_virtualhost_global.conf. Add the following block at the end of the file:

    #BEGIN BLOCK-xmlrpc
    
    <Files "xmlrpc.php">
        Require all denied
        Require ip 122.248.245.244/32
        Require ip 54.217.201.243/32
        Require ip 54.232.116.4/32
        Require ip 192.0.80.0/20
        Require ip 192.0.96.0/20
        Require ip 192.0.112.0/20
        Require ip 195.234.108.0/22
        Require ip 192.0.64.0/18
    </Files>
    
    #END BLOCK-xmlrpc
    
    #BEGIN BLOCK-WP-ADMIN-ATTACK
    #
    <Files wp-login.php>
    AuthType basic
    AuthName "To prove you are not a robot, please enter your username and password below:"
    AuthBasicProvider file
    AuthUserFile /home/wp-admin-attack-htpasswd-file
    Require valid-user
    ErrorDocument 401 "Authentication required"
    </Files>
    
    #
    # END BLOCK-WP-ADMIN-ATTACK

    Explanation of this block:

    • <Files wp-login.php> — this rule only applies to the wp-login.php file, not other WordPress files
    • AuthType basic — uses HTTP Basic Authentication (browser shows a login popup)
    • AuthBasicProvider file — credentials are fetched from the htpasswd file
    • Require valid-user — only users listed in htpasswd can access the page
    • ErrorDocument 401 — message shown when authentication fails
    • xmlrpc block — only allows specific IPs to access xmlrpc.php (WordPress.com, Jetpack, etc.)

    Step 3: Test Config & Restart Apache

    Dont restart right away. First, check if the config is valid:

    # Test if config is valid
    apachectl configtest
    # Expected output: Syntax OK

    If the output is Syntax OK, restart Apache:

    # Restart Apache (graceful to avoid dropping active connections)
    apachectl graceful
    # or
    systemctl restart httpd
    WARNING:

    If configtest returns an error, DO NOT restart. Fix the error first. If you restart with a bad config, every website on the server will go down until the config is fixed.

    Step 4: Verify Protection Is Active

    Open your browser and go to https://your-domain.com/wp-login.php. You should see an HTTP Basic Auth login popup before the WordPress login page appears:

    • Enter the htpasswd username and password you created
    • If correct, the WordPress login page will appear as normal
    • If wrong or cancelled, youll see the “Authentication required” message

    Now try accessing wp-login.php without credentials — youll be rejected. Brute force bots will be rejected too, because they dont know your HTTP Basic Auth username and password.

    Step 5: Fix File Descriptor Exhaustion

    While protecting wp-login.php stops the brute force, you also need to fix the Too many open files issue. Check the current limit:

    # Check file descriptor limit for Apache
    cat /proc/$(pgrep -f "apache2|httpd" | head -1)/limits | grep "Max open files"

    If Max open files is too low (e.g. 1024 or 4096), increase it:

    # Edit limits.conf
    nano /etc/security/limits.conf
    
    # Add at the end of the file:
    apache hard nofile 65535
    apache soft nofile 32768
    * hard nofile 65535
    * soft nofile 32768

    If using systemd, also edit the Apache service file:

    # Edit systemd service
    systemctl edit httpd
    
    # Add:
    [Service]
    LimitNOFILE=65535

    After that, restart Apache:

    systemctl daemon-reexec
    systemctl restart httpd

    Pro Tips & Warnings

    • Always use HTTPS — HTTP Basic Auth sends credentials in base64 (not encryption). Without HTTPS, an attacker can read your username and password from network traffic. Make sure SSL is active on the server
    • Dont use weak passwords in htpasswd — if an attacker knows youre using HTTP Basic Auth, theyll try to brute force htpasswd too. Use a password at least 16 characters long with a mix of letters, numbers, and symbols
    • Restrict IP access to xmlrpc.php — the xmlrpc block above only allows WordPress.com and Jetpack IPs. If you dont use Jetpack, block everything: Require all denied
    • Monitor error logs regularly — dont wait for errors to appear on their own. Run tail -f error_log at least once a day to catch issues early
    • Use Fail2Ban as an additional layer — HTTP Basic Auth protects wp-login.php, but Fail2Ban can permanently block IPs after a few failed attempts
    • Back up config before editing — always backup pre_virtualhost_global.conf before making any changes

    FAQ

    Q: Can HTTP Basic Auth be cracked?

    HTTP Basic Auth sends the username and password in base64 encoding (not encryption). If an attacker can monitor traffic (man-in-the-middle), they can read those credentials. Thats why HTTPS is mandatory — HTTPS encrypts all traffic, including HTTP Basic Auth credentials. Without HTTPS, HTTP Basic Auth only protects against bots that cant decode base64 (most bots cant, but human attackers can).

    Q: What if I forget my htpasswd password?

    If you forget the password, you can recreate the htpasswd file with the same username:

    # Recreate htpasswd file (will replace the old one)
    htpasswd -cb /home/wp-admin-attack-htpasswd-file admin123
    # Enter new password when prompted

    After that, restart Apache (apachectl graceful) so the config is reloaded. You dont need to reboot the server — just restart the Apache service.

    Q: Does HTTP Basic Auth affect server performance?

    Not significantly. HTTP Basic Auth only does one extra check: reading the htpasswd file and comparing the password hash. This process takes microseconds — far faster than a single WordPress database request. Server performance wont be affected by HTTP Basic Auth.

    Q: Why use pre_virtualhost_global.conf instead of .htaccess?

    Because the <Files> directive cant be used in .htaccess — it only works in Apaches main config file. Plus, pre_virtualhost_global.conf applies to every virtual host on the server, so all websites are protected uniformly. If you only want to protect a single website, use .htaccess in that websites document root.

    Conclusion

    WordPress brute force attacks are a real threat that happens every day on thousands of servers worldwide. From the logs I analyzed, a single IP can do 5,000+ login attempts in one hour — and that was just one out of dozens of IPs attacking simultaneously.

    The HTTP Basic Auth solution I implemented is a highly effective first line of defense. Brute force bots cant get past HTTP Basic Auth because they dont know the username and password you configured. Theyll just see the “Authentication required” message and stop trying.

    But HTTP Basic Auth isnt the only layer of defense you need. You also need: Fail2Ban to permanently block IPs, login attempt limits in WordPress, and most importantly — a strong password. The combination of all these defense layers will make your server much harder to brute force.

    Author: NOC Engineer — written based on experience handling 5,000+ brute force attempts per hour on a production server.