Domain Whitelisting
Configure project-level image source domains and authorized websites to control which images can be processed and who can use your service.
OptStuff provides two layers of domain whitelisting at the project level, each serving a different purpose.
Two-Layer Architecture
| Layer | Question | Validation | Configured In |
|---|---|---|---|
Authorized Websites (allowedRefererDomains) | WHO can use this service? | HTTP Referer header | Project Settings |
Image Sources (allowedSourceDomains) | WHAT image sources can be processed? | Image URL in request | Project Settings |
Both layers are configured in the project's Settings tab under Domain Security.
Configuring Image Sources (Project Level)
- Navigate to Dashboard → Project → Settings
- In Image Sources, add the domains where your images are hosted
- Use protocol-prefixed entries (for example
https://images.example.com) - Subdomain matching: adding
https://example.comallowsexample.comand all subdomains (cdn.example.com,img.example.com, ...)
| List Status | Production Behavior | Development Behavior |
|---|---|---|
| Empty (default) | Reject all requests (fail closed) | Allow all sources (for convenience) |
| Non-empty | Only listed domains allowed | Only listed domains allowed |
Important: Always configure at least one image source domain before using OptStuff in production. In production, an empty source list rejects all requests.
Configuring Authorized Websites (Project Level)
- Navigate to Dashboard → Project → Settings
- In Authorized Websites, add your website domains
- Use protocol-prefixed entries (for example
https://app.example.com) - Subdomains are not implied for exact host entries; use wildcard when needed:
https://app.example.com→ only this hosthttps://*.example.com→ all subdomains (andexample.com)
| List Status | Behavior |
|---|---|
| Empty (default) | All referers allowed |
| Non-empty | Only listed domains allowed |
Note:
allowedRefererDomainsis a best-effort browser-side signal enforced byvalidateReferer()for hotlinking protection, not a strong authentication guarantee. WhenallowedRefererDomainsis non-empty,validateReferer()blocks requests only when aRefereris present and not allowed; requests without aReferer(for example, server-to-server calls or privacy-stripping browser policies) are intentionally allowed. For strict caller validation, use short-lived signed URLs. See Referer Security Model for details.
Wildcard Syntax
Wildcard entries use the * prefix to match all subdomains:
| Entry | Matches | Does NOT Match |
|---|---|---|
https://example.com | example.com and all subdomains (Image Sources only) | Subdomain matching in Authorized Websites |
https://*.example.com | cdn.example.com, img.example.com, example.com (required for subdomain matching in Authorized Websites) | other-site.com |
https://cdn.example.com | cdn.example.com only | img.example.com |
Wildcard semantics differ by scope: for Image Sources, https://example.com includes subdomains automatically (for example, it allows https://cdn.example.com/photo.jpg); for Authorized Websites, subdomains are not implied, so use https://*.example.com when you need to allow referers like https://app.example.com/page (while https://example.com alone only matches that exact host).
Practical Example
Project: my-company
allowedSourceDomains: [https://cdn.site-a.com, https://images.site-b.com, https://s3.amazonaws.com]
allowedRefererDomains: [https://app.site-a.com, https://*.site-b.com]All API keys in this project share the same domain restrictions. Different keys are used for credential rotation and access control — not for domain segmentation.
Environment-Specific Configuration
| Environment | Image Sources | Authorized Websites |
|---|---|---|
| Development | Empty list (allows all sources for convenience) | Empty list (allows all) |
| Staging | Same as production | Add your staging domain (https://staging.example.com) |
| Production | Explicitly list all allowed origins | Explicitly list all allowed websites |
In production, an empty allowedSourceDomains list rejects all requests (fail-closed). Always add at least one domain before going live.
Troubleshooting
| Error | Common Cause | Solution |
|---|---|---|
403 Source domain not allowed | Image origin not in project's allowed sources | Add the domain (with https:// prefix) in Dashboard → Settings → Domain Security → Image Sources |
403 Invalid referer | Your website not in allowed referer list | Add your domain in Dashboard → Settings → Domain Security → Authorized Websites |
403 on images that formerly worked | Domain settings changed or cache propagation delay | Wait ~60s for settings to propagate; verify domains are still listed |
| Images work in development but not production | Empty source list allows all in dev, rejects all in prod | Add explicit domains for production |
Common Mistakes
- Missing protocol — Entries must include
https://(e.g.,https://cdn.example.com, notcdn.example.com) - Trailing slashes — Do not include trailing slashes (use
https://cdn.example.com, nothttps://cdn.example.com/) - Port numbers — In
allowedRefererDomains, list only the hostname (for example, usehttp://localhostwith hostlocalhost, nothttp://localhost:3000) becausevalidateReferer()comparesrefererUrl.hostname(ports are ignored). If you need port-specific matching, updatevalidateReferer()to comparerefererUrl.host(or add explicit port-aware logic).
Related Documentation
- Core Concepts — Overview of the resource hierarchy
- Referer Security Model — Trust model for Referer-based protection
- Security Best Practices — Broader security recommendations
- Error Codes —
403errors for domain violations
Last updated on
CDN and Caching
How to use OptStuff with a CDN for edge-cached image delivery — cache behavior, CDN setup guides for Cloudflare, Vercel, and CloudFront, and cache invalidation strategies.
Referer Security Model
Why the browser Referer header is reliable for anti-hotlinking, why it's not a substitute for authentication, and how OptStuff uses it alongside signed URLs.