Skip to content

Commit fc763f1

Browse files
committed
better spam rules docs
1 parent a6b9c83 commit fc763f1

3 files changed

Lines changed: 104 additions & 3 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: Spam Filter
3+
description: Block submissions from VPN, proxy and Tor networks, and reject email addresses that match suspicious patterns such as the Gmail dot/+tag evasion trick.
4+
i18nReady: true
5+
---
6+
7+
The Spam Filter rejects suspicious submissions before they reach your application. It complements [IP Validation Rules](/en/advanced/ip-validation-rules/) by acting on two extra signals: the *connection type* (VPN, proxy, Tor) and the *shape of the submitted email address* (regex patterns and Gmail-specific normalisation).
8+
9+
The Spam Filter is a paid feature. Free-tier accounts can see the settings but cannot enable them.
10+
11+
## Why this exists
12+
13+
Modern spam campaigns frequently abuse Gmail's "dots are ignored" rule and `+tag` aliases to flood signup forms while looking, on paper, like distinct addresses. For example, all of the following point to the same Gmail mailbox:
14+
15+
- `alice@gmail.com`
16+
- `a.l.i.c.e@gmail.com`
17+
- `alice+anything@gmail.com`
18+
- `a.l.i.c.e+vkd38uoukd5@gmail.com`
19+
20+
The Spam Filter lets you reject these patterns at the CAPTCHA layer so you don't have to deduplicate them downstream. It also lets you reject submissions from connections that resolve to a known VPN, proxy or Tor exit node — a common pattern for automated spam.
21+
22+
## Enabling the spam filter
23+
24+
Settings live under **Settings → Spam Filter** in the portal. You must:
25+
26+
1. Toggle **Enable Spam Filter** on (the master switch).
27+
2. Enable the individual rules you want.
28+
3. Save.
29+
30+
The rules are evaluated server-side during `/verify` (image and PoW). When a rule matches, the verification response will be `{ verified: false }` and the `status` field will indicate which rule fired (e.g. `"API.SPAM_EMAIL_RULE"` or `"API.VPN_BLOCKED"`). You can use this to show a tailored message to legitimate users — for example, prompting them to disable a VPN.
31+
32+
## VPN, proxy and Tor blocking
33+
34+
When **Block VPN, proxy and Tor traffic** is enabled, every verify call performs an IP reputation lookup on the user's IP. If the IP resolves as a VPN exit node, public proxy or Tor exit node, the verification fails with status `API.VPN_BLOCKED`.
35+
36+
**Caveats**
37+
38+
- Some legitimate users connect via corporate or privacy-focused VPNs. You should communicate the policy to them — for example, by mapping `API.VPN_BLOCKED` to a user-facing message like "We don't accept submissions from VPNs. Please disable your VPN and try again."
39+
- Lookups are best-effort. If the IP reputation service is unavailable for a request, the request is **allowed through** so an upstream outage can't block all your traffic.
40+
41+
## Email pattern rules
42+
43+
Enable **Email pattern rules** to apply per-site rules to the email address submitted with the CAPTCHA. The address is taken from the `email` parameter on the verify request — if you don't pass it, no email rule fires.
44+
45+
### Maximum dots in the local part
46+
47+
Reject addresses where the part before `@` contains more dots than the configured limit. A value of `2` rejects addresses like `b.u.eltame.ki.a@gmail.com` (5 dots) while leaving `first.last@example.com` (1 dot) untouched.
48+
49+
Leave the field blank to disable this check.
50+
51+
### Curated default patterns
52+
53+
A small, conservative regex set kept up-to-date by Prosopo. Currently covers:
54+
55+
- Local parts with 4 or more dots (the most common Gmail evasion).
56+
- Random alphanumeric suffix tags on Gmail (e.g. `meredithgeronimo241+vkd38uoukd5@gmail.com`).
57+
58+
Defaults are deliberately narrow to avoid false positives. If you want to be stricter, add your own patterns under **Custom regex blocklist**.
59+
60+
### Normalise Gmail addresses
61+
62+
When enabled, Gmail/Googlemail addresses are normalised before being matched against the **Custom regex blocklist** (dots in the local part are stripped, and any `+tag` suffix is removed). This means that a single regex like `^alice@gmail\.com$` matches all of:
63+
64+
- `alice@gmail.com`
65+
- `a.l.i.c.e@gmail.com`
66+
- `alice+promo@googlemail.com`
67+
68+
Default patterns and the dot-count check both run against the *raw* address so they remain effective regardless of this setting.
69+
70+
### Custom regex blocklist
71+
72+
A list of JavaScript regular expressions, evaluated case-insensitively. The whole address is tested. Invalid regex entered through the portal cannot be saved; if a stored regex becomes invalid (e.g. a JS engine update), it is silently skipped at runtime so a single bad pattern can't take the filter offline.
73+
74+
Examples:
75+
76+
| Pattern | Matches |
77+
| --- | --- |
78+
| `@disposable\.tld$` | Any address on `disposable.tld` |
79+
| `^test\d+@` | `test1@…`, `test99@…` |
80+
| `\+spam\d*@gmail\.com$` | Gmail addresses tagged with `+spam`, `+spam1`, … |
81+
82+
## Status codes returned to your app
83+
84+
| Status | Meaning |
85+
| --- | --- |
86+
| `API.SPAM_EMAIL_DOMAIN` | Email domain is on the global disposable-domain list |
87+
| `API.SPAM_EMAIL_RULE` | Email matched one of your configured spam filter rules |
88+
| `API.VPN_BLOCKED` | IP resolves to a known VPN, proxy or Tor exit node |
89+
90+
Show these to the user (translated via the standard locale files) so a legitimate user understands why their submission was blocked.

src/content/docs/en/basics/captcha-types.mdx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: CAPTCHA Types
3-
description: Serve different types of CAPTCHA by setting the `captchaType` to `frictionless`, `pow`, `image`.
3+
description: Serve different types of CAPTCHA by setting the `captchaType` to `frictionless`, `pow`, `image`, or `puzzle`.
44
i18nReady: true
55
---
66
import {Image} from 'astro:assets';
@@ -9,7 +9,7 @@ import ImageCaptcha from '~/assets/image-captcha.png';
99
import PoWSettings from '~/assets/pow-settings.png';
1010
import ImageSettings from '~/assets/image-settings.png';
1111

12-
Serve **different** types of CAPTCHA by setting the `captchaType` to `frictionless`, `pow`, `image`.
12+
Serve **different** types of CAPTCHA by setting the `captchaType` to `frictionless`, `pow`, `image`, or `puzzle`.
1313

1414
## Frictionless CAPTCHA
1515

@@ -39,6 +39,12 @@ An image CAPTCHA challenge requires the user to select the correct image(s) that
3939

4040
The Image CAPTCHA type does not interrogate the user's browser environment, use `frictionless` for that (above).
4141

42+
## Puzzle CAPTCHA
43+
44+
A Puzzle (`puzzle`) CAPTCHA challenge requires the user to drag a puzzle piece to a target location. The server validates that the piece was placed within tolerance of the correct position, and the drag telemetry is recorded for behavioral analysis.
45+
46+
The Puzzle CAPTCHA type does not interrogate the user's browser environment, use `frictionless` for that (above).
47+
4248
## Set Type Implicitly
4349

4450
### 1. Set the type in the widget configuration
@@ -74,7 +80,7 @@ document.getElementById('procaptcha-script').addEventListener('load', function (
7480
siteKey: 'YOUR_SITE_KEY',
7581
theme: 'dark',
7682
callback: onCaptchaVerified,
77-
captchaType: 'image', // `pow` or leave blank for `frictionless`
83+
captchaType: 'image', // `pow`, `puzzle`, or leave blank for `frictionless`
7884
})
7985
})
8086
```

src/i18n/en/nav.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ export default [
7676
slug: 'advanced/audit',
7777
key: 'advanced/audit',
7878
},
79+
{
80+
text: 'Spam Filter',
81+
slug: 'advanced/spam-filter',
82+
key: 'advanced/spam-filter',
83+
},
7984
{text: 'Framework integrations', header: true, type: 'learn', key: 'framework-integrations'},
8085
{
8186
text: 'Angular Integration',

0 commit comments

Comments
 (0)