Skip to content

Allowed origins

Allowed origins (a domain allowlist) make sure your public key only works on your own websites. Every public-key request is checked against the list using the browser’s Origin header (or Referer when Origin is missing). Requests from other origins are rejected with 403 origin_not_allowed and don’t use credits.

Allowed origins are available on every plan, including Free.

Owners and admins add entries in Security → Allowed websites: type a domain, click Add (one entry at a time, up to 100) and save. Entries are matched against the host of the request’s origin; the scheme (http/https) doesn’t matter.

Entry Matches
example.com https://example.com and http://example.com on any port – but not www.example.com
*.example.com any subdomain: www.example.com, shop.eu.example.com – but not example.com itself
localhost local development on any port
localhost:3000 only that port
127.0.0.1 local development via IP
my-app.pages.dev preview deployments on a hosting provider

Add both example.com and *.example.com if you need the apex domain and subdomains.

Native apps (React Native, Flutter, Swift, Kotlin) send neither Origin nor Referer, so the public API rejects them by default (like EmailJS, which blocks non-browser requests unless you enable them). To send from an app with the public key, turn on Security → Mobile apps → Allow mobile apps (off by default) and click Save. Then:

Request Mobile apps off, list empty Mobile apps off, list set Mobile apps on, list empty Mobile apps on, list set
Origin / Referer on the list accepted accepted accepted accepted
Origin / Referer not on the list accepted 403 origin_not_allowed accepted 403 origin_not_allowed
no Origin and no Referer (native app, server, curl) 403 origin_not_allowed 403 origin_not_allowed accepted accepted
Origin: null accepted 403 origin_not_allowed accepted 403 origin_not_allowed

The error for a request without Origin has details.reason: "no_origin". Allowing mobile apps also decides which CAPTCHA key verifies such requests: only with the switch on are they checked against Frontmail’s shared mobile widget (see Turnstile).

Requests that carry an Origin or Referer are always checked against the list, so turning the switch on doesn’t weaken the protection of your websites. Origin: null (sandboxed iframes, file:// pages) isn’t treated as a mobile app. Turnstile is still required for every template that has it turned on. The change is recorded in the audit log as security.updated.

Browsers set Origin themselves and scripts can’t fake it, so the allowlist stops other websites from embedding your key. A script running outside a browser (curl, a bot) can send any Origin header, though. That’s why allowed origins are one layer, combined with Turnstile, rate limits and bot protection.

Requests with a private key skip the origin check – they come from servers, which have no origin. A private-key request that does carry an Origin header comes from a web page, where every visitor can read the key; it is rejected with 403 private_key_in_browser unless you enable Allow private keys in browsers (see Keys).

  • Works locally, fails in production: add the production domain (and www. variant).
  • Fails in a native app or from a server script (403 origin_not_allowed, details.reason: "no_origin"): turn on Allow mobile apps for apps; on servers use a private key.
  • Fails in a mobile WebView or browser extension: the origin may be null, file:// or chrome-extension://…. Send from your backend with a private key instead.
  • Fails behind a privacy extension that strips Referer: modern browsers always send Origin on POST requests, so this is rare; check the request in DevTools.