CORS: Origin is not allowed by Access-Control-Allow-Origin

Fix the CORS error where the server returns Access-Control-Allow-Origin but the requesting origin does not match the allowed value.

Cross-Origin Resource Sharing (CORS) produces the "Origin is not allowed by Access-Control-Allow-Origin" error when the server returns an Access-Control-Allow-Origin header that does not match the requesting origin.

When CORS Produces This Error

Cross-Origin Resource Sharing (CORS) produces this error when the browser sends a request from one origin (for example, https://app.example.com) and the server responds with an Access-Control-Allow-Origin header set to a different origin (for example, https://other.example.com). The browser compares the two values and blocks the response when they do not match.

CORS also produces this error when the server returns a hardcoded origin that does not match the domain of the front-end application. A common case is a server configured with Access-Control-Allow-Origin: https://localhost:3000 that receives requests from the production domain https://app.example.com.

What Causes "Origin is not allowed by Access-Control-Allow-Origin" in CORS

Cross-Origin Resource Sharing (CORS) fails with this error because the Access-Control-Allow-Origin response header does not match the Origin request header. The browser enforces an exact match between the two values, including the scheme ( http vs https), hostname, and port number.

CORS origin mismatch occurs in several scenarios. The server hardcodes a single origin that does not match all client domains. The server sets Access-Control-Allow-Origin to a development URL that differs from the production URL. The server omits the port number when the client includes one, or vice versa.

CORS also fails with this error when a CDN or reverse proxy caches a response with one Access-Control-Allow-Origin value and serves it to a different origin. The cached header value does not match the new request's Origin, and the browser blocks the response.

How to Fix "Origin is not allowed by Access-Control-Allow-Origin" in CORS

  1. Identify the requesting origin. Open the browser developer tools, navigate to the Network tab, and inspect the failed request. The Origin request header shows the exact value the server must match.

  2. Update the server to return the correct Access-Control-Allow-Origin value. The fix depends on the platform:

    Apache HTTP Server-- Set the origin to match the requesting domain. Use SetEnvIf to support multiple origins:

    <IfModule mod_headers.c>
        SetEnvIf Origin "^https://(app\.example\.com|admin\.example\.com)$" ORIGIN_MATCH=$0
        Header always set Access-Control-Allow-Origin "%{ORIGIN_MATCH}e" env=ORIGIN_MATCH
        Header always set Vary "Origin" env=ORIGIN_MATCH
    </IfModule>

    Nginx-- Use the map directive to allow multiple origins:

    map $http_origin $cors_origin {
        default "";
        "https://app.example.com"   "$http_origin";
        "https://admin.example.com" "$http_origin";
    }
    
    server {
        location / {
            add_header 'Access-Control-Allow-Origin' $cors_origin always;
            add_header 'Vary' 'Origin' always;
        }
    }

    PHP-- Validate the Origin header against an allowlist and reflect the matching origin:

    <?php
    $allowed_origins = ['https://app.example.com', 'https://admin.example.com'];
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
    
    if (in_array($origin, $allowed_origins, true)) {
        header("Access-Control-Allow-Origin: $origin");
        header('Vary: Origin');
    }

    Node.js with Express-- Pass an array of origins to the cors middleware:

    const cors = require('cors');
    app.use(cors({
      origin: ['https://app.example.com', 'https://admin.example.com']
    }));

    Ruby on Rails-- Set the origin dynamically in the controller response:

    allowed = ['https://app.example.com', 'https://admin.example.com']
    origin = request.headers['Origin']
    if allowed.include?(origin)
      headers['Access-Control-Allow-Origin'] = origin
      headers['Vary'] = 'Origin'
    end

    ASP.NET-- Add the origin to the CORS policy in Startup.cs or Program.cs:

    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowSpecificOrigins",
            policy => policy
                .WithOrigins("https://app.example.com", "https://admin.example.com")
                .AllowAnyMethod()
                .AllowAnyHeader());
    });
  3. Include Vary: Origin in the response when the Access-Control-Allow-Origin value changes based on the request. This header prevents CDNs and browser caches from serving a cached response with the wrong origin value.

  4. Restart or reload the web server to apply the changes.

How to Verify the Fix

Send a cross-origin request with curl using the specific origin and confirm the response header matches:

curl -I -H "Origin: https://app.example.com" https://api.example.com/endpoint

CORS is working when the response contains:

Access-Control-Allow-Origin: https://app.example.com
Vary: Origin

Edge Cases and Variations

CORS origin mismatch occurs when the front-end uses http:// and the API uses https://. The browser treats these as different origins. Ensure the Access-Control-Allow-Origin value matches the exact scheme of the front-end URL.

CORS origin mismatch occurs when the port number differs between environments. A front-end running on https://app.example.com:8443 does not match https://app.example.com. Include the port in the allowed origin when the front-end uses a non-default port.

CORS origin mismatch occurs with trailing slashes. The Origin header never includes a trailing slash ( https://example.com, not https://example.com/). Do not add a trailing slash to the Access-Control-Allow-Origin value.