Cross-Origin Request Headers(CORS) with PHP headers

449,998

Solution 1

Access-Control-Allow-Headers does not allow * as accepted value, see the Mozilla Documentation here.

Instead of the asterisk, you should send the accepted headers (first X-Requested-With as the error says).

Update:

* is now accepted is Access-Control-Allow-Headers.

According to MDN Web Docs 2021:

The value * only counts as a special wildcard value for requests without credentials (requests without HTTP cookies or HTTP authentication information). In requests with credentials, it is treated as the literal header name * without special semantics. Note that the Authorization header can't be wildcarded and always needs to be listed explicitly.

Solution 2

Handling CORS requests properly is a tad more involved. Here is a function that will respond more fully (and properly).

/**
 *  An example CORS-compliant method.  It will allow any GET, POST, or OPTIONS requests from any
 *  origin.
 *
 *  In a production environment, you probably want to be more restrictive, but this gives you
 *  the general idea of what is involved.  For the nitty-gritty low-down, read:
 *
 *  - https://developer.mozilla.org/en/HTTP_access_control
 *  - https://fetch.spec.whatwg.org/#http-cors-protocol
 *
 */
function cors() {
    
    // Allow from any origin
    if (isset($_SERVER['HTTP_ORIGIN'])) {
        // Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
        // you want to allow, and if so:
        header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');    // cache for 1 day
    }
    
    // Access-Control headers are received during OPTIONS requests
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
            // may also be using PUT, PATCH, HEAD etc
            header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
        
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
            header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
    
        exit(0);
    }
    
    echo "You have CORS!";
}

Security Notes

Check the HTTP_ORIGIN header against a list of approved origins.

If the origin isn't approved, then you should deny the request.

Please read the spec.

TL;DR

When a browser wants to execute a cross-site request it first confirms that this is okay with a "pre-flight" request to the URL. By allowing CORS you are telling the browser that responses from this URL can be shared with other domains.

CORS does not protect your server. CORS attempts to protect your users by telling browsers what the restrictions should be on sharing responses with other domains. Normally this kind of sharing is utterly forbidden, so CORS is a way to poke a hole in the browser's normal security policy. These holes should be as small as possible, so always check the HTTP_ORIGIN against some kind of internal list.

There are some dangers here, especially if the data the URL serves up is normally protected. You are effectively allowing browser content that originated on some other server to read (and possibly manipulate) data on your server.

If you are going to use CORS, please read the protocol carefully (it is quite small) and try to understand what you're doing. A reference URL is given in the code sample for that purpose.

Header security

It has been observed that the HTTP_ORIGIN header is insecure, and that is true. In fact, all HTTP headers are insecure to varying meanings of the term. Unless a header includes a verifiable signature/hmac, or the whole conversation is authenticated via TLS, headers are just "something the browser has told me".

In this case, the browser is saying "an object from domain X wants to get a response from this URL. Is that okay?" The point of CORS is to be able to answer, "yes I'll allow that".

Solution 3

I got the same error, and fixed it with the following PHP in my back-end script:

header('Access-Control-Allow-Origin: *');

header('Access-Control-Allow-Methods: GET, POST');

header("Access-Control-Allow-Headers: X-Requested-With");

Solution 4

Many description internet-wide don't mention that specifying Access-Control-Allow-Origin is not enough. Here is a complete example that works for me:

<?php
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: POST, GET, DELETE, PUT, PATCH, OPTIONS');
        header('Access-Control-Allow-Headers: token, Content-Type');
        header('Access-Control-Max-Age: 1728000');
        header('Content-Length: 0');
        header('Content-Type: text/plain');
        die();
    }

    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/json');

    $ret = [
        'result' => 'OK',
    ];
    print json_encode($ret);

Solution 5

I've simply managed to get dropzone and other plugin to work with this fix (angularjs + php backend)

 header('Access-Control-Allow-Origin: *'); 
    header("Access-Control-Allow-Credentials: true");
    header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
    header('Access-Control-Max-Age: 1000');
    header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token , Authorization');

add this in your upload.php or where you would send your request (for example if you have upload.html and you need to attach the files to upload.php, then copy and paste these 4 lines). Also if you're using CORS plugins/addons in chrome/mozilla be sure to toggle them more than one time,in order for CORS to be enabled

Share:
449,998

Related videos on Youtube

Machavity
Author by

Machavity

Room Owner of Stack Overflow Close Vote Reviewers(SOCVR) Part time contributor to Charcoal and SOBotics Elected moderator on Stack Overflow (2020)

Updated on July 17, 2022

Comments

  • Machavity
    Machavity almost 2 years

    I have a simple PHP script that I am attempting a cross-domain CORS request:

    <?php
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: *");
    ...
    

    Yet I still get the error:

    Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers

    Anything I'm missing?

    • Whats91356Java
      Whats91356Java about 2 years
      2022 and the top answer really works (on some PHP versions), nice.
  • Jules
    Jules almost 12 years
    Note that sending the HTTP Origin value back as the allowed origin will allow anyone to send requests to you with cookies, thus potentially stealing a session from a user who logged into your site then viewed an attacker's page. You either want to send '*' (which will disallow cookies thus preventing session stealing) or the specific domains for which you want the site to work.
  • slashingweapon
    slashingweapon over 11 years
    Agreed. In practice you probably wouldn't allow just any old domain to use your CORS service, you would restrict it to some set that you decided to trust.
  • ncubica
    ncubica over 9 years
    FYI, this solution only worked for me in a Linux server, in IIS for some reason just didn't work, I dont know if its my hosting or just it's not suitable for IIS
  • Roy Calderon
    Roy Calderon about 9 years
    This solution worked flawlessly in my PHP 5.6.2 at backend, and AngularJS 1.3.12 at frontend.
  • Renan Franca
    Renan Franca over 8 years
    The only that's truly work!.. Just change Access-Control-Allow-Origin: * TO Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}
  • Youstay Igo
    Youstay Igo over 8 years
    This isn't working for me. Do you have any sample for a working CORS server php script that can be ajax-requested, in working condition on the web? If so, please post the link here. Nothing seems to be working for me :(
  • DanPride
    DanPride almost 6 years
    THANKS !! What an absurd waste of time it took to finally find this, and test it like so many other solutions up here, and get the fine surprise "I am done with GD Coors !!
  • halfpastfour.am
    halfpastfour.am over 5 years
    Please explain why it isn't enough and what minimal example is enough.
  • Csongor Halmai
    Csongor Halmai over 5 years
    Unfortunately, I don't remember exactly and I have no time now to investigate it again but, as much as I remember, there were some basic assumptions from the webserver's/browser's side which made it not working. This was the minimal code that worked for me.
  • AlexKh
    AlexKh about 4 years
    Great solution! Thank you so much!
  • jub0bs
    jub0bs almost 4 years
    By unconditionally allowing any origin with ACAC: true, you're essentially throwing the Same-Origin Policy out the window. This answer is terrible advice from a security point of view, and it should be downvoted to oblivion.
  • dmuensterer
    dmuensterer over 3 years
    Downvoted for using $_SERVER['HTTP_ORIGIN']. This is a huge secury risk.
  • slashingweapon
    slashingweapon over 3 years
    It is true that $_SERVER['HTTP_ORIGIN] is not "secure" in the sense that your app has no way of verifying the true origin of the request. However, it is the browser's job to protect this header. Your app is not trying to prevent people from various orgs from using it. Rather, your app is confirming to the browser that cross-site requests from certain domains are acceptable at this URL.
  • undefined
    undefined over 3 years
    As of 2021, it looks like * is now accepted as per the MDN docs.
  • jumpjack
    jumpjack over 3 years
    my $_SERVER['HTTP_ORIGIN'] is "defined but empty" (???)
  • Rohit Nair
    Rohit Nair about 3 years
    Be careful while using '*' wildcard. Never open it unless that's what you really intend to do. As for testing your angular app specify localhost:4200 and it will work while still being safer.
  • YazidEF
    YazidEF about 3 years
    This solves my issue - apparently my PHP webservice not able to entertain OPTIONS request properly - on which my Angular front end is relying upon prior to sending the POST request. Thanks!
  • ashutosh
    ashutosh almost 3 years
    if already sent in virtul host of apache ..then only this code work ..if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { die(); }
  • jberculo
    jberculo almost 3 years
    Basically what it says here is that it is enough, just not if the request method is 'options'.
  • jub0bs
    jub0bs over 2 years
  • slashingweapon
    slashingweapon over 2 years
    Yeah, never mind I recommend people actually try to understand this stuff, or warn them about what a big deal it is. He just took the code snippet and presented it as if that were the whole solution. So very clever of him, and very annoying to me. But if it gets people to pay attention to their security when writing CORS headers then it's not all bad.
  • Yougesh
    Yougesh over 2 years
    I have used this in Codeigniter 4.1.3 and it doest work
  • hugob1
    hugob1 over 2 years
    Tested on LAMP Server running PHP 7.4.x
  • Lepy
    Lepy about 2 years
    This worked really well on VUE + XAMPP (PHP)
  • Dejell
    Dejell about 2 years
    do we need to allow Origin header? sounds weird
  • puntofisso
    puntofisso about 2 years
    Of all the StackOverflow answers this is the best.
  • DistributedAI
    DistributedAI almost 2 years
    should these three lines added in top of PHP file ? Like <?php header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST'); header("Access-Control-Allow-Headers: X-Requested-With");