Skip to content

CorsMiddleware

Viames Marino edited this page Mar 26, 2026 · 3 revisions

Pair framework: CorsMiddleware

Pair\Api\CorsMiddleware handles Cross-Origin Resource Sharing for Pair APIs.

It is the middleware you use when browser clients call your API from another origin.

Constructor

new CorsMiddleware(array $options = [])

Supported options in the current implementation:

  • allowedOrigins Default: ['*']
  • allowedMethods Default: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
  • allowedHeaders Default includes Content-Type, Authorization, X-Requested-With
  • maxAge Default: 86400

Example:

// Allows all origins with the default methods and headers.
$this->middleware(new \Pair\Api\CorsMiddleware());

Main method

handle(Request $request, callable $next): void

This is the main method of the class.

Current behavior:

  • sets CORS headers for the response
  • if the request method is OPTIONS, responds with HTTP 204 and exits immediately
  • otherwise forwards the request to the next middleware or action

Example:

public function handle(\Pair\Api\Request $request, callable $next): void
{
    // Sets the Access-Control-* headers.
    // If this is a preflight request, the middleware stops here with 204.
    // Otherwise it continues with the next middleware.
}

Origin handling

Wildcard origin

When allowedOrigins contains '*', the middleware emits:

Access-Control-Allow-Origin: *

Explicit origin allow-list

When you pass a list of allowed origins, the middleware:

  • reads the request Origin header
  • echoes that origin back only if it is in the allow-list
  • adds Vary: Origin

Example:

$this->middleware(new \Pair\Api\CorsMiddleware([
    'allowedOrigins' => [
        'https://app.example.com',
        'https://admin.example.com',
    ],
    'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    'allowedHeaders' => ['Content-Type', 'Authorization', 'Idempotency-Key'],
    'maxAge' => 3600,
]));

Preflight behavior

For OPTIONS requests the middleware:

  • sends the CORS headers
  • sets HTTP status 204
  • terminates the request

That is why CORS middleware often needs to run before auth or throttling for browser-facing APIs.

Practical controller example

protected function registerDefaultMiddleware(): void
{
    // Handles browser preflight requests first.
    $this->middleware(new \Pair\Api\CorsMiddleware([
        'allowedOrigins' => ['https://app.example.com'],
        'allowedHeaders' => ['Content-Type', 'Authorization', 'Idempotency-Key'],
    ]));

    // Applies throttling after CORS.
    $this->middleware(new \Pair\Api\ThrottleMiddleware(60, 60));
}

Important implementation notes

These details reflect the current code:

  • the middleware always sends Access-Control-Allow-Methods, Access-Control-Allow-Headers, and Access-Control-Max-Age
  • Access-Control-Allow-Origin is sent only for wildcard mode or for an explicitly matched origin
  • there is no built-in Access-Control-Allow-Credentials support in this middleware

If your frontend needs credentialed cross-origin requests, document and implement that intentionally instead of assuming it is already handled.

Common pitfalls

  • Putting CORS middleware after auth or throttling and breaking preflight OPTIONS.
  • Using '*' in production for sensitive APIs without reviewing the risk profile.
  • Forgetting to allow custom client headers such as Idempotency-Key or tenant headers.
  • Assuming credentials support exists out of the box when it does not.

See also: MiddlewarePipeline, ThrottleMiddleware, Middleware, API.

Clone this wiki locally