title: Inconsistent AJAX Preflight OPTIONS Requests
body: <p>I hope this question isn't too vague, but I feel like I'm missing something fundamental.  Here is the situation I'm in.  We have a thick client application that runs on each user's local machine (Windows 7) and provides a JavaScript API to interface with the app.  We have a web app that is hosted on a server, but makes AJAX requests to localhost to interface with the thick clients JS API.  </p>
  
  <p>On one machine, we load the web app.  All AJAX requests made to localhost are made with no OPTIONS preflight requests.  The net tab of the JS console shows the POSTs being made, and the responses are successful (I get to tell the thick client what headers to include in the response, including all necessary CORS headers).</p>
  
  <p>On another machine, we load the web app from the same server.  However, when AJAX requests are made to localhost, the the (expected) OPTIONS request is made.  Unfortunately, the thick client doesn't respond with the appropriate Access-Control-Allow-Origin header, so the subsequent POST is not allowed to be made.</p>
  
  <p>The browsers don't matter, we've tested the latest versions of Firefox and Chrome and we always get the same behavior from the machines.  We have tested on 4 machines -- 2 send the OPTIONS, 2 just send the POST with no preceding OPTIONS.  It seems like it's up to the browser as to whether or not the AJAX destination differs from the origin and whether or not it send a preflight request.  I can't for the life of my figure out why my machine doesn't send out any OPTIONS requests, even though the AJAX requests (to either localhost or lvh.me) are a different host/domain than the source of the web page, yet loading the same page from another machine does produce them.</p>
  
  <p>Is there some sort of browser setting that effects this?  Or a Windows setting?  Why would we ever see different behavior on this?</p>
  
  <p>UPDATE1:</p>
  
  <p>To simplify and clarify, I have two machines, machineA and machineB.  Both machines load a web app from a remote server, example.com.  Both machines have a full blown thick application installed on their local machines that provides a web server and API.  The web app that gets loaded from example.com contains JavaScript code to submit AJAX requests to localhost, so that each user can interact with their own local instance of the thick client application.  Since localhost != example.com, the requests should be considered cross-domain.  </p>
  
  <p>When machineA submits a standard jQuery AJAX request to localhost (of which, the payload of the POST is simply "true", which will cause the thick client to echo the value back -- just used for polling to determine if the thick client is even available), the POST is made directly with no OPTIONS request:
  POST <a href="http://localhost:4000/cgi-bin/Extensions/GeoLinkConsole/evaljs.html" rel="nofollow">http://localhost:4000/cgi-bin/Extensions/GeoLinkConsole/evaljs.html</a> 200 OK 0ms
  Interestingly, the response from that POST must include the Access-Control-Allow-Origin header with example.com in order to avoid a CORS error.</p>
  
  <p>When machineB loads the same page from example.com, submits the same standard jQuery AJAX request to localhost, with the same "true" payload, the browser submits an OPTIONS request.  This is the behavior I would actually expect.  The OPTIONS request response with a 200 OK, however it doesn't include the Access-Control-Allow-Origin header, so it prevents the POST from going through.  That is not my main concern and is obviously an issue with the thick client.  My biggest question is why some machines are generating the OPTIONS and some aren't.  I hope this helps explain.</p>
  
  <p>Update 2:</p>
  
  <p>I'm including both sets of request headers.  I wasn't aware that the actual request header could affect whether or not an OPTIONS request would be generated.  Right off the bat, I see that the one that is sending the OPTIONS includes both an Acces-Control-Request-Headers and Access-Control-Request_Method  that the other one doesn't...</p>
  
  <p>Request headers from the machine that sends the POST with no OPTIONS:</p>
  
  <pre><code>Accept:    text/plain, */*; q=0.01
  Accept-Encoding:    gzip, deflate
  Accept-Language:    en-US,en;q=0.5
  Content-Length:    354
  Content-Type:   text/plain; charset=UTF-8
  Host:    localhost:4000
  Origin:    http://example.com:7802
  Referer:    http://example.com:7802/
  User-Agent:    Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
  </code></pre>
  
  <p>Request headers from machine that sends OPTIONS:</p>
  
  <pre><code>Accept:    */*
  Accept-Encoding:    gzip, deflate, sdch
  Accept-Language:    en-US,en;q=0.8
  Access-Control-Request-Headers:    accept, content-type, x-csrftoken
  Access-Control-Request-Method:    POST
  Connection:    keep-alive
  Host:    localhost:4000
  Origin:    http://example.com:7802
  Referer:    http://example.com:7802/
  User-Agent:    Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
  </code></pre>
  
  <p>In addition, the machine has a "general" headers block -- I'm assuming the request headers I just posted were for the original POST request and these relate to the preflight OPTIONS request:</p>
  
  <pre><code>Request URL:    http://localhost:4000/cgi-bin/Extensions/GeoLinkConsole/evaljs.html
  Request Method:   OPTIONS
  Status Code:    200 OK
  Remote Address:    127.0.0.1:4000
  </code></pre>