Skip to content
Zever
Home

CORS

Allow browser calls from other origins with strict, fail-closed cross-origin headers.

1 min readEdit on GitHub

middleware.CORS enforces strict cross-origin rules as HTTP middleware. Use it when browsers call your API from another origin; requests without Origin pass through.

Zever owns no cors config section. Decode your app’s block into middleware.Options (snake_case tags) and wire it outermost with r.Use before any Handle:

cors:
allowed_origins: ["https://example.com"]
allowed_methods: ["GET", "POST", "PUT", "PATCH", "DELETE"]
allowed_headers: ["Content-Type", "Authorization"]
exposed_headers: ["X-Request-Id"]
allow_credentials: true
max_age: 10m
main.go
import (
"time"
"github.com/zenta-dev/zever/core/middleware"
)
handler := middleware.CORS(middleware.Options{
AllowedOrigins: []string{"https://example.com"},
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
ExposedHeaders: []string{"X-Request-Id"},
AllowCredentials: true,
MaxAge: 10 * time.Minute,
})(mux)

MaxAge is a time.Duration, rendered as delay-seconds (10m becomes 600).

Request Response
No Origin Passes through, no CORS headers.
Allowed origin, simple Handler runs; Allow-Origin echoes origin, Vary: Origin.
Allowed origin, preflight match 204 with Allow-Origin, Allow-Methods, Allow-Headers, Max-Age.
Allowed origin, preflight mismatch 403, no Allow-Origin.
Disallowed origin Never reflected; simple requests pass, preflights fall through.

Options.Validate rejects fail-open shapes: empty origin/method lists, blank entries, * with AllowCredentials, negative MaxAge; invalid options fail closed with 500 {"error":"internal error"}. A wildcard * without credentials echoes * for any origin. DefaultCORSMaxAge (0) omits Max-Age, so browsers preflight every request.

Keep CORS outermost so preflights skip logging and rate limiting, and call r.Use before Handle, since stdhttp snapshots middleware per route. CORS is HTTP-only (chain order).

Preflights are logged or rate-limited

CORS sits inside RequestLogger or RateLimit. Move it outermost.

The browser blocks a valid origin

Not an exact allowlist match. Add the exact origin.

Preflight returns an unexpected 403

The method or a requested header is outside the allowlist (header matching is case-insensitive). Add the missing entry.

Every CORS request returns 500

Options is invalid, often * with credentials.