452

I would like to add a custom header to an AJAX POST request from jQuery.

I have tried this:

$.ajax({
    type: 'POST',
    url: url,
    headers: {
        "My-First-Header":"first value",
        "My-Second-Header":"second value"
    }
    //OR
    //beforeSend: function(xhr) { 
    //  xhr.setRequestHeader("My-First-Header", "first value"); 
    //  xhr.setRequestHeader("My-Second-Header", "second value"); 
    //}
}).done(function(data) { 
    alert(data);
});

When I send this request and I watch with FireBug, I see this header:

OPTIONS xxxx/yyyy HTTP/1.1
Host: 127.0.0.1:6666
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Origin: null
Access-Control-Request-Method: POST
Access-Control-Request-Headers: my-first-header,my-second-header
Pragma: no-cache
Cache-Control: no-cache

Why do my custom headers go to Access-Control-Request-Headers:

Access-Control-Request-Headers: my-first-header,my-second-header

I was expecting a header values like this:

My-First-Header: first value
My-Second-Header: second value

Is it possible?

FabricioG
  • 3,107
  • 6
  • 35
  • 74
fingerup
  • 4,828
  • 2
  • 16
  • 22
  • 3
    possible duplicate of [How can I add a custom HTTP header to ajax request with js or jQuery?](http://stackoverflow.com/questions/7686827/how-can-i-add-a-custom-http-header-to-ajax-request-with-js-or-jquery) – Prestaul Oct 23 '13 at 18:45
  • 1
    The title of the question should state that **"For Other Domain"** – Accountant م Sep 09 '19 at 19:13
  • This is an `OPTIONS` request (so-called pre-flight request) to understand if remote host is willing to receive requests at all. You should look at the actual (i.e. `POST`) request to see if it has all the headers properly set. – AnrDaemon Feb 17 '22 at 09:30

8 Answers8

484

Here is an example how to set a request header in a jQuery Ajax call:

$.ajax({
  type: "POST",
  beforeSend: function(request) {
    request.setRequestHeader("Authority", authorizationToken);
  },
  url: "entities",
  data: "json=" + escape(JSON.stringify(createRequestObject)),
  processData: false,
  success: function(msg) {
    $("#results").append("The result =" + StringifyPretty(msg));
  }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
milkovsky
  • 8,772
  • 4
  • 28
  • 32
  • 11
    thx, i know send Ajax Request with custom Header. My problem is with different domain.. All my custom headers are put into Access-Control-Request-Headers. it's just security in browser : cross-domain. – fingerup Nov 23 '12 at 17:36
  • yeah, in browser cross-domain requests can cause some difficulties. you can always use some proxy script to send your cross-domain requests – milkovsky Nov 25 '12 at 13:14
  • How do I add the headers with API KEY? – Si8 Jan 25 '17 at 14:22
  • @Si8 please check this post http://stackoverflow.com/questions/5517281/place-api-key-in-headers-or-url – milkovsky Feb 06 '17 at 10:15
206

This code below works for me. I always use only single quotes, and it works fine. I suggest you should use only single quotes or only double quotes, but not mixed up.

$.ajax({
    url: 'YourRestEndPoint',
    headers: {
        'Authorization':'Basic xxxxxxxxxxxxx',
        'X-CSRF-TOKEN':'xxxxxxxxxxxxxxxxxxxx',
        'Content-Type':'application/json'
    },
    method: 'POST',
    dataType: 'json',
    data: YourData,
    success: function(data){
      console.log('succes: '+data);
    }
  });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
G-raph
  • 2,141
  • 1
  • 10
  • 4
  • 1
    thx, i know send Ajax Request with custom Header. My problem is with different domain.. All my custom headers are put into Access-Control-Request-Headers. it's just security in browser : cross-domain. – fingerup Nov 14 '14 at 13:53
  • Thanks, I had set headers: "Authorization: Basic XXXXXX" accidentally, and iOS 9 / Safari 9 was throwing SyntaxError DOM 12 on a project. – Mark Oct 01 '15 at 15:00
  • 4
    Do you mean double or single quotes? I don't think anyone would use double brackets. – DBS Jun 01 '16 at 08:47
  • 4
    Double or single quotes (not "brackets") have nothing to do here. – Pere Mar 07 '17 at 17:07
  • its X-CSRF-TOKEN for the Laravel 5.6 and above – Abdul Rahman A Samad Nov 02 '18 at 03:01
140

What you saw in Firefox was not the actual request; note that the HTTP method is OPTIONS, not POST. It was actually the 'pre-flight' request that the browser makes to determine whether a cross-domain AJAX request should be allowed:

http://www.w3.org/TR/cors/

The Access-Control-Request-Headers header in the pre-flight request includes the list of headers in the actual request. The server is then expected to report back whether these headers are supported in this context or not, before the browser submits the actual request.

karlgold
  • 7,970
  • 2
  • 29
  • 22
19

Because you send custom headers so your CORS request is not a simple request, so the browser first sends a preflight OPTIONS request to check that the server allows your request.

Enter image description here

If you turn on CORS on the server then your code will work. You can also use JavaScript fetch instead (here)

let url='https://server.test-cors.org/server?enable=true&status=200&methods=POST&headers=My-First-Header,My-Second-Header';


$.ajax({
    type: 'POST',
    url: url,
    headers: {
        "My-First-Header":"first value",
        "My-Second-Header":"second value"
    }
}).done(function(data) {
    alert(data[0].request.httpMethod + ' was send - open chrome console> network to see it');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Here is an example configuration which turns on CORS on nginx (nginx.conf file):

location ~ ^/index\.php(/|$) {
   ...
    add_header 'Access-Control-Allow-Origin' "$http_origin" always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    if ($request_method = OPTIONS) {
        add_header 'Access-Control-Allow-Origin' "$http_origin"; # DO NOT remove THIS LINES (doubled with outside 'if' above)
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Max-Age' 1728000; # cache preflight value for 20 days
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'My-First-Header,My-Second-Header,Authorization,Content-Type,Accept,Origin';
        add_header 'Content-Length' 0;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        return 204;
    }
}

Here is an example configuration which turns on CORS on Apache (.htaccess file)

# ------------------------------------------------------------------------------
# | Cross-domain Ajax requests                                                 |
# ------------------------------------------------------------------------------

# Enable cross-origin Ajax requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/

# <IfModule mod_headers.c>
#    Header set Access-Control-Allow-Origin "*"
# </IfModule>

#Header set Access-Control-Allow-Origin "http://example.com:3000"
#Header always set Access-Control-Allow-Credentials "true"

Header set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Allow-Headers "My-First-Header,My-Second-Header,Authorization, content-type, csrf-token"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
3

And that is why you can't create a bot with JavaScript, because your options are limited to what the browser allows you to do. You can't just order a browser that follows the CORS policy, which most browsers follow, to send random requests to other origins and allow you to get the response that simply!

Additionally, if you tried to edit some request headers manually, like origin-header from the developers tools that come with the browsers, the browser will refuse your edit and may send a preflight OPTIONS request.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
the accountant
  • 506
  • 6
  • 14
2

Try to add 'Content-Type':'application/json':

 $.ajax({
        type: 'POST',
        url: url,
        headers: {
            'Content-Type':'application/json'
        }
        //OR
        //beforeSend: function(xhr) {
        //  xhr.setRequestHeader("My-First-Header", "first value");
        //  xhr.setRequestHeader("My-Second-Header", "second value");
        //}
    }).done(function(data) {
        alert(data);
    });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abd Abughazaleh
  • 4,615
  • 3
  • 44
  • 53
1

From the client side, I can’t solve this problem.

From the Node.js and Express.js side, you can use the cors module to handle it.

var express    = require('express');
var app        = express();
var bodyParser = require('body-parser');
var cors       = require('cors');

var port = 3000;
var ip = '127.0.0.1';

app.use('*/myapi',
        cors(), // With this row OPTIONS has handled
        bodyParser.text({type: 'text/*'}),
        function(req, res, next) {
            console.log('\n.----------------' + req.method + '------------------------');
            console.log('| prot:' + req.protocol);
            console.log('| host:' + req.get('host'));
            console.log('| URL:' + req.originalUrl);
            console.log('| body:', req.body);
            //console.log('| req:', req);
            console.log('.----------------' + req.method + '------------------------');
            next();
        });

app.listen(port, ip, function() {
    console.log('Listening to port:  ' + port);
});

console.log(('dir:' + __dirname));
console.log('The server is up and running at http://' + ip + ':' + port + '/');

Without cors(), these OPTIONS have to appear before POST.

.----------------OPTIONS------------------------
| prot:http
| host:localhost:3000
| url:/myapi
| body: {}
.----------------OPTIONS------------------------

.----------------POST------------------------
| prot:http
| host:localhost:3000
| url:/myapi
| body: <SOAP-ENV:Envelope .. P-ENV:Envelope>
.----------------POST------------------------

The Ajax call:

$.ajax({
    type: 'POST',
    contentType: "text/xml; charset=utf-8",

    //These does not work
    //beforeSend: function(request) {
    //  request.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
    //  request.setRequestHeader('Accept', 'application/vnd.realtime247.sct-giro-v1+cms');
    //  request.setRequestHeader('Access-Control-Allow-Origin', '*');
    //  request.setRequestHeader('Access-Control-Allow-Methods', 'POST, GET');
    //  request.setRequestHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type');
    //},
    //headers: {
    //  'Content-Type': 'text/xml; charset=utf-8',
    //  'Accept': 'application/vnd.realtime247.sct-giro-v1+cms',
    //  'Access-Control-Allow-Origin': '*',
    //  'Access-Control-Allow-Methods': 'POST, GET',
    //  'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type'
    //},
    url: 'http://localhost:3000/myapi',
    data: '<SOAP-ENV:Envelope .. P-ENV:Envelope>',
    success: function(data) {
      console.log(data.documentElement.innerHTML);
    },
    error: function(jqXHR, textStatus, err) {
      console.log(jqXHR, '\n', textStatus, '\n', err)
    }
  });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
-14

Try to use the rack-cors gem. And add the header field in your Ajax call.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131