Configuring CORS Settings

1. Setting Allowed Origins

ValueMeaningSecurity
*Any originUnsafe with credentials
https://app.comSingle originSafe
https://*.app.comSubdomain wildcardSafe (validate)
Regex^https://.*\.app\.com$Dynamic

Example: CORS plugin (Kong)

plugins:
  - name: cors
    config:
      origins: ["https://app.example.com", "https://admin.example.com"]
      methods: [GET, POST, PUT, DELETE, OPTIONS]
      headers: [Accept, Authorization, Content-Type, X-Request-ID]
      exposed_headers: [X-Total-Count, X-Request-ID]
      credentials: true
      max_age: 3600
      preflight_continue: false

2. Configuring Allowed Methods

MethodSimple Request?
GET, HEAD, POSTYes (no preflight)
PUT, PATCH, DELETENo (preflight required)
OPTIONSUsed for preflight

3. Setting Allowed Headers

HeaderNotes
AuthorizationRequired for bearer tokens
Content-TypeNon-simple values need preflight
X-Requested-WithAJAX detection
X-CSRF-TokenCSRF protection
*Allow all Modern

4. Configuring Exposed Headers

By default browsers expose only safelisted headers. Access-Control-Expose-Headers grants JS access to custom ones.

HeaderUse Case
X-Total-CountPagination total
X-RateLimit-*Quota display
LocationCreated resource URL
ETagCache validation

5. Setting Max Age for Preflight Requests

max_ageEffect
0No cache, preflight every time
60010 min (Chrome cap)
72002h (Firefox cap)
8640024h (Safari cap)

6. Enabling Credentials Support

Warning: When credentials: true, origin MUST be specific (no *). Browsers reject otherwise.
SettingEffect
Access-Control-Allow-Credentials: trueSends cookies, Auth header
Fetch credentials: "include"Client opt-in
XHR withCredentials = trueLegacy client opt-in

7. Configuring Preflight Caching

Example: Preflight response

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 3600
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers

8. Using Dynamic CORS Configuration

Example: Per-tenant origin allowlist

local tenant = kong.request.get_header("x-tenant-id")
local origins = tenant_config[tenant].cors_origins
local req_origin = kong.request.get_header("origin")
if origins[req_origin] then
  kong.response.set_header("Access-Control-Allow-Origin", req_origin)
  kong.response.set_header("Vary", "Origin")
end

9. Implementing CORS Error Handling

ErrorCauseFix
No 'Access-Control-Allow-Origin'Header missingAdd origin to allowlist
Credentials + wildcardSpec violationUse specific origin
Preflight fails (non-2xx)OPTIONS blocked by authAllow OPTIONS without auth
Method not allowedVerb missing from listExtend allowed_methods

10. Setting Origin Validation Rules

RuleReason
Reject null originFile:// or sandboxed iframe
Validate schemeBlock http:// on prod
Always include Vary: OriginCache correctness
Case-sensitive compareSpec compliant