Domain Configuration

Configure allowed domains and CORS settings for your forms

configuration
domains cors security origins configuration

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.COMhttps://example.com
  • No trailing slash: https://example.com/https://example.com
  • Preserve ports: https://example.com:8080 (port kept)

Adding Allowed Domains

Dashboard Configuration

  1. Navigate to your form settings
  2. Go to "Security" or "Allowed Domains" section
  3. Add each domain that should be allowed to submit
  4. 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:

  1. Check actual Origin header:

    // Add this to your page to see the origin
    console.log('Current origin:', window.location.origin);
  2. Inspect network request:

    • Open browser DevTools
    • Go to Network tab
    • Submit form
    • Check the Origin header in the request
  3. 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:

  1. Remove development/staging domains from production forms
  2. Remove domains for discontinued sites
  3. Update domains when migrating to new hosts
  4. 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:

  1. Create forms programmatically via API
  2. Add customer domains to their specific form
  3. Use customer-specific form IDs
  4. 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:

  1. Add new domain to allowed list
  2. Test submissions from new domain
  3. Update all form references
  4. Remove old domain after migration
  5. 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