Blocking disposable email sign-ups in Laravel
I noticed some users signing up on a project with throwaway emails from services like yopmail, guerrillamail, and tempmail. They'd sign up, poke around, and create multiple accounts to use the service for free.
I researched solutions for it and found propaganistas/laravel-disposable-email, a popular Laravel package (~1.35M downloads) that gives you an indisposable validation rule. It pulls from a community-maintained list of ~72k known disposable domains.
But I didn't want another package dependency for something this simple. So I grabbed their JSON file with 72,000+ disposable domains, and wrote a tiny custom validation rule:
class NotDisposableEmail implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$domain = strtolower(substr(strrchr($value, '@'), 1));
if (in_array($domain, $this->disposableDomains(), true)) {
$fail('Disposable email addresses are not allowed. Please use a permanent email address.');
}
}
private function disposableDomains(): array
{
return once(function (): array {
$path = resource_path('data/disposable-domains.json');
if (! file_exists($path)) {
return [];
}
return json_decode(file_get_contents($path), true) ?? [];
});
}
}
Then added it to the registration validation in CreateNewUser.php:
'email' => [
'required',
'string',
'email',
'max:255',
new NotDisposableEmail,
Rule::unique(User::class),
],
That's it. No package, no API calls, no latency. The JSON file loads once per request (using Laravel's once() helper), and if the file is somehow missing, it silently passes so nothing breaks. It shows this when someone tries signing up using a disposable email:
The only downside is that the list is static and new disposable services won't be caught until I update the JSON. But for now, it's good.
Update:
After many people pointed out on Thread and X, I have now removed the blocking for disposable domains and exploring better ways to filter out low-quality signups.
Webmentions