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.
Configure
Section titled “Configure”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.
Mobile apps
Section titled “Mobile apps”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.
What it does and doesn’t protect
Section titled “What it does and doesn’t protect”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).
Troubleshooting
Section titled “Troubleshooting”- 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://orchrome-extension://…. Send from your backend with a private key instead. - Fails behind a privacy extension that strips
Referer: modern browsers always sendOriginonPOSTrequests, so this is rare; check the request in DevTools.