Cors - How Do 'Preflight' an Httprequest

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:

  1. 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.

  2. 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.

  3. 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 is application/x-www-form-urlencoded, multipart/form-data, or text/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



Leave a reply



Submit