211

I'm doing a ajax call to my own server on a platform which they set prevent these ajax calls (but I need it to fetch the data from my server to display retrieved data from my server's database). My ajax script is working , it can send the data over to my server's php script to allow it to process. However it cannot get the processed data back as it is blocked by "Access-Control-Allow-Origin"

I have no access to that platform's source/core. so I can't remove the script that it disallowing me to do so. (P/S I used Google Chrome's Console and found out this error)

The Ajax code as shown below:

 $.ajax({
     type: "GET",
     url: "http://example.com/retrieve.php",
     data: "id=" + id + "&url=" + url,
     dataType: 'json',   
     cache: false,
     success: function(data)
      {
        var friend = data[1];              
        var blog = data[2];           
        $('#user').html("<b>Friends: </b>"+friend+"<b><br> Blogs: </b>"+blog);

      } 
  });

or is there a JSON equivalent code to the ajax script above ? I think JSON is allowed.

I hope someone could help me out.

sideshowbarker
  • 62,215
  • 21
  • 143
  • 153
ETAN
  • 2,852
  • 5
  • 22
  • 34
  • all the answers to your question so far explained a way to rewrite your server code so you ajax will work. None of them is about bypassing, as you asked specifically in your question. Did you find anyway to actually bypass this header? I really doubt that there would be one. – Moradnejad Feb 09 '17 at 11:50
  • there is no way to baypass it. but you can put a file on your backend that performs the request. So you call per ajax the file on your own server, that file loads the data from retrieve.php and send them back to your javascript. In that case there are no CORS rules blocking you. – Jonathan Pauw Mar 16 '20 at 20:19
  • The secure websocket protocol `wss://` is not subject to CORS blocking. – NVRM Nov 02 '20 at 09:59

6 Answers6

386

Put this on top of retrieve.php:

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

Note that this effectively disables CORS protection, and leaves your users exposed to attack. If you're not completely certain that you need to allow all origins, you should lock this down to a more specific origin:

header('Access-Control-Allow-Origin: https://www.example.com');

Please refer to following stack answer for better understanding of Access-Control-Allow-Origin

https://stackoverflow.com/a/10636765/413670

Xhynk
  • 12,250
  • 8
  • 29
  • 61
Rafay
  • 30,236
  • 5
  • 64
  • 98
  • 55
    Thats rather unsafe. Check out my answer at the bottom. – Rob Jun 25 '13 at 08:09
  • 3
    tnx, but you should not allow access to all origins as mentioned by @RobQuist in his comment, and in his answer provided a better approach – Rafay Dec 13 '13 at 17:37
  • 2
    So I found this page because I needed to actually 'bypass' Access Control on a server. The solution here isn't bypassing anything but simply properly configuring Access Control on his own server. In case anyone out there actually needs to bypass this they can use PHP's file_get_contents($remote_url);. There are obviously many ways to do this but this is how I did it. – Shawn Whinnery Mar 05 '14 at 23:39
  • 1
    @ShawnWhinnery that is basically the act of "proxying". Good solution if you really want to dynamically load data from another website that you have no control of. – Rob Feb 29 '16 at 16:27
  • what exactly didnt work @jairhumberto any errors on the browser console? or on the server end? how does your code look like? – Rafay Sep 30 '17 at 23:25
  • nope, nothing. just nothing happens as if the code wasnt even there. I just try to make an ajax request to another domain file that has as the first line the header setted via header function – jairhumberto Oct 01 '17 at 03:34
  • 1
    wanted to run PHP script from dotnet core - moved php script to my other URL but was getting cross-site scripting error. added the code you showed to top of PHP and worked perfectly. Thanks! – raddevus Apr 21 '18 at 16:36
  • This is a poor practice, and leaves you wide open for cross site scripting. You pretty much only want your own domain allowed by default. The answer you really want is this: `header('Access-Control-Allow-Origin: ' . 'http' . ( ( array_key_exists( 'HTTPS', $_SERVER ) && $_SERVER['HTTPS'] && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) ? 's' : null ) . '://' . $_SERVER['HTTP_HOST'] );` – mopsyd Apr 23 '18 at 00:55
  • What this does, is allow your own domain, and honors the current SSL settings on your server. If you want to add access for additional domains, just add another `Access-Control-Allow-Origin` header for each additional domain. – mopsyd Apr 23 '18 at 00:57
  • This worked like a charm. Thank you! – Joseph Kreifels II Mar 25 '21 at 19:52
308

Okay, but you all know that the * is a wildcard and allows cross site scripting from every domain?

You would like to send multiple Access-Control-Allow-Origin headers for every site that's allowed to - but unfortunately its officially not supported to send multiple Access-Control-Allow-Origin headers, or to put in multiple origins.

You can solve this by checking the origin, and sending back that one in the header, if it is allowed:

$origin = $_SERVER['HTTP_ORIGIN'];
$allowed_domains = [
    'http://mysite1.com',
    'https://www.mysite2.com',
    'http://www.mysite2.com',
];

if (in_array($origin, $allowed_domains)) {
    header('Access-Control-Allow-Origin: ' . $origin);
}

Thats much safer. You might want to edit the matching and change it to a manual function with some regex, or something like that. At least this will only send back 1 header, and you will be sure its the one that the request came from. Please do note that all HTTP headers can be spoofed, but this header is for the client's protection. Don't protect your own data with those values. If you want to know more, read up a bit on CORS and CSRF.

Why is it safer?

Allowing access from other locations then your own trusted site allows for session highjacking. I'm going to go with a little example - image Facebook allows a wildcard origin - this means that you can make your own website somewhere, and make it fire AJAX calls (or open iframes) to facebook. This means you can grab the logged in info of the facebook of a visitor of your website. Even worse - you can script POST requests and post data on someone's facebook - just while they are browsing your website.

Be very cautious when using the ACAO headers!

Rob
  • 4,653
  • 4
  • 23
  • 40
  • 13
    I think you need to put http:// in front of each item in the list. At least I did for one site I was working on. – blak3r Jun 18 '13 at 10:17
  • 2
    Sadly, this doesn't seem to work. I believe that only one exception can be provided per call to header(). – lewsid Jun 18 '13 at 17:29
  • 5
    @Shanimal & lewsid -> I guess comma seperated doesn't work indeed. Reference: http://www.w3.org/TR/cors/ – Rob Jun 25 '13 at 08:13
  • 3
    For dealing with a list of domains, here a relevant answer: http://stackoverflow.com/a/1850482/766177 – Valentin Despa Jul 10 '13 at 13:28
  • If i need a partial domain, it´s ok use `Access-Control-Allow-Origin: http:*.mysite1.com` ? – Rafael Oct 01 '13 at 14:42
  • 2
    @Rafael , yes it is. This wildcards all subdomains on mysite1.com. – Rob Jan 06 '14 at 20:48
  • This will now work. Say the request comes from http://mysite1.com. You will get an error that the header contains http://www.mysite2.com. – reggie Apr 23 '15 at 08:42
  • well if i want to use the API from a mobile application what can i enter instead of the url? – philx_x Jan 26 '16 at 08:05
  • You'll have to check where the request came from.. If you really want to use an API from a mobile device you should allow almost all origins, but do a lot of session checking yourself (check out oAuth2, there's a lot of info on that for mobile app development and server-side) – Rob Jan 26 '16 at 13:13
  • What if I want to have a service for mobile app, what would it be the domain for an app? – Jose Rojas Feb 29 '16 at 15:03
  • @RobQuist Rob, How to achieve, If I'm directly accessing media file from my site say, http://example.com/mp3/sample.mp3 than within source file?? – Jenson M John Apr 02 '16 at 08:58
  • If you add more than **1** header, any request will result in beeing invalid. Source [w3c](https://www.w3.org/TR/cors/#access-control-allow-origin-response-header) - only 1 header with 1 origin is valid. – Daniel W. Jun 16 '16 at 15:04
  • 13
    This is pointless adding 4 headers like that because each call to `header()` replaces the previous header of the same type. So really all you are doing is setting the last header. The [manual entry](http://php.net/manual/en/function.header.php) states that you can set a second parameter of `false` to prevent the previous header being overwritten. – BadHorsie Jun 21 '16 at 16:28
  • This is false answer , in this case only last header should work. – Vahe Galstyan Nov 17 '16 at 09:05
  • You right BadHorsie & Vahe Galstyan. I've updates the answer to make this clearer. – Rob Nov 17 '16 at 13:46
  • What if you are implementing an API that should be available for everyone, how do you deal with that? – Vladimir Nul Jan 27 '17 at 12:46
  • @VladimirNul Then you could add the wildcard. – Rob Jan 27 '17 at 13:01
  • @RobQuist I know, the question was rethorical ;) – Vladimir Nul Jan 27 '17 at 13:05
  • Hard to see sarcasm in a bunch of letters ;) Cheers! – Rob Jan 27 '17 at 15:46
  • Google chrome does not allow multiple domains. You'll get an error in the console saying: "The 'Access-Control-Allow-Origin' header contains multiple values 'xxx, xxx, xxx', but only one is allowed. Origin 'xxx' is therefore not allowed access." – A Friend Mar 29 '18 at 08:47
  • Echoing @Jesus comment above. This does not work as a solution for multiple domains. – Louis W Jul 04 '18 at 17:55
  • @LouisW You could use the ``$_SERVER['HTTP_REFERER']`` to match and then return the proper header. Sending multiple headers might not work now. – Rob Jul 06 '18 at 12:17
34

Warning, Chrome (and other browsers) will complain that multiple ACAO headers are set if you follow some of the other answers.

The error will be something like XMLHttpRequest cannot load ____. The 'Access-Control-Allow-Origin' header contains multiple values '____, ____, ____', but only one is allowed. Origin '____' is therefore not allowed access.

Try this:

$http_origin = $_SERVER['HTTP_ORIGIN'];

$allowed_domains = array(
  'http://domain1.com',
  'http://domain2.com',
);

if (in_array($http_origin, $allowed_domains))
{  
    header("Access-Control-Allow-Origin: $http_origin");
}
6

I have fixed this problem when calling a MVC3 Controller. I added:

Response.AddHeader("Access-Control-Allow-Origin", "*"); 

before my

return Json(model, JsonRequestBehavior.AllowGet);

And also my $.ajax was complaining that it does not accept Content-type header in my ajax call, so I commented it out as I know its JSON being passed to the Action.

Hope that helps.

Neysor
  • 3,855
  • 11
  • 32
  • 65
Atif Rehman
  • 308
  • 4
  • 5
3

It's a really bad idea to use *, which leaves you wide open to cross site scripting. You basically want your own domain all of the time, scoped to your current SSL settings, and optionally additional domains. You also want them all to be sent as one header. The following will always authorize your own domain in the same SSL scope as the current page, and can optionally also include any number of additional domains. It will send them all as one header, and overwrite the previous one(s) if something else already sent them to avoid any chance of the browser grumbling about multiple access control headers being sent.

class CorsAccessControl
{
    private $allowed = array();

    /**
     * Always adds your own domain with the current ssl settings.
     */
    public function __construct()
    {
        // Add your own domain, with respect to the current SSL settings.
        $this->allowed[] = 'http'
            . ( ( array_key_exists( 'HTTPS', $_SERVER )
                && $_SERVER['HTTPS'] 
                && strtolower( $_SERVER['HTTPS'] ) !== 'off' ) 
                    ? 's' 
                    : null )
            . '://' . $_SERVER['HTTP_HOST'];
    }

    /**
     * Optionally add additional domains. Each is only added one time.
     */
    public function add($domain)
    {
        if ( !in_array( $domain, $this->allowed )
        {
            $this->allowed[] = $domain;
        }
    /**
     * Send 'em all as one header so no browsers grumble about it.
     */
    public function send()
    {
        $domains = implode( ', ', $this->allowed );
        header( 'Access-Control-Allow-Origin: ' . $domains, true ); // We want to send them all as one shot, so replace should be true here.
    }
}

Usage:

$cors = new CorsAccessControl();

// If you are only authorizing your own domain:
$cors->send();

// If you are authorizing multiple domains:
foreach ($domains as $domain)
{
    $cors->add($domain);
}
$cors->send();

You get the idea.

mopsyd
  • 1,720
  • 3
  • 19
  • 28
0

Have you tried actually adding the Access-Control-Allow-Origin header to the response sent from your server? Like, Access-Control-Allow-Origin: *?

Daniel Brockman
  • 16,714
  • 3
  • 25
  • 40
  • 1
    It is an HTTP header that your server sends to inform the browser that it is okay to reveal the result to the calling script despite the fact that the script’s origin domain does not match the server’s domain. Read up on [Cross-Origin Resource Sharing](http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing)! – Daniel Brockman Sep 27 '11 at 06:11