
- Bots on account screens try stolen passwords, create fake accounts and flood password resets.
- Rate limit by account and by IP address, and avoid hard lockouts that let an attacker lock real users out.
- Turnstile and reCAPTCHA only work if your server verifies every token, including requests from the mobile app.
- Verify email addresses before giving new accounts anything costly, such as SMS messages or trial credits.
- Block passwords known from data breaches and offer two-factor login, then watch the logs for spikes.
How do you stop bots on login and signup pages?
Use layers: rate limits per account and per IP address, a bot check such as Cloudflare Turnstile or Google reCAPTCHA that your server verifies on every request, email verification before new accounts get anything costly, a block on passwords known from data breaches, and alerts when failed logins spike. Avoid hard lockouts, which let an attacker lock real users out.
Account screens are a different problem from a contact form. A spam message is annoying; a taken-over account or a wave of fake signups costs real money. This is part of our website maintenance and security work on apps with user accounts.
What are bots doing on account screens?
- Trying stolen passwords. Lists of emails and passwords from other sites' breaches are tried against your login, because people reuse passwords.
- Creating fake accounts to abuse free trials, referral rewards or anything your app sends, such as SMS codes.
- Flooding password resets and verification emails, which can harm your email sending reputation.
- Calling your API directly, skipping the web page and any check that only runs in the browser.
How should rate limiting work?
Limit attempts by more than one key. Laravel lets one limiter return several limits, and its documentation shows a login limiter with an overall cap plus three attempts per minute for each email address; when a limit is exceeded, Laravel answers with a 429 status, per its rate limiting docs. Laravel Fortify throttles login attempts per username and IP address combination by default, and its docs recommend a mix of throttling, two-factor authentication and a web application firewall, according to the Fortify documentation.
Apply the same idea to signup, password reset and verification emails, not just login. Prefer throttling and growing delays over permanently locking an account, and email the owner when something looks wrong.
Turnstile or reCAPTCHA?
| Cloudflare Turnstile | Google reCAPTCHA v3 | |
|---|---|---|
| What the user sees | Usually nothing or a checkbox; the recommended Managed mode chooses based on risk (widget modes) | Nothing; each request gets a score (reCAPTCHA v3) |
| Your decision | Pass or fail from Cloudflare | You choose a score threshold per action, starting from 0.5 (reCAPTCHA v3) |
| Token lifetime | 300 seconds, single use (server-side validation) | Two minutes, verified once (verify docs) |
| Other notes | Works without routing your traffic through Cloudflare (Turnstile overview); free plan limited to 20 widgets per account (plans) | Call it when the user acts, not on page load (reCAPTCHA v3) |
Either works on login and signup. Turnstile is simpler to run; reCAPTCHA v3 gives you a score to act on, such as asking for an email code when the score is low instead of blocking.
Why must the check be verified on the server?
Cloudflare is explicit that server-side validation is mandatory: tokens can be forged, they expire, and they are meant for single use, as its server-side validation guide explains. Your login and signup endpoints must send each token to the provider and reject the request if it fails. The same rule applies to the API your mobile app uses; if the API accepts requests without any check, bots will simply use the API.
What should happen after someone signs up?
- Verify the email address first. Laravel's
verifiedmiddleware keeps unverified users out of chosen routes, and its example throttles resend requests withthrottle:6,1, per the email verification docs. - Delay anything costly, such as SMS messages, trial credits or invitations, until the account is verified.
- Block breached passwords. Laravel's password rule can check a password against known data leaks through the haveibeenpwned.com service using a k-Anonymity model, so the full password is never sent, as its validation docs describe.
How should password reset be protected?
Password reset is the third door bots try. Show the same message whether or not an email address has an account, so the form cannot be used to check who your customers are. Limit how often a reset can be requested for one address and from one IP address, and make reset links expire and work only once. Laravel's reset configuration has settings for how long tokens stay valid and how often they can be requested, and its docs note that limiting the host names your app answers to is particularly important when the app offers password reset, because absolute links are built from the request's Host header, as the password reset docs explain.
Where does two-factor login fit?
A bot check slows automated attacks, but it cannot tell a criminal with a real stolen password from the owner. Two-factor login closes that gap for staff and for customers with valuable accounts; our guide to two-factor login and single sign-on compares the options. Our Laravel development team usually adds it alongside the rate limits, so all three layers ship together.
How do you know the protection is working?
Log failed logins, signups and password resets with the account, IP address and result, and alert on sudden spikes rather than reading logs by hand. Our guide to monitoring a production web app covers alerts that people act on. Review the numbers after each change to rate limits or thresholds, and listen to support: a jump in "I can't log in" messages means the protection is hurting real users. For apps with special risks, our custom software development team can design stricter flows.
Frequently asked questions
Will a bot check annoy real users?
Much less than older puzzles. Cloudflare Turnstile runs background challenges and only shows a checkbox when it judges the visitor risky, and reCAPTCHA v3 returns a score without interrupting the user. You decide what happens with a low score, for example asking for an email code instead of blocking the person outright.
Should we lock an account after a few failed logins?
Be careful. A hard lock on the account lets anyone who knows an email address lock its owner out. Throttling attempts per account and IP address, slowing repeated attempts, and telling the owner about suspicious activity protect the account without handing attackers that power.
Does our mobile app need bot protection too?
The app itself is harder to automate, but its API is not. Bots can call the login and signup endpoints directly, skipping any check on your website. Apply the same rate limits and verification on the server for every client, and do not trust a check that only happens in the browser or the app.
Is a bot check enough on its own?
No. It raises the cost of automated attacks but does not stop someone with a real stolen password. Combine it with rate limits, blocking breached passwords, two-factor login for sensitive accounts and alerts on unusual activity. Laravel's own documentation recommends a mix of throttling, two-factor authentication and a web application firewall.
What should we do if a wave of fake signups has already happened?
Stop the source first by adding rate limits and a server-verified bot check to signup. Then find the fake accounts by pattern, such as unverified emails created in bursts, and disable them rather than deleting them straight away, so you can restore any real customer caught by mistake.
Last updated