CORS
Octopus ships a complete Cross-Origin Resource Sharing (CORS) middleware
(octopus_middleware::Cors) that handles preflight OPTIONS requests and adds
Access-Control-* headers to responses. It is configured globally (top-level
cors) with optional per-route overrides (routes[].cors).
Setting the top-level cors block adds the CORS middleware to the request
chain. Once enabled, a matched route's routes[].cors override (attached as
the MatchedRouteCors request extension) replaces the global policy for that
route. If no top-level cors block is configured, the middleware is not
added — and per-route cors has no effect — so set a (possibly restrictive)
global cors block to turn CORS handling on.
What the middleware does01
The Cors middleware:
Resolves an effective config: a per-route
MatchedRouteCorsoverride if present in request extensions, otherwise the global configured policy.For
OPTIONSpreflight: returns204 No ContentwithAccess-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Max-Age, and (if enabled)Access-Control-Allow-Credentials.For other requests: runs the inner chain, then adds
Access-Control-Allow-Origin(and exposed-headers / credentials headers) to the response.
Origin handling
If
allowed_originscontains"*"and credentials are not allowed, the response usesAccess-Control-Allow-Origin: *.If
allowed_originscontains"*"and credentials are allowed, the request's ownOriginis echoed back (since*is invalid with credentials).Otherwise the request
Originis echoed only when it is inallowed_origins.
Configuration schema02
Global: top-level cors
Setting this block enables CORS handling for the whole gateway.
| Key | Type | Default | Description |
allowed_origins | list<string> | [] | Allowed origins ("*" for any) |
allowed_methods | list<string> | [GET, POST, PUT, DELETE, OPTIONS] | Allowed methods |
allowed_headers | list<string> | [] | Allowed request headers |
exposed_headers | list<string> | [] | Headers exposed to the browser |
max_age | int (seconds) | 3600 | Preflight cache lifetime |
allow_credentials | bool | false | Allow cookies / Authorization |
cors:
allowed_origins:
- https://app.example.com
allowed_methods: [GET, POST, PUT, DELETE, OPTIONS]
allowed_headers: [Content-Type, Authorization]
exposed_headers: [X-Request-ID]
max_age: 3600
allow_credentials: falsePer-route: routes[].cors
Overrides the global policy for a matched route (requires the global cors block
to be set, which is what adds the middleware to the chain).
| Key | Type | Default | Description |
allowed_origins | list<string> | [] | Allowed origins ("*" for any) |
allowed_methods | list<string> | [] | Allowed HTTP methods |
allowed_headers | list<string> | [] | Allowed request headers |
allow_credentials | bool | false | Allow cookies / Authorization |
max_age | int (seconds) | 3600 | Preflight cache lifetime |
routes:
- path: /api/*
upstream: backend
methods: [GET, POST, OPTIONS]
cors:
allowed_origins:
- https://app.example.com
allowed_methods: [GET, POST]
allowed_headers: [Content-Type, Authorization]
allow_credentials: true
max_age: 3600Related03
Routes — where
routes[].corslives.Authentication & authorization — the top-level
corsschema reference.Middleware overview — the full request-chain order.