On this page
Middleware
mezzio-cors provides middleware consuming PSR-7 HTTP message instances, via implementation of PSR-15 interfaces.
This middleware checks, if the incoming request is a CORS request. If so, it makes a distinction between a so called Preflight request or the regular request.
Preflight Request
A Preflight request should be a light call which provides the Browser with the CORS informations it needs to execute the regular (CORS) request.
These informations are:
- Domain accepted for executing CORS request?
- Cookies accepted?
- Which Headers are allowed to be sent?
- Which Headers are provided in the response?
- Which HTTP Methods are accepted by that Endpoint?
CORS Request
The CORS request is the actual request. That request SHOULD to be already
verified. If its not verified by a previous Preflight request,
the request will be aborted with a 403 Forbidden response.
Configuration
There are 2 ways of configuring CORS in your project. Either create a global
configuration file like cors.global.php and/or add a route specific configuration.
On the project level, you can only configure the following Headers:
| Configuration | Type | Header |
|---|---|---|
allowed_origins |
string[] | Access-Control-Allow-Origin |
allowed_headers |
string[] | Access-Control-Allow-Headers |
allowed_max_age |
string (TTL in seconds) | Access-Control-Max-Age |
credentials_allowed |
bool | Access-Control-Allow-Credentials |
exposed_headers |
string[] | Access-Control-Expose-Headers |
fnmatchFormatThe
allowed_originsstrings must fit thefnmatchformat.**
On the route level, you can configure all of the projects configuration settings and if the configuration of the route should either override the project configuration (default) or merge it.
| Configuration | Type | Header |
|---|---|---|
overrides_project_configuration |
bool | - |
explicit |
bool | - |
allowed_origins |
string[] | Access-Control-Allow-Origin |
allowed_headers |
string[] | Access-Control-Allow-Headers |
allowed_max_age |
string (TTL in seconds) | Access-Control-Max-Age |
credentials_allowed |
bool | Access-Control-Allow-Credentials |
exposed_headers |
string[] | Access-Control-Expose-Headers |
The parameter overrides_project_configuration handles the way how the
configuration is being merged. The default setting is true to ensure that a
route configuration has to specify every information it will provide.
The parameter explicit tells the ConfigurationLocator to stop trying other
request methods to match the same route because there wont be any other method.
Examples for Project Configurations
Allow Every Origin
<?php
// In config/autoload/cors.global.php
declare(strict_types=1);
use Mezzio\Cors\Configuration\ConfigurationInterface;
return [
ConfigurationInterface::CONFIGURATION_IDENTIFIER => [
'allowed_origins' => [ConfigurationInterface::ANY_ORIGIN], // Allow any origin
'allowed_headers' => [], // No custom headers allowed
'allowed_max_age' => '600', // 10 minutes
'credentials_allowed' => true, // Allow cookies
'exposed_headers' => ['X-Custom-Header'], // Tell client that the API will always return this header
],
];
Allow Every Origin from a Specific Domain and Its Subdomains
<?php
// In config/autoload/cors.global.php
declare(strict_types=1);
use Mezzio\Cors\Configuration\ConfigurationInterface;
return [
ConfigurationInterface::CONFIGURATION_IDENTIFIER => [
'allowed_origins' => ['*//example.com', '*.example.com'],
'allowed_headers' => [], // No custom headers allowed
'allowed_max_age' => '3600', // 60 minutes
'credentials_allowed' => false, // Disallow cookies
'exposed_headers' => [], // No headers are exposed
],
];
Examples for Route Configurations
Make the Configuration Explicit to Avoid Multiple Router Match Requests
<?php
// In config/autoload/cors.global.php
declare(strict_types=1);
use Mezzio\Cors\Configuration\ConfigurationInterface;
use Mezzio\Cors\Configuration\RouteConfigurationInterface;
return [
ConfigurationInterface::CONFIGURATION_IDENTIFIER => [
'allowed_origins' => ['*//example.com', '*.example.com'],
'allowed_headers' => ['X-Project-Header'],
'exposed_headers' => ['X-Some-Header'],
'allowed_max_age' => '3600',
'credentials_allowed' => true,
],
'routes' => [
[
'name' => 'foo-get',
'path' => '/foo',
'middleware' => [
// ...
],
'options' => [
'defaults' => [
RouteConfigurationInterface::PARAMETER_IDENTIFIER => [
'explicit' => true,
'allowed_origins' => ['*//someotherdomain.com'],
'allowed_headers' => ['X-Specific-Header-For-Foo-Endpoint'],
'allowed_max_age' => '3600',
],
],
],
'allowed_methods' => ['GET'],
],
[
'name' => 'foo-delete',
'path' => '/foo',
'middleware' => [
// ...
],
'allowed_methods' => ['DELETE'],
],
],
];
Result of this configuration for the CORS preflight of /foo for the upcoming
GET request will look like this:
| Configuration | Parameter |
|---|---|
allowed_origins |
['*//someotherdomain.com'] |
allowed_headers |
['X-Specific-Header-For-Foo-Endpoint'] |
allowed_max_age |
3600 |
exposed_headers |
[] |
credentials_allowed |
false |
allowed_methods |
['GET'] |
Did you note the missing DELETE? This is because of the explicit flag! Also note the empty exposed_headers which is due to the project overriding (overrides_project_configuration) parameter.
Enable Project Merging
<?php
declare(strict_types=1);
use Mezzio\Cors\Configuration\ConfigurationInterface;
use Mezzio\Cors\Configuration\RouteConfigurationInterface;
return [
ConfigurationInterface::CONFIGURATION_IDENTIFIER => [
'allowed_origins' => ['*//example.com', '*.example.com'],
'allowed_headers' => ['X-Project-Header'],
'exposed_headers' => ['X-Some-Header'],
'allowed_max_age' => '3600',
],
'routes' => [
[
'name' => 'foo-get',
'path' => '/foo',
'middleware' => [
// ...
],
'options' => [
'defaults' => [
RouteConfigurationInterface::PARAMETER_IDENTIFIER => [
'overrides_project_configuration' => false,
'allowed_origins' => [RouteConfigurationInterface::ANY_ORIGIN],
'allowed_headers' => ['X-Specific-Header-For-Foo-Endpoint'],
'allowed_max_age' => '7200',
'credentials_allowed' => true,
],
],
],
'allowed_methods' => ['GET'],
],
[
'name' => 'foo-delete',
'path' => '/foo',
'middleware' => [
// ...
],
'allowed_methods' => ['DELETE'],
],
],
];
Result of this configuration for the CORS preflight of /foo for the upcoming
GET request will look like this:
| Configuration | Parameter |
|---|---|
allowed_origins |
[RouteConfigurationInterface::ANY_ORIGIN] |
allowed_headers |
['X-Specific-Header-For-Foo-Endpoint', 'X-Project-Header'] |
allowed_max_age |
7200 |
exposed_headers |
['X-Some-Header'] |
credentials_allowed |
true |
allowed_methods |
['GET', 'DELETE'] |
Did you note the ANY_ORIGIN detail? This is, because if ANY_ORIGIN is allowed for an endpoint, we remove all other origins for that route.