# Subscription bombing: stop your signup form flooding an inbox

> Subscription bombing abuses your signup form to bury a victim's inbox in confirmation emails. Stop it with a CAPTCHA, rate limits and a send budget per address.

- Published: 2026-11-03
- Author: Koltrix Team
- Category: Email API
- Source: https://koltrix.com/blog/subscription-bombing-stop-your-signup-form-from-flooding-someones-inbox

Subscription bombing (also called list bombing) is when a bot submits a victim's address to thousands of signup forms at once, so the victim's inbox fills with confirmation emails and a real alert gets buried. Your form is only a tool in the attack, but your domain's reputation pays for it, so the fix is to limit what a form can make you send.

## What is subscription bombing, and why does it hit you?

It is an abuse of a feature that works as intended: you send an email to whatever address someone types in. The attacker never touches the victim's inbox. They feed the address to forms that will do it for them, often hundreds or thousands of different sites inside a few hours, sometimes to hide a fraud alert or a password-reset message in the noise.

For you, the damage is indirect but real: a burst of mail to an address that never asked for it can produce spam complaints, the sending domain looks like part of an attack, and a wave of messages to one mailbox provider can get you throttled. [Spamhaus has described](https://www.spamhaus.org/news/article/734/subscription-bombing-coi-captcha-and-the-next-generation-of-mail-bombs) this pattern and the defenses it expects from form owners.

## Why isn't a confirmation email enough?

Because the confirmation email is the thing being abused. Confirmed (double) opt-in stops bots from joining your list, since a bot cannot click the link. But the bomb is the confirmation message itself: you already sent it. Spamhaus's point is that confirmed opt-in alone does not protect the recipient from a flood, so pair it with a CAPTCHA that stops the automated submission in the first place.

## Which defenses actually work?

Use several layers, because each one has a hole.

| Layer | What it stops | What it misses |
|---|---|---|
| CAPTCHA or challenge on the form | Simple scripted submissions | Solver services and some advanced bots |
| Honeypot field (hidden, must stay empty) | Naive bots that fill every field | Bots that render the page |
| Rate limit per IP | One machine hammering the form | Botnets using many IPs |
| Send budget per recipient address | Any attack aimed at one victim | Nothing, if the budget is enforced server-side |
| Confirmed opt-in | Bots joining your list | The first confirmation email still goes out |
| Block signup from a form without a session or token | Direct posts to your endpoint | Attackers who load the page first |

The send budget is the layer most guides skip and the one that protects the victim. IP limits fail against a botnet because each request comes from a different address, but the recipient is the same every time.

## How do you limit sends per recipient address?

Count the mail you send to each address and refuse to send more once a small budget is spent. A reasonable starting point is one confirmation email per address per few minutes, and a handful per day, with a resend that makes the user wait. The numbers are yours to tune; the principle is that no address can receive more than N messages from your system in a window, regardless of how many requests ask for it.

Normalize the address before counting, or the attacker sidesteps the budget. Lowercase it and strip a `+tag` for providers that treat it as the same mailbox:

```python
def budget_key(email: str) -> str:
    local, _, domain = email.strip().lower().partition("@")
    return f"{local.split('+')[0]}@{domain}"

def may_send_confirmation(email: str, store) -> bool:
    key = f"confirm:{budget_key(email)}"
    if store.incr(key, window_seconds=86400) > 3:   # 3 per day
        return False
    return not store.exists(f"cooldown:{key}")       # plus a 5-minute cooldown
```

When the budget is spent, show the same "check your inbox" message as a success. If you reveal that an address is blocked, you tell an attacker which addresses are protected and you confirm which addresses exist.

## Should you tell the user when you do not send?

No. Return an identical response whether or not an email went out. This also keeps your form from becoming a way to find out which addresses have accounts, which is a separate problem with its own fixes. Log the suppressed attempt on your side so you can see an attack in progress.

## How do you spot an attack while it is happening?

Watch three numbers on your signup endpoint: submissions per minute, the share of distinct domains among recipient addresses, and the confirmation-click rate. A bomb looks like a spike in submissions, many different recipients at a few mailbox providers, and almost no clicks. Alert on the click rate falling while volume rises. Our [contact-form abuse guide](/blog/contact-forms-that-send-email-preventing-spam-and-abuse) covers the same signals for forms that email you.

## What should the confirmation email itself do?

Keep it short, say which site and which form triggered it, and make the link survive security scanners that open every URL, as described in [confirmation links that survive security scanners](/blog/email-confirmation-links-that-survive-security-scanners). Include a line such as "If you did not request this, ignore this email; nothing will happen." That sentence reduces complaints from bomb victims, who will otherwise hit the spam button on your domain.

## What about the addresses you already collected?

Treat unconfirmed signups as disposable: delete them after a short window (a few days is common) and never send marketing to them. Handle bounces and complaints from the confirmation mail through your [suppression list policy](/blog/suppression-list-policy) so the same address is not sent to again. Validating syntax and the domain's MX records at the form catches typos and obvious junk, as in [email validation at signup](/blog/signup-email-validation).

## What does this look like with an email API?

If you send confirmations through an API, the per-recipient budget belongs in your application, before the API call, because the provider will deliver whatever you submit. Expect your own provider-side limits too; [handling 429 responses](/blog/email-api-rate-limits-429) matters when an attack pushes your volume up. With Koltrix you send through `POST https://api.koltrix.com/api/v2/emails` with an API key or through the SMTP relay, and the form-level checks above still run in your code first.

## Bottom line

Subscription bombing works because a signup form will email anyone it is told to. Put a CAPTCHA in front of the form, limit requests per IP, and above all cap how many messages any one address can receive from you per day, returning the same response either way. Keep confirmed opt-in, delete unconfirmed signups, and watch the confirmation-click rate so you notice the next attack in minutes instead of from a complaint.
