jQuery $.ajax(), $.post sending "OPTIONS" as REQUEST_METHOD in Firefox
Asked Answered
U

23

338

Having trouble with what I thought was a relatively simple jQuery plugin...

The plugin should fetch data from a php script via ajax to add options to a <select>. The ajax request is pretty generic:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

This seems to work fine in Safari. In Firefox 3.5, the REQUEST_TYPE on the server is always 'OPTIONS', and the $_POST data does not appear. Apache logs the request as type 'OPTIONS':

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Why would this ajax call work in Safari, but not Firefox, and how do I fix it for Firefox?

Response Headers
Date: Wed, 08 Jul 2009 21:22:17 GMT
Server:Apache/2.0.59 (Unix) PHP/5.2.6 DAV/2
X-Powered-By: PHP/5.2.6
Content-Length  46
Keep-Alive  timeout=15, max=100
Connection  Keep-Alive
Content-Type    text/html

Request Headers
Host    orderform:8888
User-Agent  Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5
Accept  text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset  ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive  300
Connection  keep-alive
Origin  http://ux.inetu.act.org
Access-Control-Request-Method   POST
Access-Control-Request-Headers  x-requested-with

Here is a picture of the Firebug output:

Ungava answered 8/7, 2009 at 18:26 Comment(6)
Can you post the firebug response and request headers. I am not getting any error when I run similar code in Firefox.Hooves
Added header info, and a picture from Firebug.Ungava
Just had this same problem while implementing an embedded webserver. Thanks for asking :)Odysseus
If you are looking for a Java JAX-RS solutions, see here: Access-Control-Allow-OriginNeighboring
The behavior of firefox seems to have changed now? I don't get any option requests.Headfirst
When the response header contains "Access-Control-Allow-Headers: content-type", i got an OPTIONS request and then a POST request.Beaune
E
169

The reason for the error is the same origin policy. It only allows you to do XMLHTTPRequests to your own domain. See if you can use a JSONP callback instead:

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
Elf answered 10/7, 2009 at 12:34 Comment(8)
Note: See this docs.jquery.com/Release:jQuery_1.2/… for info on using JSONP. You will need the additional "=?" in your query string to trigger JSONPThumbstall
why is firefox the only browser to do this? I want a post not a get.Nudism
$.getScript should be possible cross-domain, see docs.jquery.com/Release:jQuery_1.2/Ajax#Cross-Domain_getScript (but it is not for me in FF 3.6)Counterpoison
Crossite-POST: Does anybody know a solution to do a POST with application/json as Content-Type?Phlebotomy
So what exactly is the solution?Protoactinium
Looking for a solution to this too and using getJSON instead of ajax call doesn't do it for me as it's much more limited.Andromada
@Phlebotomy for that you are going to need to use CORS, which is well supported in newer browsers... limited support in IE8-9, and needs server-side support.Executant
@Phlebotomy $.ajax with option content-type: "application/json", and the server response header contains "Access-Control-Allow-Headers: content-type", i got an OPTIONS request and a POST request, still looking for whyBeaune
C
57

I used the following code on Django side to interpret the OPTIONS request and to set the required Access-Control headers. After this my cross domain requests from Firefox started working. As said before, the browser first sends the OPTIONS request and then immediately after that the POST/GET

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Edit: it seems to be that at least in some cases you also need to add the same Access-Control headers to the actual response. This can be a little bit confusing, since the request seems to succeed, but Firefox does not pass the contents of the response to the Javascript.

Cerellia answered 19/8, 2010 at 8:46 Comment(4)
Your edit about the actual POST/GET response is a bit scary; if anyone can confirm that, then please let us know here!Mercenary
I don't know if it is as bug or a feature, but seems to be that somebody else has noticed it as well. See for example kodemaniak.de/?p=62 and search for "empty response body"Indecision
There's a difference between simple requests and those that need preflight. Your "solution" will only work with preflight requests, so it's no real solution. Whenever you get an "Origin:"-header in the request headers, you should reply with that being allowed.Emlyn
I believe the header Access-Control-Allow-Headers should contain the value x-csrf-token, not x-csrftoken.Biotic
G
17

This mozilla developer center article describes various cross-domain request scenarios. The article seems to indicate that a POST request with content type of 'application/x-www-form-urlencoded' should be sent as a 'simple request' (with no 'preflight' OPTIONS request). I found , however, that Firefox sent the OPTIONS request, even though my POST was sent with that content type.

I was able to make this work by creating an options request handler on the server, that set the 'Access-Control-Allow-Origin' response header to '*'. You can be more restrictive by setting it to something specific, like 'http://someurl.com'. Also, I have read that, supposedly, you can specify a comma-separated list of multiple origins, but I couldn't get this to work.

Once Firefox receives the response to the OPTIONS request with an acceptable 'Access-Control-Allow-Origin' value, it sends the POST request.

Gammy answered 19/5, 2010 at 13:32 Comment(1)
Note: Access-Control-Allow-Origin can have only one value (you can't use a comma-separated list), however you can write (or use) a plugin that will programmatically change it to allow different values.Eyeglasses
R
15

I've fixed this issue using an entirely-Apache based solution. In my vhost / htaccess I put the following block:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

You may not need the latter part, depending on what happens when Apache executes your target script. Credit goes to the friendly ServerFault folk for the latter part.

Railroader answered 6/2, 2011 at 7:49 Comment(1)
Your answer helped me, but if need some logic behind CORS, it doesn't solve completely.Hardcastle
L
10

This PHP at the top of the responding script seems to work. (With Firefox 3.6.11. I have not yet done a lot of testing.)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}
Leoine answered 28/10, 2010 at 17:19 Comment(2)
This might be a matter of taste, but always sending those response headers (also for GET, POST, ...) is a bit too much to my liking. (And, I wonder if always sending those complies with the specs?)Mercenary
wrap it in if($_SERVER['HTTP_ORIGIN']). If that header is there, it's an CORS-request, if not, well, no need to send anything.Emlyn
F
7

I had same problem with sending requests to google maps, and solution is quite simple with jQuery 1.5 - for dataType use dataType: "jsonp"

Fao answered 10/2, 2011 at 2:40 Comment(2)
Incompatible with method POST.Danica
It works with a GET method but it's a very limited solution. For example, by doing so you can't send back a response with a specific header including a token.Chewink
H
7

Culprit is preflight request using OPTIONS method

For HTTP request methods that can cause side-effects on user data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method.

Web specification refer to: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

I resolved the problem by adding following lines in Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }
Humerus answered 22/4, 2016 at 15:4 Comment(1)
This answer is very helpful thanks. The fact that the browser is sending a preflight request with an OPTIONS method is non-obvious.Virgy
L
4

I was looking through source 1.3.2, when using JSONP, the request is made by building a SCRIPT element dynamically, which gets past the browsers Same-domain policy. Naturally, you can't make a POST request using a SCRIPT element, the browser would fetch the result using GET.

As you are requesting a JSONP call, the SCRIPT element is not generated, because it only does this when the Type of AJAX call is set to GET.

http://dev.jquery.com/ticket/4690

Lacey answered 22/9, 2009 at 19:51 Comment(0)
T
4

We had a problem like this with ASP.Net. Our IIS was returning an Internal Server Error when trying to execute a jQuery $.post to get some html content due to PageHandlerFactory was restricted to respond only GET,HEAD,POST,DEBUG Verbs. So you can change that restriction adding the verb "OPTIONS" to the list or selecting "All Verbs"

You can modify that in your IIS Manager, selecting your website, then selecting Handler Mappings, double click in your PageHandlerFactory for *.apx files as you need (We use Integrated application pool with framework 4.0). Click on Request Restrictions, then go to Verbs Tabn and apply your modification.

Now our $.post request is working as expected :)

Toluate answered 23/2, 2011 at 20:45 Comment(0)
I
2

Check if your form's action URL includes the www part of the domain, while the original page you have opened is viewed without www.

Typically done for Canonical Urls..

I struggled for hours before stumbling upon this article and found the hint of Cross Domain.

Incisure answered 8/7, 2009 at 18:26 Comment(0)
C
2

I seems that if o.url = 'index.php' and this file exists is ok and returning a success message in the console. It returns an error if I use url:http://www.google.com

If doing a post request why not using directly the $.post method:

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

It is so much simpler.

Celom answered 8/7, 2009 at 18:38 Comment(1)
Got the same thing with this...thought I should use $.ajax() so I could at least get some debug info on the error condition..Ungava
E
1

Another possibility to circumvent the problem is to use a proxy script. That method is described for example here

Elisaelisabet answered 4/8, 2010 at 12:26 Comment(0)
B
1

Solution to this is:

  1. use dataType: json
  2. add &callback=? to your url

this worked on calling Facebook API and with Firefox. Firebug is using GET instead of OPTIONS with the above conditions (both of them).

Barnwell answered 19/1, 2011 at 20:26 Comment(0)
E
1

I have posted a clear example of how to solve this if control the server code of the domain you are POSTing to. This answer is touched on in this thread, but this more clearly explains it IMO.

How do I send a cross-domain POST request via JavaScript?

Emblements answered 30/9, 2011 at 1:48 Comment(0)
S
0

Can you try this without

contentType:application/x-www-form-urlencoded

Sukhum answered 8/7, 2009 at 18:40 Comment(0)
D
0

Try adding the option:

dataType: "json"

Dockhand answered 10/7, 2009 at 12:51 Comment(1)
that worked, why json is considered "safe" for cross-domain requests?Protoactinium
G
0

I had a similar problem with trying to use the Facebook API.

The only contentType which didn't send the Preflighted request seemed to be just text/plain... not the rest of the parameters mentioned at mozilla here

  • Why is this the only browser which does this?
  • Why doesn't Facebook know and accept the preflight request?

FYI: The aforementioned Moz doc suggests X-Lori headers should trigger a Preflighted request ... it doesn't.

Grantley answered 28/6, 2010 at 17:39 Comment(0)
C
0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}
Chaim answered 1/1, 2013 at 10:26 Comment(1)
The question was already answered 6 months ago. How does this solve it?Burweed
L
0

You need to do some work on server side. I see you are using PHP on server side, but solution for .NET web application is here: Cannot set content-type to 'application/json' in jQuery.ajax

Do the same in PHP script and it will work. Simply: At first request browser is asking server if is allowed to send such data with such type and second request is the proper/allowed.

Liebowitz answered 11/9, 2013 at 11:44 Comment(0)
R
0

Try to add the following:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  
Ribald answered 9/6, 2014 at 13:1 Comment(0)
C
0

I used a proxy url to solve a similar problem when I want to post data to my apache solr hosted in another server. (This may not be the perfect answer but it solves my problem.)

Follow this URL: Using Mode-Rewrite for proxying, I add this line to my httpd.conf:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Therefore, I can just post data to /solr instead of posting data to http://ip:8983/solr/*. Then it will be posting data in the same origin.

Clavicembalo answered 21/7, 2015 at 8:13 Comment(0)
H
0

I already have this code handling well my cors situation in php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

And it was working fine locally and remotely, but not for uploads when remote.

Something happen with apache/php OR my code, I didn't bother to search it, when you request OPTIONS it returns my header with cors rules but with 302 result. Therefore my browser doesn't recognise as an acceptable situation.

What I did, based on @Mark McDonald answer, is just put this code after my header:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Now, when requesting OPTIONS it will just send the header and 202 result.

Hardcastle answered 15/3, 2017 at 15:14 Comment(0)
A
-1

Please be advised:

JSONP supports only the GET request method.

*Send request by firefox:*

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Above request send by OPTIONS(while ==>type: 'POST')!!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

But above request send by GET(while ==>type: 'POST')!!!!

When you are in "cross-domain communication" , pay attention and be careful.

Angers answered 9/8, 2015 at 6:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.