Have you ever noticed a HTTP OPTIONS request being sent from your browser to a server, but never knew what it meant? Here's how it looks like in Chrome Developer Tools:
This is your browser attempting a cross-site HTTP request. Per MDN:
Cross-site HTTP requests are HTTP requests for resources from a different domain than the domain of the resource making the request.
It is common for pages to load resources from different hosts. These resources typically include CSS stylesheets, scripts, images, etc. However, if a script is able to load resources from or create resources on another host, that could be problematic in terms of security. As such, browsers implement a same-origin policy whereby a script on one resource/page can access data from another resource/page, as long as the resources have the same origin/host. Having scripts retrieve and send data without having to leave a page is extremely useful. This is where AJAX comes into play. However, the same-origin policy is still an obstacle.
One way to work around the same-origin policy with AJAX is by using a technique called CORS (Cross-Origin Resource Sharing). This standard makes it so that browsers have to "preflight" a HTTP request with an OPTIONS request header. The OPTIONS request is sent to the target server, and the server (if configured properly) can respond to the OPTIONS request informing the client whether it is approved to access the resource. Think of it as requiring the client to ask for permission from the server before the client can send across the XMLHttpRequest. But this standard isn't implemented in all browsers. The older versions of browsers do not support it.
Let's walk through an example. We have a messaging client on https://chat.com where users can post messages for other users to see. The messages are stored in a database, and served from https://messages.chat.com, which is a different host than chat.com (yes, a different subdomain still makes it under a different host). When a user fills out the form to submit a message from chat.com, the browser needs to make a POST request to messages.chat.com using AJAX, and the server will then store that message in the database. The API for posting messages states that the request must be sent to https://messages.chat.com/messages.
But because the client and server are on different hosts and the same-origin policy is in place, CORS can be used to successfully communicate with the server. Here are the steps to make this all happen:
After the application initiates the POST request, the browser will submit a HTTP OPTIONS request to the server before actually sending the POST request. This is the preflight request, and it will look something like this:
OPTIONS /messages/ HTTP/1.1 Host: messages.chat.com Accept: */* Access-Control-Request-Method: POST Access-Control-Request-Headers: accept, content-type Origin: https://chat.com
The Access-Control-Request-Method header informs the server that when the actual request is sent, it will be in the form of a POST. The Access-Control-Request-Headers header lets the server know what other headers will be included in the actual request. Finally, the Origin header indicates the server/host that made the request. In this example, let's say the server is configured to respond to OPTIONS request, and allows any host to make GET, PUT, POST, and DELETE requests. So, the server will respond back with something like this:
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET,PUT,POST,DELETE Access-Control-Allow-Headers: Content-Type, Accept
The Access-Control-Allow-Origin header indicates that all hosts are allowed, the Access-Control-Allow-Methods header indicates the HTTP methods that are allowed, and the Access-Control-Allow-Headers header tells the client what other headers can be included in the POST request.
After the preflight OPTIONS request is acknowledged and approved by the server and sent back to the client, the client can then proceed with submitting the actual POST request.
And that's how CORS works. Remember, the server has to be configured to properly respond to OPTIONS requests if it wants to allow other hosts to access resources. More details about the standard, headers, and compatible browsers can be found on MDN.