jarir-ahmed/universal-cors

A secure-by-default, framework-agnostic CORS handler for PHP (plain PHP, Yii2, Laravel, Slim).

Maintainers

Package info

github.com/jarir2020/jarir-ahmed-universal-cors

pkg:composer/jarir-ahmed/universal-cors

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-06-06 06:37 UTC

This package is auto-updated.

Last update: 2026-06-06 06:38:25 UTC


README

A secure-by-default, framework-agnostic CORS handler for PHP. One small class that answers preflight requests and emits the right Access-Control-* headers — without the classic footguns (* + credentials, blanket origin reflection, forced debug mode).

Why

CORS is usually "fixed" by pasting header("Access-Control-Allow-Origin: *") plus Access-Control-Allow-Credentials: true into a front controller. That combination is forbidden by the spec and lets any website call your API with the user's cookies. This package makes the safe path the easy path.

Requirements

  • PHP >= 7.4

Install

composer require jarir-ahmed/universal-cors

Usage (plain PHP / any framework)

Put this at the very top of your front controller or API entry point:

use JarirAhmed\UniversalCors\Cors;

$cors = new Cors([
    'allowOrigins'     => ['https://app.example.com', 'http://localhost:3000'],
    'allowMethods'     => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    'allowHeaders'     => ['Content-Type', 'Authorization'],
    'allowCredentials' => true,
    'maxAge'           => 3600,
]);

// Emits headers; returns true if this was a preflight (then stop).
if ($cors->send()) {
    exit;
}

// ... your normal request handling continues here ...

Configuration

Key Default Notes
allowOrigins [] Exact origins, or ['*'] (only without credentials).
allowOriginPatterns [] Wildcard patterns, e.g. https://*.example.com. * matches one DNS label and never crosses a dot.
allowMethods GET, POST, PUT, PATCH, DELETE, OPTIONS
allowHeaders Content-Type, Authorization Use ['*'] to reflect the browser's requested headers.
exposeHeaders [] Response headers JS may read.
allowCredentials false When true, the matching origin is reflected (never *).
maxAge 0 Preflight cache seconds.

Safety guarantees

  • allowOrigins: ['*'] with allowCredentials: true throws on construction.
  • With credentials, the request Origin is reflected for allowed origins — never a blanket * — and Vary: Origin is set for correct caching.
  • Unlisted origins receive no Access-Control-Allow-Origin header, so the browser blocks the response.

Testing without sending headers

resolve() and handle() are pure and return a CorsResult you can assert on:

$result = $cors->handle($_SERVER);     // or resolve($origin, $method, $requestHeaders)
$result->headers;       // array<string,string> of response headers
$result->isPreflight;   // bool
$result->status;        // 204 for a preflight to short-circuit, else null
$result->isAllowed();   // whether an Allow-Origin header was produced

Framework notes

  • Plain PHP / Slim / custom: call $cors->send() early; exit on preflight.
  • Yii2: prefer the built-in yii\filters\Cors behavior; use this package when you want one consistent CORS policy across non-Yii entry points too.
  • Laravel: register send() in a global middleware's handle() before the app runs.

License

MIT