This note is to help our customers understand CORS (especially as it relates to Infura). We’ll talk about how it works and what to do about your CORS errors.
These are some of the best resources I’ve found:
It is a mechanism that allows restricted resources on a server to be requested from a webpage that was not served by that server, i.e. a webpage that was served by a different origin. It is enforced by the browser, which provides headers in the request (mainly the Origin header) so that the cross-site server can determine if it should authorise the browser to access the requested resource.
Some requests are “simple” - GET, HEAD, and some POSTs, i.e. those that have “allowed” content types. The allowed POST content types are either are harmless (text/plain), or could be harmful but are legacy types that everyone knows how to deal with already (multipart/form-data).
However, even for the simple requests above, the browser will need to send a preflight if there’s authentication or other headers are present that are not on the CORS-safelisted list. For simple requests, that don’t include any headers not on the safelist, the browser doesn’t need to run a “preflight” request.
A preflight request is one with the verb “OPTIONS” plus some headers to ask the server for a response that says if the real request will be allowed.
Here’s an example pre-flight request copied from the browser devtools and represented as a curl command:
curl 'https://ipfs.infura.io:5001/api/v0/add?stream-channels=true&progress=false' \ -X 'OPTIONS' \ -H 'Accept: */*' \ -H 'Accept-Language: en-AU,en-GB-oxendict;q=0.9,en;q=0.8' \ -H 'Access-Control-Request-Headers: authorization' \ -H 'Access-Control-Request-Method: POST' \ -H 'Connection: keep-alive' \ -H 'Origin: http://localhost:3000' \ -H 'Referer: http://localhost:3000/' \ -H 'Sec-Fetch-Dest: empty' \ -H 'Sec-Fetch-Mode: cors' \ -H 'Sec-Fetch-Site: cross-site' \ -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/18.104.22.168 Safari/537.36' \ --compressed -v
And here is the corresponding actual request:
curl 'https://ipfs.infura.io:5001/api/v0/add?stream-channels=true&progress=false' \ -H 'Accept: */*' \ -H 'Accept-Language: en-AU,en' \ -H 'Connection: keep-alive' \ -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFUZB7uL4BhiUxKJW' \ -H 'Origin: http://localhost:3000' \ -H 'Referer: http://localhost:3000/' \ -H 'Sec-Fetch-Dest: empty' \ -H 'Sec-Fetch-Mode: cors' \ -H 'Sec-Fetch-Site: cross-site' \ -H 'Sec-GPC: 1' \ -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/22.214.171.124 Safari/537.36' \ -H 'authorization: Basic xxxtokenxxx' \ --data-raw $'------WebKitFormBoundaryFUZB7uL4BhiUxKJW\r\nContent-Disposition: form-data; name="file"; filename=""\r\nContent-Type: application/octet-stream\r\n\r\nxxxxBINARY_DATA_bla_blaxxxx' --compressed -v
NOTE that this is a POST with “multipart/form-data” which would normally qualify as a simple request, but because there’s an authorization header it is no longer “simple” and a pre-flight request is sent by the browser. The Sec-* headers are not on the CORS safelist either.
(NOTE curl uses -X POST by default with “multipart/form-data”.)
Note that both Infura/eth and Infura/IPFS API endpoints return an access-control-allow-origin header with the exact same URL as the Origin header provided in the request, i.e. not a ‘*’ or wildcard. This is because of the CORS rule that says authentication is not allowed, where the allowed origin is a wildcard, and Infura often (esp. IPFS) requires auth.
i.e. if the Origin header is “http://localhost:3000” then the same origin will be returned in the access-control-allow-origin header. In fact if the value for the Origin header is “whatever” then “whatever” will be returned.
NOTE: There is one source of CORS error from Infura. Below is a pre-flight for a ethereum endpoint request that includes an authorization header (i.e. the API Secret Key was included):
curl 'https://mainnet.infura.io/v3/c1f6fa004ded489fa95d3d84219e8860' \ -X 'OPTIONS' \ -H 'authority: mainnet.infura.io' \ -H 'accept: */*' \ -H 'accept-language: en-AU,en-GB-oxendict;q=0.9,en;q=0.8' \ -H 'access-control-request-headers: **authorization**,content-type' \ -H 'access-control-request-method: POST' \ -H 'cache-control: max-age=0' \ -H 'origin: http://localhost:3001' \ -H 'referer: http://localhost:3001/' \ -H 'sec-fetch-dest: empty' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-site: cross-site' \ -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.96.36.199 Safari/537.36' \ --compressed -v
Note that a preflight request does not actually include the authorization header, but it does signal that the actual request will include it (see underlined above).
The response is:
< HTTP/2 200 < date: Mon, 14 Nov 2022 23:02:37 GMT < content-length: 0 < vary: Accept-Encoding < vary: Origin < vary: Access-Control-Request-Method < vary: Access-Control-Request-Headers
As you can see, access-control-allow-origin, etc, are missing in the response, the browser will show a CORS error in the console.
And the response when the authorization header is not included is:
< HTTP/2 200 < date: Mon, 14 Nov 2022 23:12:57 GMT < content-length: 0 < access-control-allow-headers: Content-Type < access-control-allow-methods: POST < access-control-allow-origin: http://localhost:3001 < access-control-max-age: 86400 < vary: Accept-Encoding < vary: Origin < vary: Access-Control-Request-Method < vary: Access-Control-Request-Headers
i.e. access-control-allow-origin, etc, are present. So, if the API secret is included in the request (only from a browser, not from backend code), then the request will fail with a CORS error.
The bottom line is: if you get a CORS error, then there’s a good reason (such as we want to disallow including API secret key in the front end) or changes need to be made on the Infura side to correct them.
Alternatively, you can move your Infura access to the backend - if your architecture allows this option. The backend does not send CORS related headers (unless you code them in) and Infura will not send back CORS related responses, and even if it did it’s only the browser that makes decisions based on them, so CORS does not come into the picture.
CORS is enforced in the browser, this is why your backend code or a curl request “works”. Both these environments are not doing any CORS checking, and are not sending CORS headers.
If you do get a CORS error, open a support case and provide the code generating the request, or the equivalent curl request, so that we can understand what is happening.