Skip to content

blockHeadless and block list

Besides allowed origins and Turnstile, Frontmail has two lightweight filters that you can enable in the SDK, in the dashboard, or both. The dashboard settings live on the Security page (owners and admins can change them).

Rejects sends from automated, headless browsers (Puppeteer, Playwright, Selenium and similar), which are often used by form spam bots.

init({ publicKey: 'pk_4f2a…', blockHeadless: true });
  • In the SDK, the check runs in the browser before any request is made: send() rejects with a BlockedError with code headless_blocked. No request, no credit.
  • In Security → Abuse protection → Block headless browsers, the API also rejects public-key requests whose user agent identifies a headless or automated browser (HeadlessChrome, Puppeteer, Playwright, Selenium, PhantomJS, jsdom …). Use both.

Legitimate users are not affected. Your own end-to-end tests are – send with a private key in tests, or disable the option on test environments.

A block list rejects sends whose recipient – or the value of a chosen param – matches an entry. Typical uses: a persistent spammer’s address, disposable email domains, a competitor scraping your forms.

init({
publicKey: 'pk_4f2a…',
blockList: {
list: ['spam@example.net', 'mailinator.com'],
watchVariable: 'email', // param or form field to check
},
});
  • Values are compared case-insensitively after trimming. An entry without @ (or starting with @, like @mailinator.com) matches every address on that domain; other entries match an email address or the watched value exactly.
  • In the SDK, the check runs before the request; a match rejects with BlockedError code recipient_blocked.
  • In Security → Block list, owners and admins enter Blocked values (one per line, up to 1,000) and optionally a Parameter to check (e.g. email, dotted paths work too). The API enforces the list for every request – public or private key, including outdated clients or scripts that don’t use the SDK – before any credit is used. It checks the rendered recipients (To/CC/BCC) and the value of the parameter to check.

The SDK list is visible in your page source; keep sensitive entries only in the dashboard list.

The SDKs can also prevent a single browser from sending too often – for example, a visitor double-clicking the submit button:

init({ publicKey: 'pk_4f2a…', limitRate: { id: 'contact', throttle: 10_000 } });

A second send within 10 seconds from the same page (or the same id) is rejected locally with BlockedError code rate_limited. This is a convenience, not a security measure – see Rate limits for server-side limits.

Security → Abuse protection → Strict parameters rejects requests that contain parameters the template’s schema doesn’t define (see Parameters). Bots that fill in every field of a form, including made-up ones, are stopped before they use credits.