Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mh1's avatar
Level 1

Laravel Fortify - Rate limit registrations by IP address

We’ve been running into an issue where people are abusing our free trial credits by automatically creating tons of new accounts.

I’d like to rate limit the registration.store route provided by Laravel Fortify — ideally to allow only X registrations per IP per day.

What’s the best or most efficient way to implement this?

Thanks!

1 like
4 replies
Tray2's avatar

There really isn't a reliable one, you could probably store the ip's in a table, and then remove it after x amount of time, however it is extremely easy to to change the ip, and render the limiting ineffective. I would suggest using a honeypot that your system looks at to prevent the creation of fake accounts.

1 like
martinbean's avatar

@mh1 Rate-limiting by IP address isn’t reliable as you can either:

  • Unintentionally block different people from the same IP address, e.g. in a large corporation.
  • Not actually block bad actors who have a non-static, rotating IP address.

You should instead be employing other methods that aren’t that much of an issue for genuine customers, but friction for bad actors.

The main deterrent would be only letting users with a verified email address start a trial. A genuine customer isn’t going to care about clicking a link to verify their email address as they only have to do it once. But if you have a bad actor trying to abuse your service by creating many accounts, then this is going to get annoying fast for them.

Depending on how bad the problem is, you could also add card verification checks before starting the service. So you just do a check, check the card details matches the account holder (e.g. has the same name). You can then flag accounts where the card details don’t match (e.g. a bad actor is using stolen card details), or using the same card across multiple accounts.

1 like
mh1's avatar
Level 1

Thank you for your insights.

Yesterday, I also added Cloudflare CAPTCHA. Email verification has been in place since the beginning, but they started creating emails using subaddressing (e.g. [email protected]), so I blocked that as well. I’ve also added a rule to block unauthorized email providers. We’ll see if this helps or if they continue abusing the system — in which case, I suppose we’ll have to add card verification next.

1 like
Jsanwo64's avatar
  1. Don’t rely solely on IP-based rate limiting (use it as a throttle, not protection)

You can still apply IP throttling as a first barrier, but don’t treat it as your main defense.

In Fortify, you can wrap the registration route with a rate limiter, e.g.:

// app/Providers/FortifyServiceProvider.php

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;

public function boot()
{
    Fortify::createUsersUsing(CreateNewUser::class);

    RateLimiter::for('register', function (Request $request) {
        return [
            Limit::perDay(3)->by($request->ip()), // e.g. 3 accounts/IP/day
            Limit::perMinute(1)->by($request->ip()), // spam burst protection
        ];
    });

    Fortify::registerView(fn () => view('auth.register'));
}

then do

// routes/web.php
use Illuminate\Support\Facades\Route;

Route::post('/register', [\Laravel\Fortify\Http\Controllers\RegisteredUserController::class, 'store'])
    ->middleware('throttle:register');

This will slow bots and basic scripts, but it won’t stop determined abusers.

  1. Use stronger user validation (this matters more)

Rate limiting is friction; verification is blockage.

a) Enforce real email ownership (no sub-addressing, no disposable domains)

  1. Block + trick (you already did this).

  2. Use a disposable email domain blacklist.

  3. Require clicking the verification link before trial activation.

b) Require basic identity proof before trial starts (not before signup)

Examples:

  1. Store IP & device details and prevent >1 free trial per combination.

  2. Require phone verification (OTP).

  3. Require payment method verification (card pre-auth $0 or $1).

c) Block by device/browser fingerprint

Even if someone uses rotated proxies and fresh emails, their device is often the same.

Use:

  1. FingerprintJS
  2. Client hints
  3. Simple user-agent + accept-language + timezone + local storage

Match that to accounts and restrict trial access.

checkout https://github.com/Propaganistas/Laravel-Disposable-Email

Please or to participate in this conversation.