marko/cors
CORS middleware for Marko --- enables browser-based frontends and mobile apps to access your API by adding the correct HTTP headers automatically.
Cross-Origin Resource Sharing (CORS) headers tell browsers which origins, methods, and headers are permitted when making cross-domain requests. Without them, your API is inaccessible to JavaScript running on a different domain.
This package provides CorsMiddleware that inspects each request, validates the origin, and attaches the appropriate response headers. Preflight OPTIONS requests are handled automatically and short-circuited with a 204 response --- no controller code runs for them.
Apply the middleware per-controller or per-route using the #[Middleware] attribute.
Installation
Section titled “Installation”composer require marko/corsConfiguration
Section titled “Configuration”All options are set via environment variables and default values are defined in config/cors.php:
return [ 'allowed_origins' => array_filter(explode(',', $_ENV['CORS_ALLOWED_ORIGINS'] ?? '')), 'allowed_methods' => explode(',', $_ENV['CORS_ALLOWED_METHODS'] ?? 'GET,POST,PUT,PATCH,DELETE,OPTIONS'), 'allowed_headers' => explode(',', $_ENV['CORS_ALLOWED_HEADERS'] ?? 'Content-Type,Authorization'), 'expose_headers' => array_filter(explode(',', $_ENV['CORS_EXPOSE_HEADERS'] ?? '')), 'supports_credentials' => filter_var($_ENV['CORS_SUPPORTS_CREDENTIALS'] ?? false, FILTER_VALIDATE_BOOLEAN), 'max_age' => (int) ($_ENV['CORS_MAX_AGE'] ?? 0),];| Environment Variable | Default | Description |
|---|---|---|
CORS_ALLOWED_ORIGINS | (empty) | Comma-separated origins allowed to access the API |
CORS_ALLOWED_METHODS | GET,POST,PUT,PATCH,DELETE,OPTIONS | Comma-separated HTTP methods allowed in CORS requests |
CORS_ALLOWED_HEADERS | Content-Type,Authorization | Comma-separated request headers the browser may send |
CORS_EXPOSE_HEADERS | (empty) | Comma-separated response headers the browser may read |
CORS_SUPPORTS_CREDENTIALS | false | Whether cookies and auth headers are allowed |
CORS_MAX_AGE | 0 | Preflight cache duration in seconds (0 disables caching) |
To override defaults, publish config/cors.php into your application and modify it directly, or set the corresponding environment variables.
Applying to a Controller
Section titled “Applying to a Controller”To enable CORS for all routes in a controller, add the #[Middleware] attribute at the class level:
use Marko\Cors\Middleware\CorsMiddleware;use Marko\Routing\Attributes\Middleware;use Marko\Routing\Http\Response;
#[Middleware(CorsMiddleware::class)]class PostController{ public function index(): Response { // CORS headers added automatically }}Applying to Individual Routes
Section titled “Applying to Individual Routes”Apply the attribute on a specific method to scope CORS to that route only:
use Marko\Cors\Middleware\CorsMiddleware;use Marko\Routing\Attributes\Get;use Marko\Routing\Attributes\Middleware;use Marko\Routing\Http\Response;
class PostController{ #[Get('/posts')] #[Middleware(CorsMiddleware::class)] public function index(): Response { // CORS headers added only on this route }}Allowing Specific Origins
Section titled “Allowing Specific Origins”Configure allowed origins via the CORS_ALLOWED_ORIGINS environment variable (comma-separated):
CORS_ALLOWED_ORIGINS=https://app.example.com,https://admin.example.comWildcard Origin
Section titled “Wildcard Origin”To allow any origin (useful for fully public APIs):
CORS_ALLOWED_ORIGINS=*When a wildcard is configured, all origins are permitted.
Sending Cookies and Auth Headers
Section titled “Sending Cookies and Auth Headers”To allow browsers to send credentials (cookies, Authorization headers):
CORS_SUPPORTS_CREDENTIALS=trueWhen enabled, Access-Control-Allow-Credentials: true is added to each response.
Preflight Caching
Section titled “Preflight Caching”Set CORS_MAX_AGE to avoid repeated preflight requests:
CORS_MAX_AGE=3600This adds Access-Control-Max-Age: 3600 to preflight responses, telling the browser to cache the result for one hour.
API Reference
Section titled “API Reference”CorsMiddleware
Section titled “CorsMiddleware”use Marko\Routing\Http\Request;use Marko\Routing\Http\Response;
public function handle(Request $request, callable $next): Response;Processes the request: validates the Origin header, handles OPTIONS preflight requests with a 204 response, and appends CORS headers to all other responses from allowed origins. Implements MiddlewareInterface.
CorsConfig
Section titled “CorsConfig”use Marko\Config\ConfigRepositoryInterface;
public function __construct(private ConfigRepositoryInterface $config);
public function allowedOrigins(): array;public function allowedMethods(): array;public function allowedHeaders(): array;public function exposeHeaders(): array;public function supportsCredentials(): bool;public function maxAge(): int;Reads CORS configuration from the config repository under the cors.* namespace. All methods throw ConfigNotFoundException if the underlying config key is missing.
CorsException
Section titled “CorsException”public function getContext(): string;public function getSuggestion(): string;Base exception for CORS-related errors. Extends MarkoException --- carries a context (where the error occurred) and a suggestion (how to fix it).