Every login page on the public internet is being probed right now. Botnets running credential-stuffing lists don't care if you're a five-person agency site or an enterprise SaaS — if there's a form that accepts a username and password, it's a target. The good news: brute force attacks are one of the easiest attack classes to defend against once you know exactly what to configure.
This isn't a theoretical overview. Below are the specific controls that actually stop brute force and credential-stuffing attacks, in the order you should implement them.
Why "just use a strong password" isn't enough
Brute force attacks today rarely try every possible password character-by-character. They use two smarter methods:
- Credential stuffing — attackers use lists of real username/password pairs leaked from other breaches, betting users reuse passwords across sites.
- Password spraying — trying one common password (like
Winter2024!) against thousands of usernames, staying under per-account lockout thresholds.
Both bypass "strong password" advice entirely because the password itself might be strong — it just wasn't unique to your site. Your defenses need to work at the request level, not the password-strength level.
Rate Limiting: Your First Line of Defense
Rate limiting caps how many login attempts can happen in a given time window, regardless of whether the credentials are correct.
Where to apply it
- Per IP address — block or throttle after 10-15 failed attempts in 5 minutes.
- Per account/username — lock the account temporarily after 5 failed attempts, independent of source IP (this catches distributed attacks from botnets).
- Per session/device fingerprint — combine IP and username limits with a device signal to reduce false positives on shared IPs (offices, VPNs, mobile carriers).
Practical implementation
- Nginx example — use the
limit_reqmodule to throttle requests to/loginor/wp-login.php:limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; location /login { limit_req zone=login burst=3 nodelay; } - Application-level — most frameworks have built-in support: Laravel's
ThrottleRequestsmiddleware, Django'sdjango-ratelimit, Express'sexpress-rate-limit. - WordPress — plugins like Limit Login Attempts Reloaded or WP Fail2Ban handle this without touching server config.
Progressive Delays Beat Hard Lockouts
Hard lockouts (locking an account after N attempts) create a denial-of-service vector — an attacker can lock legitimate users out just by guessing wrong repeatedly. Progressive delays are more resilient:
- 1st failed attempt: no delay
- 2nd-3rd failed attempts: 1-2 second delay
- 4th-6th failed attempts: 10-30 second delay
- 7+ failed attempts: CAPTCHA challenge required, plus extended delay
This slows automated attacks to a crawl (turning a 10,000-attempt brute force into a multi-day operation) while legitimate users who mistype a password twice barely notice.
CAPTCHA and Bot Challenges — Use Them Selectively
Don't put CAPTCHA on every login attempt — it hurts conversion and user experience for no benefit against low-volume attacks. Instead, trigger it contextually:
- After 3+ failed attempts from the same IP or account
- When login requests come from IPs with no prior browsing history on the site (straight to
/loginwith no page views first — classic bot behavior) - When request headers look automated (missing standard browser headers, no JS execution, unusual
User-Agentstrings)
hCaptcha, Cloudflare Turnstile, or reCAPTCHA v3 (invisible scoring) are all reasonable choices. Turnstile and reCAPTCHA v3 are less disruptive since they score risk silently before showing a visible challenge.
Multi-Factor Authentication Closes the Real Gap
Rate limiting slows attackers down. MFA makes stolen or guessed passwords useless on their own. If you implement nothing else from this list, implement MFA for:
- Admin and privileged accounts — non-negotiable
- Any account with access to billing, customer data, or infrastructure
- All accounts, eventually — TOTP apps (Authy, Google Authenticator) are low-friction and free to integrate via libraries like
speakeasyorpyotp
For agencies managing WordPress or CMS backends for clients, enforcing MFA via plugins like WP 2FA or Wordfence Login Security removes an entire attack category with minimal setup time.
Web Server and Header-Level Protections
Brute force protection isn't only application logic — server and network configuration matter too:
- Fail2Ban — monitors auth logs and auto-bans IPs after repeated failures at the firewall level (works for SSH, WordPress, custom apps via log parsing).
- Web Application Firewall (WAF) — Cloudflare, AWS WAF, or ModSecurity rules can block known attack patterns and malicious IP ranges before requests even reach your app.
- Rename or obscure default login paths — moving away from predictable paths like
/wp-adminor/adminreduces automated scanner noise, though this is obscurity, not real security — pair it with the controls above. - Security headers — while headers like
Content-Security-PolicyandX-Frame-Optionsdon't stop brute force directly, they reduce the attack surface for credential-harvesting via clickjacking or injected scripts targeting your login form.
Common Mistakes That Undermine Brute Force Defenses
- Only rate limiting by IP — distributed attacks from thousands of residential proxy IPs sail right through. Always pair with per-account limits.
- Returning different error messages for "wrong password" vs "user doesn't exist" — this lets attackers enumerate valid usernames before even attempting passwords. Use a generic "invalid credentials" message.
- No logging or alerting — if you can't see failed login spikes in real time, you won't know an attack is happening until damage is done. Ship auth logs to a monitoring tool or at minimum alert on failure-rate anomalies.
- Ignoring password reset and API endpoints — attackers often target "forgot password" flows or API login endpoints because they're less protected than the main login form. Apply the same rate limits everywhere credentials are checked.
Checking Whether Your Site Is Actually Protected
It's easy to assume these controls are in place because a plugin was installed once, or a WAF rule was added years ago and never revisited. The only way to know for sure is to check the live configuration — SSL setup, security headers, cookie flags, and exposed endpoints all factor into how exploitable a login page really is, even before brute force protection comes into play.
Running a scan with a tool like WebSentry gives you a fast read on the surrounding security posture — SSL/TLS configuration, missing security headers, cookie security flags, CORS misconfigurations, and DNS issues that often accompany weak login page hardening. Sites with an A-F grade from WebSentry make it obvious, at a glance, which of these foundational pieces need attention before or alongside brute force-specific fixes.
If you manage login pages for clients as an agency, or you're responsible for your own site's auth flow, run a free scan at websentry.dev and see where the gaps actually are — rather than guessing.
Check your own site
Run a free security scan and see if your site has the issues covered in this article. Results in under 30 seconds.
