Domain Configuration
Configure allowed domains and CORS settings for your forms
Domain Configuration
Configure allowed domains and CORS settings for your forms.
Overview
Domain configuration is a critical security feature that controls which websites can submit to your forms. This prevents unauthorized usage and potential abuse of your form endpoints.
How Domain Validation Works
FormFeeder validates the Origin header of incoming requests against your form's allowed domains list. This happens automatically for all form submissions.
Origin Header
The browser automatically includes an Origin header with cross-origin requests:
POST /v1/form/abc123 HTTP/1.1
Host: api.formfeeder.io
Origin: https://www.example.com
Content-Type: application/json
Domain Matching
FormFeeder performs exact matching with normalization:
- Lowercase:
https://Example.COM→https://example.com - No trailing slash:
https://example.com/→https://example.com - Preserve ports:
https://example.com:8080(port kept)
Adding Allowed Domains
Dashboard Configuration
- Navigate to your form settings
- Go to "Security" or "Allowed Domains" section
- Add each domain that should be allowed to submit
- Save changes
API Configuration
{
"allowedDomains": [
"https://www.example.com",
"https://example.com",
"https://app.example.com"
]
}
Private Form Creation
When creating private forms, specify the allowed domain:
curl -X POST "https://api.formfeeder.io/v1/forms/from-email" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"allowedDomain": "https://www.example.com"
}'
Domain Formats
Valid Domain Examples
https://www.example.com
https://example.com
https://subdomain.example.com
https://example.com:8080
http://localhost:3000
http://localhost:8080
https://my-app.vercel.app
https://username.github.io
Invalid Formats
example.com (missing protocol)
https://example.com/ (trailing slash will be removed)
https://example.com/path (paths not allowed)
*.example.com (wildcards not supported)
http://*.localhost (wildcards not supported)
Development vs Production
Local Development
For local development, add your development server domains:
{
"allowedDomains": [
"http://localhost:3000",
"http://localhost:3001",
"http://127.0.0.1:3000",
"https://www.example.com"
]
}
Staging Environments
Include your staging URLs:
{
"allowedDomains": [
"https://staging.example.com",
"https://dev.example.com",
"https://preview-123.netlify.app",
"https://www.example.com"
]
}
Production Only
For production forms, keep the list minimal:
{
"allowedDomains": [
"https://www.example.com",
"https://example.com"
]
}
Common Scenarios
Multiple Subdomains
Each subdomain must be explicitly added:
{
"allowedDomains": [
"https://www.example.com",
"https://app.example.com",
"https://blog.example.com",
"https://shop.example.com"
]
}
Different Protocols
HTTP and HTTPS are treated as different origins:
{
"allowedDomains": [
"http://example.com", // For redirects or legacy
"https://example.com" // Primary secure site
]
}
CDN and Static Hosts
Include CDN and hosting platform domains:
{
"allowedDomains": [
"https://www.example.com",
"https://example.netlify.app",
"https://example.vercel.app",
"https://example.github.io"
]
}
Troubleshooting Domain Issues
Error 401: Origin Not Allowed
Common causes:
- Domain not in allowed list
- Protocol mismatch (http vs https)
- Subdomain mismatch (www vs non-www)
- Port number differences
- Case sensitivity (though we normalize)
Debugging steps:
Check actual Origin header:
// Add this to your page to see the origin console.log('Current origin:', window.location.origin);Inspect network request:
- Open browser DevTools
- Go to Network tab
- Submit form
- Check the Origin header in the request
Test with curl:
curl -X POST "https://api.formfeeder.io/v1/form/abc123" \ -H "Origin: https://www.example.com" \ -H "Content-Type: application/json" \ -d '{"test": "data"}' \ -v
Missing Origin Header
Some environments don't send Origin headers:
- Native mobile apps: May not send Origin
- Server-to-server: No Origin header
- Old browsers: Limited CORS support
Solutions:
- Use API key authentication for server requests
- Add explicit Origin header in mobile apps
- Use form-encoded submissions for better compatibility
Security Best Practices
Principle of Least Privilege
Only add domains that actually need form access:
// Good: Minimal necessary domains
{
"allowedDomains": [
"https://www.example.com"
]
}
// Avoid: Overly permissive
{
"allowedDomains": [
"https://www.example.com",
"https://old.example.com",
"https://test.example.com",
"http://localhost:3000",
"https://dev.example.com"
]
}
Regular Audits
Periodically review and clean up allowed domains:
- Remove development/staging domains from production forms
- Remove domains for discontinued sites
- Update domains when migrating to new hosts
- Check for typos or incorrect domains
Environment Separation
Use different forms for different environments:
- Production form: Only production domains
- Staging form: Staging and test domains
- Development form: Local development domains
Advanced Configuration
Multiple Forms Strategy
For complex applications, consider using multiple forms:
// Environment-based form selection
const FORM_IDS = {
production: 'prod_abc123',
staging: 'stage_def456',
development: 'dev_ghi789'
};
const environment = process.env.NODE_ENV || 'development';
const formId = FORM_IDS[environment];
Dynamic Domain Validation
For SaaS applications with custom domains:
- Create forms programmatically via API
- Add customer domains to their specific form
- Use customer-specific form IDs
- Implement domain management in your admin panel
API Integration
Manage domains programmatically:
// Add domain to existing form
await fetch(`https://api.formfeeder.io/v1/forms/abc123/domains`, {
method: 'POST',
headers: {
'Authorization': 'Bearer your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
domain: 'https://new.example.com'
})
});
// Remove domain
await fetch(`https://api.formfeeder.io/v1/forms/abc123/domains/https%3A%2F%2Fold.example.com`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer your-api-key'
}
});
CORS Headers
FormFeeder automatically sets appropriate CORS headers based on your domain configuration:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Preflight Requests
For certain content types, browsers send preflight OPTIONS requests:
OPTIONS /v1/form/abc123 HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
FormFeeder handles these automatically when the domain is allowed.
Monitoring and Logs
Failed Domain Attempts
Monitor unauthorized access attempts:
- Dashboard Analytics: View blocked requests by domain
- Security Logs: Track repeated unauthorized attempts
- Alerts: Get notified of unusual domain activity
Metrics Available
- Total requests vs allowed requests
- Top blocked domains
- Geographic distribution of blocked requests
- Time-based patterns of access attempts
Migration Guide
Updating Existing Forms
When changing domains:
- Add new domain to allowed list
- Test submissions from new domain
- Update all form references
- Remove old domain after migration
- Monitor for any missed references
Bulk Updates
For multiple forms:
# Script to update multiple forms
FORMS=("abc123" "def456" "ghi789")
NEW_DOMAIN="https://new.example.com"
for form_id in "${FORMS[@]}"; do
curl -X POST "https://api.formfeeder.io/v1/forms/$form_id/domains" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"domain\": \"$NEW_DOMAIN\"}"
done