{"id":1981,"date":"2017-08-30T22:24:03","date_gmt":"2017-08-30T16:54:03","guid":{"rendered":"https:\/\/pheonixsolutions.com\/blog\/?p=1981"},"modified":"2026-09-04T16:58:43","modified_gmt":"2026-09-04T11:28:43","slug":"block-wordpress-login-attacks-csf","status":"publish","type":"post","link":"https:\/\/pheonixsolutions.com\/blog\/block-wordpress-login-attacks-csf\/","title":{"rendered":"Block wordpress login attacks on csf"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This guide explains how to <strong>block WordPress login attacks on CSF<\/strong>, configuring ConfigServer Security &amp; Firewall to automatically detect and ban IP addresses that repeatedly fail to log in to <code>wp-login.php<\/code>. Brute-force login attempts against WordPress are extremely common \u2014 automated bots probe <code>wp-login.php<\/code> on virtually any publicly discoverable WordPress site around the clock, and without protection like this, your server logs will fill up with thousands of these attempts over time, some of which will eventually succeed against weak credentials if left unaddressed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>CSF<\/strong> (ConfigServer Security &amp; Firewall) is a popular firewall and intrusion-detection suite used widely on CentOS and cPanel servers. Out of the box, it blocks common attacks like failed SSH and IMAP logins, but WordPress-specific login protection isn&#8217;t built in by default \u2014 it requires adding a custom detection rule, which is exactly what this guide walks through, including two steps that are easy to miss and will leave the rule silently non-functional if skipped.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">I. Prerequisites<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before you block WordPress login attacks on CSF, make sure you have:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A CentOS server<\/li>\n\n\n\n<li>A web server (Apache or LiteSpeed) serving your WordPress site<\/li>\n\n\n\n<li>A WordPress installation with a known access log location<\/li>\n\n\n\n<li>CSF firewall already installed and running<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">II. How CSF and lfd Work Together<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before editing any files, it&#8217;s worth understanding the two components involved, since this guide touches both:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CSF<\/strong> is the firewall component \u2014 it manages the actual iptables (or firewalld) rules that allow or block traffic<\/li>\n\n\n\n<li><strong>lfd<\/strong> (Login Failure Daemon) is a separate background service that continuously watches log files for suspicious patterns \u2014 like repeated failed SSH logins \u2014 and instructs CSF to add a temporary or permanent block when a threshold is exceeded<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Custom detection rules, like the one this guide adds for WordPress, work by teaching <code>lfd<\/code> a new pattern to watch for in a specified log file. This is why restarting only CSF itself isn&#8217;t enough to activate a new custom rule \u2014 <code>lfd<\/code> is the service that actually needs to reload the updated pattern, which Step VII covers in detail after a lot of guides skip it entirely.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-scaled.png\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"512\" src=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-1024x512.png\" alt=\"\" class=\"wp-image-11368\" srcset=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-1024x512.png 1024w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-300x150.png 300w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-768x384.png 768w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-1536x768.png 1536w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2017\/08\/csf_lfd_wordpress_architecture-2048x1024.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">III. Understand How the Detection Rule Works<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add the following block to CSF&#8217;s custom regex file. Open it with a text editor:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">vi \/usr\/local\/csf\/bin\/regex.custom.pm\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add this rule:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">if (($globlogs{CUSTOM1_LOG}{$lgfile}) and ($line =~ \/(\\S+).*\\] \"POST \\\/wp-login\\.php.*\" 200\/)) {\n    return (\"Failed WordPress login from\",\"$1\",\"wordpress\",\"5\",\"80,443\",\"3600\");\n}\n<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Note:<\/strong> If you&#8217;re copying this from an older source, double-check that any quotation marks are straight quotes (<code>\"<\/code>), not curly\/smart quotes (<code>\"<\/code> <code>\"<\/code>). Smart quotes are a common artifact of copying text out of a word processor or certain web pages, and Perl (which <code>regex.custom.pm<\/code> is written in) will fail to parse the file correctly if they slip in.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What the return values mean<\/strong>, in order: a description shown in block notifications, the captured IP address (<code>$1<\/code>), a label used to group this rule&#8217;s blocks, the failure count threshold (<code>5<\/code> \u2014 meaning 5 matched failures trigger a block), the ports to block (<code>80,443<\/code>), and the block duration in seconds (<code>3600<\/code> = 1 hour).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IV. Why This Rule Matches on HTTP Status 200<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part that confuses a lot of people at first glance, since a <code>200<\/code> status code normally means &#8220;success&#8221; \u2014 so why would it indicate a <em>failed<\/em> login? Here&#8217;s the actual mechanism:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>When a WordPress login <strong>succeeds<\/strong>, WordPress issues an HTTP <strong>302 redirect<\/strong> to <code>wp-admin<\/code> \u2014 the browser is sent somewhere else, not shown a rendered page directly<\/li>\n\n\n\n<li>When a WordPress login <strong>fails<\/strong>, WordPress re-renders the login page itself, showing an error message \u2014 which returns a standard <strong>200 OK<\/strong> status, since a full page was successfully served, just one containing a login error rather than a dashboard<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">So counterintuitively, a <code>200<\/code> response to a <code>POST \/wp-login.php<\/code> request specifically indicates a failed attempt, while a <code>302<\/code> indicates success \u2014 which is exactly why the regex pattern above filters for <code>200<\/code> rather than <code>302<\/code>. This detail is worth understanding rather than just copying blindly, since it&#8217;s the entire basis for how this rule distinguishes real failed logins from successful ones in the log.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">V. Configure the Custom Log Path<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Now tell <code>lfd<\/code> which log file to actually watch. Open CSF&#8217;s main configuration file:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">vi \/etc\/csf\/csf.conf\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Find (or add) the <code>CUSTOM1_LOG<\/code> setting and point it at your actual WordPress site&#8217;s access log:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">CUSTOM1_LOG = \"\/home\/*\/access-logs\/*\"\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The correct path depends on your hosting setup. Common examples:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>cPanel server (domain-specific log):<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">CUSTOM1_LOG = \"\/home\/username\/access-logs\/domain.com\"\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Plain Apache server with combined logging:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">CUSTOM1_LOG = \"\/var\/log\/apache2\/access.log\"\n<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> The wildcard example from the original quick-reference version (<code>\/home\/*\/access-logs\/*<\/code>) works across all cPanel accounts on a server, but it&#8217;s broader than necessary if you&#8217;re only protecting one specific domain \u2014 pointing directly at the specific log file for the domain you&#8217;re protecting reduces the log-scanning overhead and avoids accidentally matching unrelated sites&#8217; login attempts against the same threshold count.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">VI. Restart Both CSF and lfd<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the step most commonly missed, and it&#8217;s the reason a correctly written rule sometimes appears to do nothing. Restarting <code>csf<\/code> alone reloads firewall rules, but <code>lfd<\/code> \u2014 the actual process reading log files and applying custom regex rules \u2014 needs to be restarted separately to pick up the new pattern and log path:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">csf -r\nsystemctl restart lfd\n<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">If <code>lfd<\/code> isn&#8217;t running as a systemd service on your specific CentOS version, use <code>service lfd restart<\/code> instead.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">VII. Verify the Rule Is Active<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Confirm both services restarted cleanly:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">systemctl status lfd\ncsf -l\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>csf -l<\/code> lists currently active firewall rules; while this won&#8217;t show the custom regex rule directly (since no IP has been blocked yet), it confirms CSF itself is running correctly before you move on to testing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">VIII. Test the Rule<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">From a separate machine (not one you rely on for legitimate site access, in case something goes wrong), intentionally fail to log in to <code>wp-login.php<\/code> on your WordPress site five times in a row \u2014 matching the threshold set in Step III.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then check whether the IP was blocked:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">csf -g &lt;TEST_IP_ADDRESS>\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This searches CSF&#8217;s active rules for the specified IP. If it&#8217;s found, the rule is working correctly. You can also check <code>lfd<\/code>&#8216;s own log for confirmation:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">tail -f \/var\/log\/lfd.log\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Watching this file live while triggering test failures is the most direct way to confirm the entire pipeline \u2014 log line, regex match, and block \u2014 is functioning end to end.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IX. Whitelist Your Own IP First<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Before relying on this in production, whitelist your own administrative IP address to avoid accidentally locking yourself out during legitimate troubleshooting or repeated password attempts:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">csf -a &lt;YOUR_IP_ADDRESS> \"Admin - always allow\"\n<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> Do this <em>before<\/em> extensive testing, not after. It&#8217;s a genuinely common mistake to test a new brute-force protection rule from your own IP, get blocked by your own rule, and then be unable to reach the server&#8217;s control panel or SSH to fix it \u2014 plan around this in advance.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">X. Unban an IP (If Needed)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If a legitimate user or your own IP gets blocked unexpectedly, remove the block with:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">csf -dr &lt;IP_ADDRESS>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">XI. Troubleshooting Common Issues<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule doesn&#8217;t trigger even after repeated failed logins:<\/strong> This is almost always one of two things: <code>lfd<\/code> wasn&#8217;t restarted after the configuration change (Step VI), or <code>CUSTOM1_LOG<\/code> doesn&#8217;t actually point to a log file that&#8217;s receiving WordPress access log entries. Confirm the log path is correct by checking it directly: <code>tail -f \/path\/to\/your\/access-log<\/code> while triggering a test failure, and confirm you see matching <code>POST \/wp-login.php<\/code> lines with a <code>200<\/code> status.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Legitimate users are getting blocked too easily:<\/strong> Consider raising the threshold from <code>5<\/code> to a higher number in the regex rule&#8217;s return statement, especially if your users are prone to typos, or pair this with a WordPress-side login limiting plugin that shows a warning before CSF&#8217;s threshold is reached.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule blocks successful logins too:<\/strong> Double-check that the regex pattern specifically matches <code>200<\/code>, not a broader pattern that could also catch <code>302<\/code>. If you modified the original rule, verify the status code condition wasn&#8217;t accidentally altered or removed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">XII. Monitoring Blocked IPs Over Time<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once the rule is active, it&#8217;s worth periodically reviewing what it&#8217;s actually catching, both to confirm it&#8217;s working and to spot patterns worth acting on further.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>View all currently blocked IPs:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">csf -l\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Search specifically for WordPress-related blocks<\/strong>, using the label defined in the rule (<code>wordpress<\/code>):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep wordpress \/var\/log\/lfd.log\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This shows a running history of every IP that triggered the rule, which can reveal useful patterns \u2014 for example, a large number of attempts from a specific country or hosting provider&#8217;s IP range might justify a broader block using CSF&#8217;s country-blocking feature (<code>CC_DENY<\/code> in <code>csf.conf<\/code>), rather than relying solely on the per-IP threshold to catch each attacker individually.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Check overall lfd activity and confirm the service remains healthy:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">systemctl status lfd\n<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If <code>lfd<\/code> has crashed or stopped for any reason, no custom rules \u2014 including this one \u2014 will be enforced until it&#8217;s restarted, so periodic verification that the service is actually running is a worthwhile habit, not just a one-time setup check.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">XIII. Best Practices for WordPress Login Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Combine this with a WordPress-side login limiter plugin<\/strong> (like Limit Login Attempts Reloaded) for defense in depth \u2014 CSF blocks at the network\/firewall level, while a plugin can add account-level lockouts and user notifications, covering scenarios where an attacker rotates IP addresses faster than CSF&#8217;s threshold catches them<\/li>\n\n\n\n<li><strong>Consider renaming or restricting access to wp-login.php<\/strong> entirely for an additional layer, though be aware this can break some plugins or workflows that expect the default login path<\/li>\n\n\n\n<li><strong>Enable two-factor authentication<\/strong> on WordPress admin accounts, which meaningfully reduces the impact even if a brute-force attempt eventually succeeds against a weak password<\/li>\n\n\n\n<li><strong>Review <code>\/var\/log\/lfd.log<\/code> periodically<\/strong> to understand attack patterns and confirm the rule continues functioning correctly over time, especially after any CSF or server updates<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">XIV. Conclusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Blocking WordPress login attacks with CSF comes down to teaching <code>lfd<\/code> a custom pattern that correctly distinguishes failed logins (HTTP 200) from successful ones (HTTP 302), pointing it at the right access log file, and \u2014 critically \u2014 restarting <code>lfd<\/code> itself, not just CSF, for the new rule to actually take effect. With your own IP whitelisted first and the rule verified through a real test, this becomes a reliable, low-maintenance layer of protection against the automated brute-force attempts that virtually every public WordPress site experiences continuously.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For more on CSF&#8217;s configuration options and custom rule syntax, see the <a href=\"https:\/\/configserver.dev\/csf\/\" target=\"_blank\" rel=\"noopener\">official CSF documentation<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently Asked Questions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why does my rule need lfd restarted, not just CSF?<\/strong> <code>lfd<\/code> is the separate background process responsible for actually reading log files and matching custom regex patterns \u2014 CSF itself only manages firewall rules. Restarting CSF alone reloads firewall state but doesn&#8217;t make <code>lfd<\/code> re-read the updated <code>regex.custom.pm<\/code> file or the new <code>CUSTOM1_LOG<\/code> path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Does this work with LiteSpeed or Nginx instead of Apache?<\/strong> Yes, as long as the web server&#8217;s access log uses a similar combined log format containing the HTTP method, request path, and status code \u2014 just point <code>CUSTOM1_LOG<\/code> at that server&#8217;s actual access log path instead of Apache&#8217;s default location.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Will this block legitimate users who mistype their password a few times?<\/strong> It&#8217;s possible if they fail 5 times in a row within the tracking window, which is why whitelisting known-good IPs (Step IX) and considering a slightly higher threshold for sites with less security-conscious users are both worth considering based on your specific situation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Is CSF&#8217;s custom rule approach better than using a WordPress security plugin instead?<\/strong> They solve the problem at different layers and work well together rather than being an either\/or choice \u2014 CSF blocks at the firewall level before the request even fully reaches WordPress, reducing server load from repeated attempts, while a WordPress plugin can add account-specific protections and admin notifications that a firewall-level rule can&#8217;t provide on its own.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">If you have any questions about this setup or run into an issue not covered here, feel free to reach out to us at <a href=\"https:\/\/pheonixsolutions.com\/\">Pheonix Solutions<\/a> \u2014 we&#8217;re happy to help.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Related Articles:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/pheonixsolutions.com\/blog\/how-to-manage-the-csf-firewall-in-whm-cpanel\/\" target=\"_blank\" rel=\"noreferrer noopener\">How to Manage the CSF Firewall in WHM\/cPanel<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/pheonixsolutions.com\/blog\/implement-ipset-on-csf-on-cpanel-pheonixsolutions\/\" target=\"_blank\" rel=\"noreferrer noopener\">Implement IPSET on csf on cPanel | Pheonixsolutions<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction This guide explains how to block WordPress login attacks on CSF, configuring ConfigServer Security &amp; Firewall to automatically detect [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1019],"tags":[308,167],"class_list":["post-1981","post","type-post","status-publish","format-standard","hentry","category-cloud-aws","tag-csf","tag-wordpress-2","psol-cat-cloud-aws"],"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/phn2x7-vX","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1981","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=1981"}],"version-history":[{"count":1,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1981\/revisions"}],"predecessor-version":[{"id":11370,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/1981\/revisions\/11370"}],"wp:attachment":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=1981"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=1981"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=1981"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}