better-route: CORS and preflight
Use explicit CORS policy for browser or embedded clients. Do not rely on incidental WordPress defaults when the API needs Authorization, Idempotency-Key, If-Match, X-Request-ID, or credentials.
Global middleware
use BetterRoute\Middleware\Cors\CorsMiddleware;
use BetterRoute\Middleware\Cors\CorsPolicy;
$cors = new CorsMiddleware(new CorsPolicy(
allowedOrigins: ['https://app.example.com'],
allowCredentials: true
));
$router->middleware([$cors]);
Default allowed headers include:
AuthorizationContent-TypeIdempotency-KeyIf-MatchIf-None-MatchX-Request-IDX-WP-Nonce
Default exposed headers include ETag, Idempotency-Replayed, X-RateLimit-*, and X-Request-ID.
Explicit preflight route
CorsMiddleware can short-circuit OPTIONS requests, but the router must register an OPTIONS route for that path when WordPress would not otherwise dispatch it.
$router->options('/account/payment-methods', static fn () => null)
->middleware([$cors]);
Router::options() is public by default in 0.5.0. Do not attach business handlers to preflight routes.
Rules
- Prefer an origin allowlist. Use
*only for non-credentialed public APIs. - If
allowCredentials: true, do not return wildcard origin;CorsPolicyechoes the allowed request origin. - Put CORS early in the middleware list so errors and short-circuits still get headers where possible.
- Keep allowed headers aligned with actual client needs; add custom headers deliberately.
- Keep CORS separate from authentication. CORS says which browser origins may call; auth says who the caller is.
Source refs
libraries/better-route/src/Middleware/Cors/CorsMiddleware.phplibraries/better-route/src/Middleware/Cors/CorsPolicy.phplibraries/better-route/src/Router/Router.phplibraries/better-route/tests/BuiltInMiddlewareTest.phplibraries/better-route/tests/RouterPipelineTest.php