-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
cfg := cors.NewConfig(
cors.WithAllowedOrigins("https://app.example.com"),
cors.WithAllowCredentials(true),
cors.WithExposedHeaders("X-Request-Id"),
cors.WithMaxAge(10*time.Minute),
)
app := rex.New(cors.WithCORS(cfg))cors.WithCORS(nil) takes the defaults — which allow no origin.
type Config struct {
Policy rextension.OriginPolicy
AllowedMethods []string
AllowedHeaders []string
ExposedHeaders []string
MaxAge time.Duration
}type OriginPolicy struct {
AllowedOrigins []string
AllowCredentials bool
}Origins are matched exactly — scheme, host and port:
"https://app.example.com" // right
"app.example.com" // never matches: no scheme
"https://app.example.com:8443" // a different origin than the same host on 443There is no pattern or wildcard-subdomain matching, deliberately. A naive suffix
check for .example.com matches evil-example.com; a prefix check for
https://app.example matches https://app.example.attacker.com. Both have
appeared in real advisories.
"*" allows any origin and is valid only without credentials. The
combination is refused at startup rather than emitted, because the Fetch
specification requires a concrete origin on a credentialed response and browsers
discard anything else — a silent failure that reads as a server bug from the
client's side.
Default: empty — nothing allowed.
Advertised in Access-Control-Allow-Methods on a preflight response.
Leave it empty. An empty list means the router's own Allow set for that
path is used, which is derived from the routes that actually exist and therefore
cannot drift — and a route added later is advertised without anyone remembering
to update a list.
Set it only when you deliberately want to advertise less than you serve.
Default: empty.
The request headers a client may send, advertised in
Access-Control-Allow-Headers.
Empty echoes the requested headers back. That is permissive but honest: a browser only asks for headers the page is actually trying to send, and an allowlist that omits one produces a failure the developer reads as "CORS is broken" rather than as a policy decision.
Set it when you want the allowlist to be an actual policy:
cors.WithAllowedHeaders("Content-Type", "Authorization", "X-Request-Id")Default: empty.
Response headers a cross-origin script may read, via
Access-Control-Expose-Headers. Without this a script can read only the
CORS-safelisted response headers.
The default is not empty:
[]string{"X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "Retry-After"}Those are sent by rextension-ratelimit on every response and are useless to a
cross-origin client unless exposed — a client that cannot read its remaining
quota discovers the limit by exceeding it.
⚠ WithExposedHeaders replaces the defaults rather than adding to them.
Include the X-RateLimit-* headers explicitly if you still want them:
cors.WithExposedHeaders(
"X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "Retry-After",
"X-Request-Id",
)How long a browser may cache a preflight result.
Default: 10 minutes. Browsers cap this — Chrome at 2 hours, Firefox at 24 — so a larger value is silently clamped rather than honoured.
WithAllowedOrigins(origins…) |
set the exact origins permitted |
WithAllowCredentials(bool) |
permit cookies and HTTP authentication |
WithPolicy(rextension.OriginPolicy) |
set the whole policy at once, for sharing with CSRF |
WithAllowedMethods(methods…) |
override the advertised methods |
WithAllowedHeaders(headers…) |
set the request headers a client may send |
WithExposedHeaders(headers…) |
replace the readable response headers |
WithMaxAge(d) |
preflight cache duration |
NewConfig(opts…) applies them over NewDefaultConfig() and tolerates a nil
option in the list.
Policy.Valid() is checked in OnInitialize, and a contradictory policy —
"*" with credentials, a bare hostname with no scheme — aborts startup:
cors: origin "app.example.com" is not a valid origin: missing scheme
That is deliberate. Both mistakes otherwise produce a policy that looks configured and allows nothing, discovered only from a browser console.
ext := cors.NewCORSExtension(cfg).(interface{ Policy() rextension.OriginPolicy })
policy := ext.Policy()Useful for handing the same policy to the CSRF configuration when it was built inside the CORS config rather than outside it.
rextension-cors — Cross-Origin Resource Sharing for Rex · MIT · © 2026 Kryovyx
Ecosystem