Preflight can only be applied to the request, not to the entire domain. I brought the same question up on the mailing list, and there were security concerns. Here’s the entire thread: http://lists.w3.org/Archives/Public/public-webapps/2012AprJun/0228.html
There are a few things to consider if you’d like to limit the number of preflight requests. First note that WebKit-based browsers set a max preflight cache lifetime of 10 minutes:
https://github.com/WebKit/webkit/blob/master/Source/WebCore/loader/CrossOriginPreflightResultCache.cpp
and Blink-based browsers limit the cache to two hours:
https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/preflight_result.cc;l=40;drc=098756533733ea50b2dcb1c40d9a9e18d49febbe
(I’m not sure if this is true for other browsers). So while you should always set the Access-Control-Max-Age header.
Next note that it is impossible to avoid a preflight on PUT/DELETE requests. So updates/deletes to your API will require at least one preflight every 10 minutes.
On GET/POST, avoid custom headers if at all possible, since these still trigger preflights. If your API returns JSON, note that a Content-Type of ‘application/json’ also triggers a preflight.
If you are willing to bend just how “RESTful” your API is, there are a few more things you can try. One is to use a Content-Type that doesn’t need a preflight, like ‘text/plain’. Custom headers always trigger preflights, so if you have any custom headers, you could move them into query parameters. At the extreme end, you could use a protocol like JSON-RPC, where all requests are made to a single endpoint.
In all honesty, because of the browser’s preflight cache limit of 10/120 minutes, and REST’s resource urls, the preflight cache is of limited value. There’s very little you can do to limit preflights over the course of a long running app. I’m hopeful the authors of the CORS spec will try to address this in the future.