Link allowlist & content lock
Your public key is visible in your website’s code, so anyone can copy it and call the API directly. For a contact form that’s fine: the email always goes to you. It’s different when a template takes its recipient from params (a newsletter confirmation, an auto-reply, an order confirmation): whoever holds the public key could then send your template – from your domain, with your branding – to any address, and fill in the text and links. That’s how phishing works.
Frontmail’s rule is therefore: dynamic recipient ⇒ locked content. You can still send to a recipient chosen in the browser, but the caller can’t turn your template into a phishing email.
When the content is locked
Section titled “When the content is locked”| Request | Locked |
|---|---|
| public key + template whose To/CC/BCC use params (Allow dynamic recipient on) | yes |
| auto-reply (always goes to the address from the form) | yes – links and sender fields |
| private key (your server) | no, unless the template has Only allow links to allowed domains |
| public key + template with a fixed recipient (contact form) | no – only the From rule applies |
A template that has Allow dynamic recipient on but fixed addresses in To/CC/BCC isn’t locked.
What a locked request must satisfy
Section titled “What a locked request must satisfy”- CAPTCHA is required. The template must have Require CAPTCHA on, otherwise the request
fails with
403 dynamic_recipient_requires_captcha. CAPTCHA uses your own Turnstile keys. If you can’t use CAPTCHA, send the template from your server with a private key. - Fixed sender. From address, From name and Reply-To must not use params –
403 dynamic_field_not_allowed. - No caller markup, short texts. Params of type HTML can’t be filled in, and every text value
is at most 500 characters (URL params 2,048; a smaller Max length on the param still
applies) –
422 invalid_template_paramswithdetails.reason: "content_locked". - Links only to allowed domains – see below –
403 link_not_allowed. - Per-recipient limit. At most 5 emails per recipient address per day (UTC), configurable
in Security → Emails per recipient per day (1–1,000). Over the limit:
429 rate_limitedwithdetails.scope: "recipient". Auto-replies share the counter – over the limit no auto-reply is created.
Every check runs before a credit is used. For auto-replies, a failed check marks the auto-reply
as failed (with the error code in history), the credit is refunded and
nothing is sent.
Allowed link domains
Section titled “Allowed link domains”A link is allowed when its domain is:
- written directly in the template (HTML, text version or subject) – not produced by a
placeholder.
https://acme.com/reset?token={{token}}allowsacme.com;{{reset_url}}orhttps://{{sub}}.acme.comallow nothing; - on your allowed origins list (Security → Allowed websites);
- on Security → Allowed link domains – one per line,
example.com(exactly that host) or*.example.com(subdomains, not the apex) – the same rules as allowed origins; - Frontmail’s own domain (the Sent via Frontmail footer on the Free plan).
mailto:, tel: and any other non-web link (javascript:, data:, …) are allowed only when the
exact same link is written in the template.
What is checked
Section titled “What is checked”After rendering, Frontmail collects every URL a mail client could open or turn into a link:
href,src,srcset,action,formaction,background,posterand other URL attributes, also inside Outlook conditional comments;- CSS
url()and@importinstyleattributes and<style>, and<meta http-equiv="refresh">; - URLs and bare domains in the HTML text, the text version and the subject – mail clients
auto-link
evil.com/loginorwww.evil.com, so these count as links too.
Before comparing, URLs are normalized the way a browser would read them: HTML entities and CSS
escapes are decoded, invisible characters removed, \ treated as /, protocol-relative //host
resolved, user info dropped (https://acme.com@evil.com is evil.com), percent-encoding decoded,
upper case and trailing dots removed, and internationalized domains compared in punycode – a
look-alike such as аcme.com with a Cyrillic а doesn’t match acme.com (the error marks it with
idn: true).
The error lists only the offending domains in details.hosts – never full URLs, which may
contain tokens.
Limits
Section titled “Limits”- Bare domains are detected with a list of known non-domain endings (
report.pdf,index.htmlandNode.jsaren’t links), so a text likeMr.Smithmay count as the domainmr.smith. Phishing-favoured endings that are also real domains (.zip,.mov) always count. - Plain e-mail addresses in the text (
jane@example.com) aren’t treated as links. - The check is about where links lead, not about the text around them. Keep texts short and your recipient limit low for public forms.
Checking links on other templates
Section titled “Checking links on other templates”Turn on Only allow links to allowed domains in the template settings (Settings → Protection) to apply the link check to every request, including private-key sends. It’s on by default for gallery templates with a dynamic recipient.
From address
Section titled “From address”For every public-key request, the From address of the template must not come from params
(otherwise 403 dynamic_field_not_allowed). A
contact form that puts the visitor’s address into Reply-To keeps working – that’s the right
place for it. The template editor warns about all of this in Checks.