CORS - How do 'preflight' an httprequest?
During the preflight request, you should see the following two headers: Access-Control-Request-Method and Access-Control-Request-Headers. These request headers are asking the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
For example, suppose the browser makes a request with the following headers:
Origin: http://yourdomain.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header
Your server should then respond with the following headers:
Access-Control-Allow-Origin: http://yourdomain.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: X-Custom-Header
Pay special attention to the Access-Control-Allow-Headers response header. The value of this header should be the same headers in the Access-Control-Request-Headers request header, and it can not be '*'.
Once you send this response to the preflight request, the browser will make the actual request. You can learn more about CORS here: http://www.html5rocks.com/en/tutorials/cors/
What is the motivation behind the introduction of preflight CORS requests?
I spent some time being confused as to the purpose of the preflight request but I think I've got it now.
The key insight is that preflight requests are not a security thing. Rather, they're a not-changing-the-rules thing.
Preflight requests have nothing to do with security, and they have no bearing on applications that are being developed now, with an awareness of CORS. Rather, the preflight mechanism benefits servers that were developed without an awareness of CORS, and it functions as a sanity check between the client and the server that they are both CORS-aware. The developers of CORS felt that there were enough servers out there that were relying on the assumption that they would never receive, e.g. a cross-domain DELETE request that they invented the preflight mechanism to allow both sides to opt-in. They felt that the alternative, which would have been to simply enable the cross-domain calls, would have broken too many existing applications.
There are three scenarios here:
Old servers, no longer under development, and developed before CORS. These servers may make assumptions that they'll never receive e.g. a cross-domain DELETE request. This scenario is the primary beneficiary of the preflight mechanism. Yes these services could already be abused by a malicious or non-conforming user agent (and CORS does nothing to change this), but in a world with CORS the preflight mechanism provides an extra 'sanity check' so that clients and servers don't break because the underlying rules of the web have changed.
Servers that are still under development, but which contain a lot of old code and for which it's not feasible/desirable to audit all the old code to make sure it works properly in a cross-domain world. This scenario allows servers to progressively opt-in to CORS, e.g. by saying "Now I'll allow this particular header", "Now I'll allow this particular HTTP verb", "Now I'll allow cookies/auth information to be sent", etc. This scenario benefits from the preflight mechanism.
New servers that are written with an awareness of CORS. According to standard security practices, the server has to protect its resources in the face of any incoming request -- servers can't trust clients to not do malicious things. This scenario doesn't benefit from the preflight mechanism: the preflight mechanism brings no additional security to a server that has properly protected its resources.
Preflight request is sent with all methods
See https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Simple_requests
A CORS preflight OPTIONS
request can be triggered just by adding a Content-Type
header to a request — if the value’s anything except application/x-www-form-urlencoded
, text/plain
, or multipart/form-data
. And that’s true even for GET
requests (though you should never add a Content-Type
header to a GET
— because there’s no request body, so it serves no purpose).
And among the headers shown in the question, the Authorization
header will also trigger a preflight, as will the "Language
" header (which isn’t even a standard header name; maybe Accept-Language
was intended?), and the Access-Control-Allow-Origin
header (which isn’t even a request header; it’s a response header that should never be used in frontend code).
As far as the headers that don’t trigger a preflight: the Fetch spec (which defines CORS behavior) specifies what it calls a CORS-safelisted request-header, and defines as one of:
Accept
Accept-Language
Content-Language
Content-Type
whose value, once parsed, has a MIME type (ignoring parameters) that isapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
Any request — including any GET
request — which contains a header that’s not among those CORS-safelisted request-headers listed above will trigger a preflight.
To help make all that more clear, I updated the MDN docs about CORS “simple requests” and the MDN docs about CORS preflighted requests (it’s slightly more complicated than what’s described above, actually—but what’s above suffices for the context of this question).
Note that WebKit/Safari places additional restrictions on the values allowed in the Accept
, Accept-Language
, and Content-Language
headers.
If any of those headers have ”non-standard” values, WebKit/Safari will do a preflight.
As far as what WebKit/Safari considers “non-standard” values for those headers, that’s not really documented except in the following WebKit bugs:
- Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language
- Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS
- Switch to a blacklist model for restricted Accept headers in simple CORS requests
No other browsers impose those extra restrictions, because they’re not part of the spec. They were unilaterally added to WebKit with no discussion with the spec editor or other browsers.
CORS preflight request not handled by external API
James answer is ok, but right now I'm more comfortable with solution based on Node.js rather than Nginx, as I developing locally on windows.
I came up to the solutions with Express server:
var cors = require('cors')
var app = express()
var proxy = require('express-http-proxy');
app.use(cors())
app.use('/', proxy('https://api.getresponse.com'))
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
CORS: Response to preflight request doesn't pass access control check: It does not have HTTP ok status
The way you implement it, differs from the docs.
With the amount of details you provided it's hard to tell what is wrong, but my best guess is you left out implementing an OPTIONS
method on your endpoint causing it to fail the pre-flight.
Best is to just go for the options as described in the docs unless you have a very good reason not to:
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://example.com",
"http://www.contoso.com");
});
});
and
app.UseCors(MyAllowSpecificOrigins);
How can I identify a CORS preflight request?
Check for the Access-Control-Request-Method header. It would not make much sense to send it in a request other than the preflight request.
Related Topics
Open Url in New Tab or Reuse Existing One Whenever Possible
How to Get All the Applied Styles of an Element by Just Giving Its Id
How to Break Line in JavaScript
Need to Find Height of Hidden Div on Page (Set to Display:None)
Decoding Url Parameters with JavaScript
How to Block Website from Loading in Iframe
How to Call a Js Function Using Onclick Event
"Status Code:200 Ok (From Serviceworker)" in Chrome Network Devtools
Html5 Drag & Drop Change Icon/Cursor While Dragging
How to Open a File Browser with Default Directory in JavaScript
What Is the Purpose of the HTML "No-Js" Class
How to Remove an Item from an Array in Angularjs Scope
Where's the Connection Between Index.HTML and Index.Js in a Create-React-App Application
Pass a JavaScript Variable Value into Input Type Hidden Value
How to Listen/Detect Changes to an Input Value - When the Input Value Is Changed via JavaScript