Open Source API Gateway

403 Forbidden: CORS Origin Not Allowed

Membrane API Gateway returns 403 Forbidden with a link to this page when the cors plugin rejects a CORS preflight request, because the origin of the calling web page is not allowed by the CORS policy. The browser then blocks the actual cross-origin request of the page.

The Error Response

The response contains the status 403 Forbidden, the detail The origin '...' is not allowed by the CORS policy., where the quoted value is the origin, for example https://app.example.com, the problem type https://membrane-api.io/problems/security/origin-not-allowed and a link to this page. The type security with the subtype origin-not-allowed means that the CORS policy of the gateway refused the request.

How CORS Preflights Work

Before a web page sends a cross-origin request that is not a simple request, the browser sends a preflight: an OPTIONS request with the headers Origin, Access-Control-Request-Method and, if needed, Access-Control-Request-Headers. The cors plugin checks these values against its origins, methods, headers and credentials settings and answers the preflight itself. If one check fails, it rejects the preflight with this error. The CORS Guide for API Developers explains the background.

Common Causes

The Origin Is Not Listed

The Origin of the page is not in origins. The origin must match scheme, host and port: http://app.example.com, https://app.example.com and https://app.example.com:8443 are three different origins. Membrane compares origins in lower case and ignores a trailing slash.

The Page Runs on Localhost During Development

A development server such as http://localhost:3000 has its own origin, which is often forgotten in the list.

The Origin Is Null

Pages loaded from a file: URL, sandboxed iframes and some redirects send Origin: null. This value is only accepted when null is listed explicitly in origins.

How to Fix It

  1. Open the browser's developer tools and note the Origin header of the failing preflight request.
  2. Add exactly that origin to origins. Separate several origins with spaces.
api:
port: 2000
flow:
- cors:
origins: https://example.com https://app.example.com
target:
url: https://api.predic8.de
api:
  port: 2000
  flow:
    - cors:
        origins: https://example.com https://app.example.com
  target:
    url: https://api.predic8.de

For development only, allowAll: true skips all CORS checks. Do not use it in production.

Details in Production Mode

Unlike most errors, CORS rejections are sent with the detail and the link to this page even when Membrane runs in production mode. Setting production: true in the configuration document does not remove them.

Related Documentation

Configuration reference:

← All Membrane error messages

Still stuck? See the troubleshooting guide or ask on GitHub Issues. For commercial support, contact info@predic8.de.