CORS — Cross-Origin Resource Sharing
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). CORS errors only surface at runtime when a browser makes a cross-origin request. There's no compiler or linter that catches CORS misconfiguration — you discover it when the frontend fails to communicate with the API, or worse, during security audit. Detection_hints.tools is not specified, so no automated tooling is cited for this term.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates using a CORS middleware and allowlisting specific origins. This is typically a single middleware addition plus configuration, touching one or two files. Not a one-liner (e1) since you need to handle preflight, error responses, and configure allowed origins, but not a major refactor either.
Closest to 'localised tax' (b3). CORS configuration applies to web context PHP APIs but is typically handled in one place (middleware/router level). Once configured correctly, it doesn't impose ongoing maintenance burden across the codebase. The choice is somewhat architectural for APIs but contained to the HTTP layer.
Closest to 'serious trap' (t7). The misconception explicitly states that developers believe 'Access-Control-Allow-Origin: * fixes all CORS errors' when it actually breaks with credentials. This contradicts intuition from other wildcard patterns. Common_mistakes lists multiple traps: preflight handling, error response headers, and the wildcard+credentials combination that browsers silently reject. A competent developer unfamiliar with CORS will likely hit multiple of these.
Also Known As
TL;DR
Explanation
The Same-Origin Policy prevents JavaScript from making requests to a different domain, protocol, or port than the page it is running on. CORS is the controlled mechanism for relaxing this restriction. For simple requests (GET, POST with certain content types), the browser adds an Origin header; the server responds with Access-Control-Allow-Origin and the browser either allows or blocks the response. For complex requests (PUT, DELETE, custom headers, JSON content-type), the browser sends a preflight OPTIONS request first to check permissions; the server must respond with Access-Control-Allow-Methods, Access-Control-Allow-Headers, and optionally Access-Control-Max-Age. In PHP APIs, CORS headers must be set on every response including error responses. Wildcard (*) for Access-Control-Allow-Origin is only appropriate for public APIs — for APIs that use cookies or Authorization headers, the specific origin must be listed and Access-Control-Allow-Credentials: true must be set.
Common Misconception
Why It Matters
Common Mistakes
- Not handling OPTIONS preflight requests — complex requests get a preflight that must return 200 with CORS headers, not a 405 Method Not Allowed.
- Setting CORS headers only on success responses — error responses (4xx, 5xx) must also include CORS headers or the browser hides the error from the JavaScript.
- Using Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — browsers reject this combination; specify the exact origin.
- Setting CORS headers in application middleware only — some PHP frameworks handle OPTIONS at the router level before middleware runs; handle it earlier.
Code Examples
// Missing preflight handling — CORS errors on all complex requests
header('Access-Control-Allow-Origin: *');
// No OPTIONS handling — preflight fails with 404/405
// With credentials — wildcard + credentials = browser rejection
// Handle preflight + correct headers
function setCorsHeaders(string $allowedOrigin): void {
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if ($origin === $allowedOrigin) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Vary: Origin'); // important for caching
}
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-CSRF-Token');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache preflight 24hrs
}
// Handle preflight before routing
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
setCorsHeaders('https://app.example.com');
http_response_code(200);
exit;
}