Docs / Security / Domain whitelist
How Playtcha domain whitelisting works
Public site keys are meant to be embedded in HTML, which means they are world-readable by design. The domain whitelist is what stops someone from copying your public key onto another site and issuing challenges from their own browser context.
Overview
- The browser sends an
Originheader to the widget endpoints:/v1/challenge,/v1/render-plan, and/v1/complete. - Playtcha extracts the host from that header and compares it against the project’s allowed-domain rules.
- If the host is allowed, challenge issuance, render planning, or completion continues.
- If the host is rejected, Playtcha returns
403with a specific origin failure code and a safe hint.
The origin check is enforced on the three widget endpoints only. The server-to-server /v1/verify call is identified by your secret key, not by Origin, so it does not enforce the domain whitelist. That is by design: your backend can run anywhere (a container, a serverless function, a cron job) and still verify a token that was minted in a whitelisted browser.
1. Matching rules
Matching is host-based and case-insensitive. Protocol and port are stripped before comparison.
| Rule | Matches | Does not match |
|---|---|---|
example.com | example.com | app.example.com |
*.example.com | app.example.com, a.b.example.com | example.com |
Wildcards cover subdomains only. They do not cover the apex domain itself.
2. Why Origin is canonical
On the widget endpoints (/v1/challenge, /v1/render-plan, /v1/complete), the Origin header is the only source of truth for domain validation. Playtcha deliberately does not trust body fields like domain, and it deliberately does not fall back to Referer when Origin is missing.
- Client-supplied body fields are easy to fake.
Refereris easy to strip and is not the browser contract we want to defend.- Missing
Originmeans the request is not coming from the browser context Playtcha expects.
That is why server-side or proxy-driven requests without a real browser Origin are rejected on the public widget endpoints. The server-to-server /v1/verify endpoint does not check Origin — it is authenticated by your secret key and the result token is already bound to a whitelisted origin from the challenge phase.
3. Localhost and dev mode
Local development is intentionally stricter than many products. localhost and 127.0.0.1 are only allowed when both conditions are true:
- The project has dev mode enabled.
- The localhost host is explicitly listed in the allowed domains.
Dev mode alone does not implicitly allow every local origin. That keeps the rule explicit and easier to debug.
4. Common failure reasons
| Code | Meaning |
|---|---|
origin_missing_origin | No Origin header was present, so Playtcha could not treat the request as a browser-issued widget call. |
origin_no_domains_configured | The project has no allowed domains configured yet. |
origin_not_whitelisted | The host is not listed in the project's allowed domains. |
origin_localhost_not_dev_mode | A localhost host was used without dev mode enabled. |
origin_localhost_not_whitelisted | Dev mode is on, but localhost is still not in the allowed-domain list. |
FAQ
Can I use a wildcard for preview deployments?
Yes, if the preview hosts follow a shared subdomain pattern that you are comfortable allowing. Use a wildcard rule carefully and deliberately.
Why not just trust Referer?
Because Referer is a weaker signal and easier to remove. The contract is Origin-only on the public widget endpoints.
What should I read next?
Pair this with token lifecycle for the token model and quickstart for the actual integration path.