Demo, all content is generated
Question

Hundreds of fake signups overnight, all with random gmail addresses

Solved · 1105 views · asked by olga_s · edited

Woke up to 430 new users. Names like kdjfhsdk, emails like m.a.r.k.o.w.e.n.s.2.1@gmail.com with dots everywhere. They don't do anything in the app, just sign up. My Resend quota is now almost gone because each one gets a confirmation mail.

What I’ve tried

Deleted some by hand in the Supabase dashboard. Added a 'I am not a robot' checkbox in Lovable, they still come.

Comment
Same pattern on my app last week, dots in gmail addresses. Didn't know why until now. vikram_s · edited

4 answers

Marked as helpful by the asker
amir_h · edited

A checkbox is just a field, bots tick it. You need a real CAPTCHA checked by Supabase, not by your frontend:

  1. Make a free Cloudflare Turnstile site key (invisible for most humans).
  2. Supabase → Authentication → Attack Protection → enable CAPTCHA, choose Turnstile, paste the secret key.
  3. In the signup form, render the Turnstile widget and pass its token:
await supabase.auth.signUp({
  email, password,
  options: { captchaToken },
})

Now Supabase rejects any signup without a valid token, even when bots call the API directly.

The dotted gmail addresses are a known pattern: bots use your confirmation mail to spam or to warm up addresses. That's why your quota burned.

Cleanup: the fakes never confirmed, so you can delete users where email_confirmed_at is null and created in that window.

Comment
Turnstile is on. Lovable added the widget when I pasted your code. Zero fake signups since this morning. olga_s · edited
Enable it for password reset too, same form of abuse happens there. chidi_eze · edited
dmitri_v · edited

The dots are the Gmail trick: m.a.r.k@gmail.com and mark@gmail.com are the same inbox. If free credits or trials are involved, store a normalized email (dots and +tag removed for gmail.com) with a unique index so one inbox can't sign up 40 times.

Comment
sanne_dev · edited

If you delete in SQL, do it on auth.users with a strict filter and look at the count with a select first. And make sure your profiles table has on delete cascade, or you'll have orphans.

Comment
lena_ops · edited

Also look at Authentication → Rate Limits: there's a per-IP limit for sign-ups and sign-ins. Default is generous; for a small app you can lower it. It won't stop a botnet but it stops the lazy scripts.

Comment