CORS Configuration
This guide covers configuring Cross-Origin Resource Sharing (CORS) for License Monitor to allow web applications to access the API from different domains.
Understanding CORS
Section titled “Understanding CORS”CORS is a security mechanism that controls which web origins can access your API:
┌─────────────────────────────────────────────────────────────────┐│ Browser Security Model │├─────────────────────────────────────────────────────────────────┤│ ││ https://dashboard.example.com (Origin A) ││ ┌───────────────────────────────────────────────────────────┐ ││ │ Dashboard Application │ ││ │ │ ││ │ fetch('https://api.example.com/api/licenses') │ ││ │ │ │ ││ └───────────────────────────┼────────────────────────────────┘ ││ │ ││ ▼ ││ ┌───────────────────────────────────────────────────────────┐ ││ │ Preflight Request │ ││ │ OPTIONS /api/licenses │ ││ │ Origin: https://dashboard.example.com │ ││ └───────────────────────────┬────────────────────────────────┘ ││ │ ││ ▼ ││ https://api.example.com (Origin B) ││ ┌───────────────────────────────────────────────────────────┐ ││ │ License Monitor API │ ││ │ │ ││ │ CORS Headers: │ ││ │ Access-Control-Allow-Origin: https://dashboard.example.com│ ││ │ Access-Control-Allow-Methods: GET, POST │ ││ │ Access-Control-Allow-Headers: X-API-Key, Content-Type │ ││ └───────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────┘Configuration
Section titled “Configuration”License Monitor CORS Settings
Section titled “License Monitor CORS Settings”Configure CORS in config.toml:
[api]enabled = truebind_address = "127.0.0.1"bind_port = 8080
# CORS Configurationcors_enabled = truecors_origins = [ "https://dashboard.example.com", "https://admin.example.com"]cors_methods = ["GET", "POST", "OPTIONS"]cors_headers = ["X-API-Key", "Content-Type", "Authorization"]cors_max_age = 86400cors_credentials = trueConfiguration Options
Section titled “Configuration Options”| Option | Description | Default |
|---|---|---|
cors_enabled | Enable/disable CORS | false |
cors_origins | Allowed origins (exact match) | [] |
cors_methods | Allowed HTTP methods | ["GET", "POST", "OPTIONS"] |
cors_headers | Allowed request headers | ["Content-Type"] |
cors_max_age | Preflight cache duration (seconds) | 86400 |
cors_credentials | Allow credentials (cookies, auth) | false |
Common Configurations
Section titled “Common Configurations”Single Dashboard
Section titled “Single Dashboard”[api]cors_enabled = truecors_origins = ["https://dashboard.example.com"]cors_credentials = trueMultiple Dashboards
Section titled “Multiple Dashboards”[api]cors_enabled = truecors_origins = [ "https://dashboard.example.com", "https://dashboard-staging.example.com", "https://admin.example.com"]Development Environment
Section titled “Development Environment”[api]cors_enabled = truecors_origins = [ "http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:3000"]All Local Development
Section titled “All Local Development”For local development only:
[api]cors_enabled = truecors_allow_any_origin = true # DEVELOPMENT ONLYCORS Headers
Section titled “CORS Headers”Response Headers
Section titled “Response Headers”License Monitor sends these CORS headers:
| Header | Description |
|---|---|
Access-Control-Allow-Origin | Allowed origin |
Access-Control-Allow-Methods | Allowed methods |
Access-Control-Allow-Headers | Allowed headers |
Access-Control-Max-Age | Preflight cache time |
Access-Control-Allow-Credentials | Allow credentials |
Access-Control-Expose-Headers | Exposed headers |
Preflight Response Example
Section titled “Preflight Response Example”HTTP/1.1 204 No ContentAccess-Control-Allow-Origin: https://dashboard.example.comAccess-Control-Allow-Methods: GET, POST, OPTIONSAccess-Control-Allow-Headers: X-API-Key, Content-Type, AuthorizationAccess-Control-Max-Age: 86400Access-Control-Allow-Credentials: trueReverse Proxy CORS
Section titled “Reverse Proxy CORS”If using a reverse proxy, configure CORS at the proxy level:
server { location /api/ { # Handle preflight if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://dashboard.example.com' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'X-API-Key, Content-Type, Authorization' always; add_header 'Access-Control-Max-Age' 86400 always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; }
# Add CORS headers to all responses add_header 'Access-Control-Allow-Origin' 'https://dashboard.example.com' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Expose-Headers' 'X-Request-ID' always;
proxy_pass http://license_monitor; }}Dynamic Origin Handling
Section titled “Dynamic Origin Handling”# Map valid originsmap $http_origin $cors_origin { default ""; "https://dashboard.example.com" $http_origin; "https://admin.example.com" $http_origin;}
server { location /api/ { if ($cors_origin = "") { return 403; }
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Credentials' 'true' always; # ... rest of configuration }}Troubleshooting
Section titled “Troubleshooting”Common Errors
Section titled “Common Errors”Error: “No ‘Access-Control-Allow-Origin’ header”
Access to fetch at 'https://api.example.com/api/health' from origin'https://dashboard.example.com' has been blocked by CORS policySolution: Add the origin to cors_origins:
cors_origins = ["https://dashboard.example.com"]Error: “Credentials not supported with wildcard origin”
The value of the 'Access-Control-Allow-Origin' header must not be '*'when the request's credentials mode is 'include'Solution: Use specific origin instead of wildcard:
cors_origins = ["https://dashboard.example.com"]cors_credentials = trueError: “Method not allowed”
Method POST is not allowed by Access-Control-Allow-MethodsSolution: Add the method to allowed methods:
cors_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]Testing CORS
Section titled “Testing CORS”# Test preflight requestcurl -X OPTIONS \ -H "Origin: https://dashboard.example.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: X-API-Key, Content-Type" \ -v https://api.example.com/api/licenses
# Test actual requestcurl -X GET \ -H "Origin: https://dashboard.example.com" \ -H "X-API-Key: your-api-key" \ -v https://api.example.com/api/healthBrowser Testing
Section titled “Browser Testing”// Test CORS from browser consolefetch('https://api.example.com/api/health', { method: 'GET', headers: { 'X-API-Key': 'your-api-key', }, credentials: 'include',}).then(response => response.json()).then(data => console.log('Success:', data)).catch(error => console.error('CORS Error:', error));Security Considerations
Section titled “Security Considerations”Origin Validation
Section titled “Origin Validation”- Always validate origins against a whitelist
- Never reflect the Origin header without validation
- Use exact domain matching, not pattern matching
Credentials
Section titled “Credentials”When cors_credentials = true:
- Cookies are sent with requests
- Authorization headers are included
- Origin cannot be
*
Sensitive Operations
Section titled “Sensitive Operations”Consider stricter CORS for sensitive endpoints:
# Strict CORS for admin endpoints[api.cors_overrides."/api/admin"]cors_origins = ["https://admin.example.com"]cors_methods = ["GET", "POST"]Next Steps
Section titled “Next Steps”- Rate Limiting - Protect against abuse
- API Authentication - Secure API access
- Network Security - Network configuration