Membrane API Gateway returns 403 Forbidden with a link to this page when the cors plugin rejects a CORS preflight request, because the page wants to send a request header that is not allowed by the CORS policy. The browser then blocks the actual cross-origin request of the page.
The response contains the status 403 Forbidden, the detail The headers '...' is not allowed by the CORS policy., where the quoted value is the origin of the page (see the note below), the problem type https://membrane-api.io/problems/security/headers-not-allowed and a link to this page. The type security with the subtype headers-not-allowed means that the CORS policy of the gateway refused the request.
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.
A header in Access-Control-Request-Headers is not listed in headers. Only the CORS-safe headers Accept, Accept-Language, Content-Language, Content-Type and Range are allowed without configuration. Headers such as Authorization, X-Api-Key or X-Requested-With must be listed.
HTTP libraries and frameworks often add headers such as X-Requested-With or tracing headers without you noticing. They all appear in Access-Control-Request-Headers and must be allowed.
In current versions, the detail of this error shows the origin of the page instead of the name of the rejected header. To find the header, look at the Access-Control-Request-Headers header of the preflight request in the browser's developer tools and compare each entry with headers.
Access-Control-Request-Headers header of the failing preflight request.headers.api:
port: 2000
flow:
- cors:
origins: https://app.example.com
methods: GET, POST
headers: Authorization, X-Requested-With
target:
url: https://api.predic8.de
api:
port: 2000
flow:
- cors:
origins: https://app.example.com
methods: GET, POST
headers: Authorization, X-Requested-With
target:
url: https://api.predic8.de
For development only, allowAll: true skips all CORS checks. Do not use it in production.
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.
Configuration reference:
Still stuck? See the troubleshooting guide or ask on GitHub Issues. For commercial support, contact info@predic8.de.