neomerx/cors-psr7
PSR-7 CORS implementation for PHP that parses requests and generates standards-compliant Cross-Origin Resource Sharing headers. Supports preflight and actual requests, configurable origins, methods, headers, credentials, and max age for easy middleware integration.
Architecture fit. The package is a policy engine, not middleware. It implements the W3C CORS algorithm against PSR-7 ServerRequestInterface and emits the header set a caller should apply; it does not ship a Laravel middleware, a service provider, or any HttpFoundation adapter. Laravel's request lifecycle is Symfony HttpFoundation (Illuminate\Http\Request), so the integration seam is a per-request PSR-7 conversion in both directions. That is a real architectural mismatch: you are adding a translation layer to use a library whose only job is to compute a handful of response headers that Laravel already computes natively.
Integration feasibility. Feasible but non-trivial. A working implementation needs: a PSR-7 factory (nyholm/psr7 or guzzlehttp/psr7), symfony/psr-http-message-bridge, a custom middleware that converts the request, runs CorsAnalyzer, maps the resulting header collection back onto the Response, and short-circuits preflight OPTIONS before auth/routing. Expect a 3–5 engineer-day spike plus hardening and test coverage — meaningful effort for a problem the framework solves out of the box via fruitcake/php-cors and HandleCors.
Technical risk. High for the value delivered:
Vary handling, multi-origin) have been exercised in production at scale.psr/http-message constraint against Laravel's dependency graph. PSR-7 v1-only constraints conflict with modern packages requiring ^1.1 || ^2.0; PHP version ceilings may also block PHP 8.3/8.4.HandleCors remains registered, you get duplicate or conflicting Access-Control-* headers, which browsers resolve inconsistently.Origin values is a genuine vulnerability, not a bug.Bottom line. For a standard Laravel service this is a net-negative trade: added dependencies, added request-conversion latency, added glue code to own, in exchange for parity with a first-party-supported path. It becomes defensible only under an explicit PSR-7-first or polyglot architecture mandate where the same CORS policy object must be shared across Laravel, Slim, and other PSR-7 runtimes.
Key questions.
HandleCors / fruitcake/php-cors fail to do for us today?max-age — and are those paths covered by our own contract tests?Stack fit. Weak-to-conditional. The library's domain model (CorsSettings, CorsContainer, CorsAnalyzer) maps cleanly onto Laravel's config array — allowedOrigins, allowedMethods, allowedHeaders, exposedHeaders, maxAge, supportsCredentials — so configuration portability is the one genuine advantage. The transport does not fit: every request pays an object-graph conversion that produces no business value.
Migration path. If proceeding:
CorsMiddleware with a PSR-17/PSR-7 bridge; convert Illuminate\Http\Request → PSR-7, run the analyzer, apply headers to the outgoing response.204 with headers and skip the rest of the pipeline.HandleCors and its config to eliminate double-header behavior.bootstrap/app.php ->withMiddleware(); older via Kernel::$middleware) and confirm ordering relative to auth, throttling, and routing.Compatibility. Validate the full matrix before committing: PHP version ceiling, psr/http-message major version, and coexistence with laravel/framework's transitive fruitcake/php-cors. Run composer why-not neomerx/cors-psr7 in a throwaway branch — a constraint conflict here kills the option cheaply. Confirm the chosen PSR-7 implementation and bridge are actively maintained, since those become the real long-term dependencies.
Sequencing. Phase 0: constraint and compatibility spike (half day, go/no-go). Phase 1: branch prototype behind an interface (CorsPolicy) with a native implementation as default and the PSR-7 implementation as an alternate. Phase 2: contract tests covering simple request, preflight, credentialed request, disallowed origin, and error responses. Phase 3: feature-flagged rollout to a low-traffic service. Phase 4: decide on broader adoption based on observed cost. Keeping the adapter behind an interface preserves the option to swap back to fruitcake/php-cors without touching call sites.
Maintenance. The dominant cost. With no release since late 2022 and no dependents, upstream is unlikely to absorb PHP version bumps, PSR-7 evolution, or security review. Treat it as vendored code: budget for an internal fork or a hard pin, and record it on the dependency-risk register. Apache-2.0 makes forking legal and straightforward; it does not make it free.
Support. No vendor, no commercial backing, no meaningful community. All support is internal — the team that writes the middleware owns CORS incidents end to end, including browser-specific quirks that would otherwise be absorbed upstream.
Scaling. Compute cost is negligible: header derivation is arithmetic on a small settings object. The measurable overhead is PSR-7 object construction and the request/response copy per call — immaterial at typical API volumes, non-zero on high-RPS edge services. Optimize by making the settings container a singleton and relying on maxAge preflight caching; if preflight volume is significant, terminate CORS at the CDN/ingress layer rather than in PHP.
Failure modes.
OPTIONS, surfacing in browsers as an opaque CORS error with no server-side signal.Vary: Origin → shared caches serve one origin's headers to another; silently catastrophic behind a CDN.HandleCors and the new middleware active → duplicated headers, inconsistent browser behavior, hard to reproduce.psr/http-message → blocked framework upgrades months later, discovered at the worst time.Ramp-up. Low for the CORS domain, moderate for the integration. Any engineer can edit the settings array; debugging requires understanding both the PSR-7 abstraction and Laravel's middleware ordering, which is a narrower skill set. Document the adapter, the ordering constraint, and the reason the native path was not used — otherwise the next maintainer will reasonably try to remove it and reintroduce duplicate headers.
Recommendation. Do not adopt for a conventional Laravel application; fruitcake/php-cors is maintained, framework-integrated, and already installed. Consider this package only if a PSR-7-first architecture is mandated across multiple runtimes, and only after the compatibility spike clears and an internal owner is named.
How can I help you explore Laravel packages today?