Custom rules
Sometimes the spam you actually get is specific. The same SEO agency emails the same pitch every Tuesday from the same domain. A bot reuses the same comment. A link pattern shows up across hundreds of submissions.
Custom rules let you encode that pattern once and have every future matching submission filed as spam automatically.
Three kinds of rules
Each form has three rule lists, edited on the form's settings page under Custom rules:
| Rule type | What it matches | Example |
|---|---|---|
| Regex | A regex pattern against the full payload (all fields concatenated) | `/(buy |
| Keyword | Case-insensitive substring match against any field value | outsourcing |
| Blocked email | Exact match against the email field, or any field containing an email |
spammer@example.com, *@bad-domain.com |
A submission that matches any rule on any list is filed as spam with reason custom_rule_matched:<rule>.
Regex syntax
Patterns use PCRE syntax (PHP regex). Wrap in delimiters and add flags as needed:
/(?i)\b(crypto|nft|web3)\s+(advisor|consultant|agency)\b/
/https?:\/\/[a-z0-9-]+\.(xyz|top|click)\b/i
/[\p{Cyrillic}]{10,}/u # Long Cyrillic strings (often spam in EN-only forms)
The pattern is matched against a string of all field values joined with newlines. So a regex hit on the message body or the name field both count.
Keyword syntax
Keywords are plain strings, case-insensitive substring match. No wildcards, no regex — if you need either, use the regex list instead.
Good keyword candidates:
- Brand names of recurring spammers
- Specific phrases from copy-paste pitches ("I noticed your website")
- Domains and TLDs you'd never expect from a real user
Blocked-email syntax
Three forms accepted:
someone@example.com— exact match on a single address.*@example.com— any address at that domain.*@*.example.com— any subdomain of that domain.
Match is case-insensitive. The check runs against any field that contains an @ and parses as an email address, not just the field literally named email.
Performance considerations
Rules run synchronously, before the submission is persisted. They're cheap, but not free.
- Regex is the most expensive. A pathological regex on a 50KB payload will block the request thread for seconds. Keep patterns simple, anchor them, and avoid
.*at both ends. - Keyword is O(n × m) where n is field length and m is keyword count. Stays fast up to a few hundred keywords.
- Blocked email is a hash lookup. Effectively free even at thousands of entries.
If you're approaching the per-form rule cap (100 regex, 500 keyword, 5000 email), ask yourself whether AI moderation would do the same job with less maintenance.
Common patterns
# Match anything that looks like a crypto pitch
/(?i)\b(crypto|defi|web3|nft|blockchain)\s+(deal|advisor|opportunity)\b/
# Match any URL with a sketchy TLD
/https?:\/\/[^\s]+\.(xyz|top|click|bid|loan)\b/i
# Match obvious SEO outreach
/(?i)\b(guest\s+post|link\s+exchange|backlink|seo\s+services)\b/
# Match phone numbers in the message field (most legit forms have separate phone fields)
/(\+?\d[\d\s\-]{8,}\d)/
# Long runs of non-Latin script in an English-only form
/[\p{Cyrillic}\p{Han}]{15,}/u
Combining with AI moderation
Custom rules and AI moderation stack. Rules run first (cheap, deterministic). If no rule matches, the submission falls through to AI moderation (slower, fuzzier).
The dashboard shows hit counts per rule over the last 30 days. If a rule has zero hits in a month, delete it — every rule you keep adds latency to every submission.