CORS in Leaf MVC
MVC HTTP access
Configure browser access from your app environment.
Leaf MVC wires CORS into the app for you, then lets production apps tighten allowed origins, methods, and headers through environment config or a published config file.
CORS is the browser security layer that decides which origins can read responses from your app. Since CORS is a common pain point for web developers, Leaf provides a first-party integration that takes care of the repetitive setup for you.
Setting Up
You can install the CORS module through the Leaf CLI or with composer.
leaf install corscomposer require leafs/corsAfter installing the CORS module, Leaf MVC will automatically set up CORS to handle all incoming requests without any limits. While this is great for development, it's not recommended for production. You can customize the CORS settings using the CORS config.
Configuring CORS
Most of the configuration options can be configured using environment variables. Here are the available options:
CORS_ALLOWED_ORIGINS='https://app.example.com'
CORS_ALLOWED_METHODS='GET,HEAD,PUT,PATCH,POST,DELETE'
CORS_ALLOWED_HEADERS='*'Origins are matched exactly, so each configured origin must be a full origin including the scheme, like https://app.example.com. Partial values like example.com will not match. To allow a whole family of origins, such as every subdomain of a site, use a regular expression written as a string:
CORS_ALLOWED_ORIGINS='/^https:\/\/(.*\.)?example\.com$/'While this is easier and allows you to easily configure different environments, it can sometimes be limiting, for example when you want to allow an array that mixes exact origins and regex strings. For this reason, you can publish your CORS configuration using the command below:
leaf config:publish corsThis will publish the CORS config file to config/cors.php, where you can customize the settings to your liking.
<?php
return [
/*
|--------------------------------------------------------------------------
| Configure allowed origins
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Allow-Origin CORS header. Possible values:
|
| * String - set origin to a specific origin, including the scheme.
| For example if you set it to "https://example.com" only requests
| from "https://example.com" will be allowed. Origins are matched
| exactly; partial values like "example.com" will not match.
|
| * Regex string - set origin to a regular expression written as a
| string, which will be tested against the request origin. If it
| matches, the request origin will be reflected. For example
| '/^https:\/\/(.*\.)?example\.com$/' will allow
| "https://example.com" and any of its subdomains.
|
| * Array - set origin to an array of valid origins. Each origin can
| be an exact origin or a regex string. For example
| ['https://example1.com', '/^https:\/\/(.*\.)?example2\.com$/']
| will accept requests from "https://example1.com" or from
| "example2.com" and its subdomains.
|
*/
'origin' => _env('CORS_ALLOWED_ORIGINS', '*'),
/*
|--------------------------------------------------------------------------
| Configure allowed HTTP methods
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Allow-Methods CORS header.
| Expects a comma-delimited string (ex: 'GET,PUT,POST') or
| an array (ex: ['GET', 'PUT', 'POST'])
|
*/
'methods' => _env('CORS_ALLOWED_METHODS', 'GET,HEAD,PUT,PATCH,POST,DELETE'),
/*
|--------------------------------------------------------------------------
| Configure allowed HTTP headers
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Allow-Headers CORS header. Expects a
| comma-delimited string (ex: 'Content-Type,Authorization') or
| an array (ex: ['Content-Type', 'Authorization']). If not specified,
| defaults to reflecting the headers specified in the request's
| Access-Control-Request-Headers header.
|
*/
'allowedHeaders' => _env('CORS_ALLOWED_HEADERS', '*'),
/*
|--------------------------------------------------------------------------
| Configure expose headers
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Expose-Headers CORS header. Expects
| a comma-delimited string (ex: 'Content-Range,X-Content-Range')
| or an array (ex: ['Content-Range', 'X-Content-Range']).
| If not specified, no custom headers are exposed.
|
*/
'exposedHeaders' => _env('CORS_EXPOSED_HEADERS', ''),
/*
|--------------------------------------------------------------------------
| Configure credentials
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Allow-Credentials CORS header.
| Set to true to pass the header, otherwise it is omitted.
|
*/
'credentials' => false,
/*
|--------------------------------------------------------------------------
| Configure max age
|--------------------------------------------------------------------------
|
| Configures the Access-Control-Max-Age CORS header. Set to
| an integer to pass the header, otherwise it is omitted.
|
*/
'maxAge' => null,
/*
|--------------------------------------------------------------------------
| Configure preflight continue
|--------------------------------------------------------------------------
|
| Pass the CORS preflight response to the next handler.
|
*/
'preflightContinue' => false,
/*
|--------------------------------------------------------------------------
| Log open
|--------------------------------------------------------------------------
|
| Provides a status code to use for successful OPTIONS requests,
| since some legacy browsers (IE11, various SmartTVs) choke on 204.
|
*/
'optionsSuccessStatus' => 204,
];Configuration Options
The cors() method takes in an array of options. Here are the available options:
origin: Configures the Access-Control-Allow-Origin CORS header. Possible values:String- setoriginto a specific origin, including the scheme. For example if you set it to"https://example.com"only requests from "https://example.com" will be allowed. Origins are matched exactly; partial values like"example.com"will not match.Regex string- setoriginto a regular expression written as a string, which will be tested against the request origin. If it matches, the request origin will be reflected. For example'/^https:\/\/(.*\.)?example\.com$/'will allow "https://example.com" and any of its subdomains.Array- setoriginto an array of valid origins. Each origin can be an exact origin or a regex string. For example['https://example1.com', '/^https:\/\/(.*\.)?example2\.com$/']will accept requests from "https://example1.com" or from "example2.com" and its subdomains.
methods: Configures the Access-Control-Allow-Methods CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex:['GET', 'PUT', 'POST']).allowedHeaders: Configures the Access-Control-Allow-Headers CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex:['Content-Type', 'Authorization']). If not specified, defaults to reflecting the headers specified in the request's Access-Control-Request-Headers header.exposedHeaders: Configures the Access-Control-Expose-Headers CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex:['Content-Range', 'X-Content-Range']). If not specified, no custom headers are exposed.credentials: Configures the Access-Control-Allow-Credentials CORS header. Set totrueto pass the header, otherwise it is omitted. When enabling credentials, use explicit origins rather than'*', since browsers reject credentialed responses that allow every origin.maxAge: Configures the Access-Control-Max-Age CORS header. Set to an integer to pass the header, otherwise it is omitted.preflightContinue: Pass the CORS preflight response to the next handler.optionsSuccessStatus: The status code returned for successful preflightOPTIONSrequests, since some legacy browsers (IE11, various SmartTVs) choke on204. Set it to200if you need to support those clients.
The default configuration is the equivalent of:
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"allowedHeaders": "*",
"exposedHeaders": "",
"credentials": false,
"maxAge": null,
"preflightContinue": false,
"optionsSuccessStatus": 204,
}What to read next
Now that CORS is handled, here are a few other parts of Leaf and Leaf MVC worth a look:
Authentication
Learn more about routing in Leaf MVC, dynamic routes, middleware and more.
Roles & Permissions
Learn how to process incoming requests, handle form submissions, and more.
Session Data
Save user data, flash messages, and more using Leaf's session module.
Frontend
Learn about SSR, SPA, and how to use Leaf with your favorite frontend framework.
