AJAX Cross Domain Proxy

Under Work on August 02nd, 2007 with 56 comments

Update – May 2012 Code has been moved to Github as PHP Cross Domain Proxy

Update – Sep 2011 I have updated the AJAX Cross Domain Proxy script according to the latest comments and suggestions. Please note that by default the script accepts only full URLs. However, you can change this by switching CSAJAX_FILTER_DOMAIN to false. This will allow you to list domains (instead of long URLs) in $valid_requests. Also, during development you can take advantage of the CSAJAX_DEBUG which by default is set to false. Switch it to true to receive debugging messages; do not forget to switch it back to false on production.

It is well known that cross domain AJAX requests (XMLHTTPRequest) are not permitted due to security reasons. Numerous workarounds exist such as cross domain JSON and Flash but some of them are not suitable for every single case. For instance, cross domain JSON assumes that remote server is able not only to serve JSON but to include a call to the specified function (the callback function) as well. On the other hand, Flash method assumes that… well, that Flash is enabled!

An interesting approach is presented by Cameron Adams in his great article Go forth and API. Cameron suggests to take advantage of mod_rewrite or mod_proxy module in Apache in order to redirect our calls in external domains; a simple but ingenious solution! However, the most common solution is the application proxy which is accompanied by some advantages outlined perfectly well by Jonathan Snook:

[...] you have more control over the entire lifecycle. You can parse the data from the remote server, do with it what you will before sending it back to the client. If anything fails along the way, you can handle it in your own way. And lastly, you can log all remote calls. With that you can track success, failure and popularity.

Cross Domain Ajax: a Quick Summary

Lately, I have developed an application proxy in PHP which I decided to publish. You can have a look at the demo and of course download it.

How it works? All you have to do is to place the corresponding file in your web server. Whenever you want to make a cross domain request, just make a request to http://www.yourdomain.com/ajax-proxy.php and specify the cross domain URL in parameter csurl. Obviously, you can add more parameters according to your needs; note that the rest of the parameters will be used for the cross domain request. For example, if you are using jQuery:

$('#target').load(
	'http://www.yourdomain.com/ajax-proxy.php', {
		csurl: 'http://www.cross-domain.com/',
		param1: value1, param2: value2
	}
);

It’s worth mentioning that both POST and GET methods work, while headers were taken into consideration. That is to say, headers sent from browser to proxy are used for the cross domain request and vice versa. Finally, for security reasons you will need to define all the valid requests into the ajax-proxy.php file:

$valid_requests = array(
  'http://www.domainA.com/',
  'http://www.domainB.com/path-to-services/service-a'
);

Please note that the script is released under a CC-GNU GPL.

 


Comments (56)

Leave your comment ↓

  1. Howard Katz:

    I’ve been looking for something like this. I updated uploaded you $valid_requests array with the websites referenced in your demo site and uploaded the php code to my website, along with your html that references the php file.

    Unfortunately I couldn’t get it working. I know jquery is working, because once I click on one of the links, the #response div reports a “‘Loading! Please wait…” message that shortly disappears, to be replaced by … nothing!

    I’m new to php so I’m not sure what I could return (if anything) from ajax-proxy.php to help to debug the problem. Any suggestions? Since I’m a nubie, being as concrete as possible would be helpful!

    Thanks,
    Howard

  2. Iacovos: Author comment

    It seems that the ajax-proxy exits without making any request at all. What I forget to mention above is that you have to specify the exact URLs in $valid_requests array; not just the domains. For instance, if you are making requests to http://example.com/service, then neither http://www.example.com/service or http://example.com would work.

    Hope this solves your problem! If the problem persists you can always access directly the ajax-proxy via your browser i.e. http://www.yourdomain.com/ajax-proxy.php?csurl=http://www.iacons.net/feed/ and check the actual output.

    Thank you for stopping by and leaving your feedback!

  3. Richard:

    why would i get this error when i load the proxy via web browser? I am sure I properly initiated the csurl param

    Bad Request (Invalid Header Name)

  4. Richard:

    I removed this line if code and now it works.
    //curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);

    I will tell you I looked at the headers and it seems there were no header names and a bunch of 0′s for values.

  5. Iacovos: Author comment

    Thank you Richard for your feedback. The line of code that you have erased copies the headers sent by your browser into the request send by the ajax-proxy to your cross-domain application. Is it possible to let me know both your browser (name, version, os) and your cross-domain application in order to debug it?

  6. sanjay:

    i am not getting any response nor any error after adding the domains to the array list on php file.. Could you please help!!

  7. kevin:

    it’s a pity, though really useful, there is no demo proxy developed in asp , for which i am dying . still thanks a lot.

  8. fedmich:

    great…

    useful but the curl on my site isn’t enabled… so Im using other means of retrieving websites like fopen()

    for asp code, I had something like this before, just dont remember on which website did I used this for

    Cheers and Happy New Year

  9. Iacovos: Author comment

    fedmich, did you modify the ajax-proxy.php to work with fopen etc? If yes, it would be nice if you could provide me your changes, integrate them into the current script and finally release an updated version that works with both approaches!

    Happy New Year!

  10. Al:

    I get… Fatal error: Call to undefined function curl_init() in C:\root\www\crosssdomainajax\ajax-proxy.php on line 67

  11. Iacovos: Author comment

    Al, you need to enable curl module in PHP.

  12. ONi:

    Iacovos: Sir, you are a genious!! this is exactly what I was looking for and works like charm. I made a little modification though, to the issue in the comment #2, I added $_GET['csurl'] to the list of $valid_requests, so on, any URL that you try to get will be a valid request, hope this works for everyone!.

  13. Iacovos: Author comment

    ONi, thanks a lot. Regarding your little modification, that was not necessary. You could simply set the value of CSAJAX_FILTERS to false (line #13). However, you (not just you, but everyone) must understand the security issue behind the filtering option. By disabling the filtering option, the ajax-proxy script can serve as an open, proxy script and anyone could use it to request any page. I am not going further and analyze how someone could take advantage of it; if anyone has more questions, feel free to contact me.

  14. Silverlight Networking - Using a proxy to overcome cross-domain-scripting troubles » Mark Monster:

    [...] tested this proxy, written in PHP. It works for me because I’ve got a Webhost that supports PHP and no ASP.NET. I’ve [...]

  15. Lars:

    Hi Iacovos,

    Thanks for this handy example.
    I am using it in my prototype web app (a gmaps mashup).

    Re: 13, it seems like it would be useful for $valid_requests to be an array of domains, or hostnames, or URL prefixes, or match patterns, so that they’re more general than exact URLs.
    Is there much security risk in just specifying the hosts (or domains) that can be proxied to?

    Lars

  16. phil:

    There’s some bugs in your code…

    When posting it fails line 77 should be:
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_params));

    If csurl is giving as a GET variable, but the data is posted it fails at line 39. Changing it to:
    $request_url = urldecode($_REQUEST['csurl']);
    is the quickest way to fix it.

    Other than than nice work :)

  17. Joran:

    JSONP and Flash have their disadvantages. Flash also limits the request headers. But with the proxy approach the client has to make a request to the proxy which then has to make a request to the API, which then responds to the proxy which then responds to the client. In other words, the approach incurs double the latency and double the bandwidth on the system as a whole. In other words, it’s much slower than JSONP and Flash.

  18. Beautifying York Lists with AJAX — Breakz < drum & bass . breakz . hip-hop > Alternative music in York:

    [...] proxy, which takes a local request and redirects it to an external domain. The one I used is the PHP AJAX proxy by Iacovos [...]

  19. Ryan Underdown:

    Thanks Iacovos (and Phil, line 77 change got post working for me as well.) Was struggling with cross domain $.ajax for a day or so before I ran across this.

  20. a3cube:

    This is great but I think you should update the code to Phil’s suggestion on line 77 should be:
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_params));

    I ran into the same problem until I saw he’s comment which resolve everything

  21. Iacovos: Author comment

    Thank you guys for your comments and suggestions. I will get into the code and make the appropriate changes ASAP.

  22. a3cube:

    I ran into some problem when I was working with $_POST. Actually, I changed

    $request_params = $request_method == ‘GET’ ) ? $_GET : $_POST;

    to

    $request_params = $_REQUEST;

    but I noticed (via var_dump()) that something like action=true&register=1 will end you with action=true&register=1 which will make it hard for you to get $_REQUEST['register'].

    so I change

    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_params));

    to

    curl_setopt($ch, CURLOPT_POSTFIELDS, $request_params);

    I hope this would help someone.

  23. a3cube:

    [quote comment="129654"]but I noticed (via var_dump()) that something like action=true&register=1 will end you with action=true& amp;register=1 which will make it hard for you to get $_REQUEST['register']. [/quote]

  24. Iacovos: Author comment

    a3cube thank you for your valuable feedback.

    As far as I understood you are trying to perform a GET request to the AJAX Cross Domain Proxy which eventually will make a POST request? If this is the case, I am afraid this is beyond the functionality of this script. Try issuing a POST request to the proxy and this will transfer your post request to the final target.

    P.S. The script has been updated according to the latest comments.

  25. a3cube:

    No it’s a post request via jQuery

    $.ajax({
    type: “POST”,
    url: “ajaxproxy)?URL_AJAXPROXY:”).URL_REMOTE ; ?>”,
    data: unescape(“action=register_user&register=1″),
    success: function(response){
    $(“#reg_process”).hide();
    $(‘#rsp_display’).html(response);
    $(‘#rsp_display’).show();
    },
    error:function(xhr,err,e){
    alert( “Error: ” + err );
    }
    });

    My last comment dis not work on chrome and Opera. So I made a change to it by

    curl_setopt($ch, CURLOPT_POSTFIELDS, html_entity_decode(http_build_query($request_params)));

    this would convert & amp; to &. The reason for this is that http_build_query() encode html enities like & to & amp;

    I hope this help.

  26. Kyle Simpson:

    I have a project called flXHR http://flxhr.flensed.com which is a javascript+flash solution for cross-domain Ajax, but goes one step further: flXHR implements an identical API to the native XHR object, which means it can be dropped into any existing project with almost no code changes necessary, and you automagically will get cross-domain Ajax capability.

  27. Matias:

    Hi Iacovos,

    Great work!

    I downloaded the example and work seamless. However, I need to request the following URL and it doesn’t work:

    http://xmlfeed.laterooms.com/index.aspx?aid=1000&rtype=7&hids=73737&sDate=2010-03-28&nights=4

    I specified the whole domain and also the domain with page and parameters in ajax-proxy.php. None of the options seem valid

    $valid_requests = array(
    ‘http://xmlfeed.laterooms.com/’,
    ‘http://xmlfeed.laterooms.com/index.aspx?aid=1000&rtype=7&hids=73737&sDate=2010-03-28&nights=4′
    );

    Appreciate your help!

  28. Gustav:

    Hello Jacobus, please remove the counter of the download link, it doesn’t works.

    coz stats.iacons.net is down
    link directly at:

    http://lab.iacons.net/ajax-proxy/ajax-proxy.phps

    Thanks Jacobus, your code will save me hours and days of work.

    Gustav
    micro ISV

  29. ahmed:

    Please , I want to download the file but the link is currupted.

  30. Iacovos: Author comment

    Thank you ahmed, the link has been updated.

  31. rudraksha:

    I ran into the same problem until I saw phil’s comment which resolve everything

  32. Paul Albinson:

    Many thanks for the code it got me out of a awkward situtation where I needed to use a script from a different sub domain via AJAX.

    A few thoughts/fixes though:

    If you wish to make the system restrict to domains/sub domains rather than having to specify every single url you can change

    if ( !in_array($check_url, $valid_requests) ) {

    to

    if ( !in_array($parsed['host'], $valid_requests) ) {

    you can then use the valid_requests array like so:

    $valid_requests = array(
    ‘a.example.com’,
    ‘b.example.com’,
    ‘anotherdomain.com’
    );

    Also:

    I had the same problem as Richard mentioned in comment 3 and like him found that removing the following line solved it:

    curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);

    It seems that it doesn’t actually get all the headers we need and thus breaks it. Seems to work perfectly well without it. I see the point of using it to include the original headers but as it doesn’t actually work then it is surpless to requirment unless it is fixed.

    Also the comment made by a3cube (comment 22)

    “problem when I was working with $_POST. Actually, I changed

    $request_params = $request_method == ‘GET’ ) ? $_GET : $_POST;

    to

    $request_params = $_REQUEST;”

    was really useful as I had my jQuery AJAX using post but had the url I wanted in a get url. Alternatively I guess you could put the url in the post as well and thus can only deal with post requests.

  33. Paul Albinson:

    Another issue I found is that using the proxy can mess up any other ajax script you have on the page as in my situation it stopped the other scripts seeing the Zend Framework Authentication which was a major issue for the site as it stopped the other features working. The solution in my situtation was to remove the headers from the response, mad I agree but fixed my particular issue.

  34. Emilio Alvarez:

    Thanks for your work. We try your ajax cross domain proxy and works perfect.

    Thanks.

  35. Lugdum:

    I’m trying to use it to sent an xml request to a web service.
    The content type is of my request is text/xml but I have an error : Unsupported Content-Type: application/x-www-form-urlencoded Supported ones are: [text/xml]
    Any idea ?

  36. Brett:

    Great script to start from. I am now using a slightly modified version of the code for a site I am working on, and am very appreciative for your work.

    One thing I did run into was that if a response contained more than one cookie, only one of the cookies would be propagated back thru the proxy.

    I changed:
    header($response_header);
    to
    header($response_header, FALSE);
    which fixed the issue.

  37. Brett:

    Found what looks like another bug. When copying the request headers, I had to change another line of code.

    I changed:
    $request_headers[$headername] = $value;
    to
    $request_headers[] = “$headername: $value”;

  38. Brett:

    Due to the bug fix in #37, headers are now correctly being propagated. However, we don’t want to propagate the “Host” header, b/c the Host value of your proxy server is probably different than the Host value for the URL you are forwarding to. Thus, I simply changed the code to:

    if ($headername != ‘Host’) {
    $request_headers[] = “$headername: $value”;
    }

  39. EdisonCode:

    Thank you very much for this code. I ended up modifying it quite a bit to post json data to a .net web service and return the resulting payload to a jQuery application. My AJAX requests is similar to:

    $.ajax({
    type: “POST”

    data: ‘{ “json” : “data”, … }’;

    });

    The main change that I’d like to pass on for others trying to accomplish something similar with this excellent proxy code is to change:

    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_params));

    to

    curl_setopt($ch, CURLOPT_POSTFIELDS, $HTTP_RAW_POST_DATA);

    I also found it useful to completely wipe out the header data and send the only http header that I needed by doing:

    $headerData = array();
    array_push($headerData, ‘Content-Type: application/json;’);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headerData);

    Hope this helps others and thanks again for a great starting place.

  40. Iacovos Constantinou: Author comment

    I have updated the AJAX Cross Domain Proxy script according to the latest comments and suggestions. Thank you all for your feedback.

  41. Hadrian:

    Hi!

    Good job!
    The change I’ve made for json retrieving it’s so simple like that : use the htmlspecialchars_decode function with the response_content.

    Somebody know a plugin for symfony to do that?

  42. Ajax call from external domain « Codeperl's Knowledge Sharing System:

    [...] http://iacons.net/2007/08/02/ajax-cross-domain-proxy/Like this:LikeBe the first to like this post.   [...]

  43. Rudraksha:

    reat script to start from.

  44. Rudraksha:

    Rudraksha-rudraksha.com supplies Rudraksha Rudraksh beads, rudraksa mala,rudrakshas pendants, yantras, rudraksh wrist bands, rudraksa bracelets, sphatik beads, Nepal rudraksha, 1 face to 21 Mukhi rudraksha, puja items, rudraksha collection, Very low price ct 09849738764

  45. Rudraksha:

    Rudraksha-india.com offer Rudraksha, Rudraksha Beads, Nepal Rudraksha One of the best Rudraksha beadsDealers, Rudraksha Suppliers and Wholesales Rudraksh India. We deal with Nepali 1 to 21 face variety ct 09849738764

  46. wooden Kitchens:

    Hmm is anyone else having problems with the pictures on this blog loading?

    I’m trying to find out if its a problem on my end or if it’s the blog.
    Any suggestions would be greatly appreciated.

  47. http://rosariobenx.wordpress.com/2013/06/18/painoindeksi-katsominen-onnistuu-nopeasti-nettisivulta-loytyvalla-lomakkeella/:

    I loved as much as you will receive carried out right
    here. The sketch is tasteful, your authored subject matter stylish.
    nonetheless, you command get bought an impatience over that you
    wish be delivering the following. unwell unquestionably come further formerly
    again since exactly the same nearly very often inside case you shield this increase.

  48. bathroom shower:

    It’s a pity you don’t have a donate button! I’d certainly donate to
    this superb blog! I guess for now i’ll settle for book-marking and adding your RSS
    feed to my Google account. I look forward to fresh updates and
    will share this blog with my Facebook group. Talk soon!

  49. job centre online:

    This site was… how do I say it? Relevant!! Finally I’ve found something
    that helped me. Thanks!

  50. Shantell:

    To recreate the 1940′s vintage appeal, frame movie posters in
    the era to hang for the walls. This area also allows single customers to take a seat to get a meal without
    waiting for the table. The conecpt behind repurposing is actually a similar; take a classic or will no longer used item and give
    it a use.

  51. Andreas:

    Hello,
    I need to make a json (NOT jsonp) cross-domain POST.
    I tried different things… – but they fail all – probably because I’m a rookie ;-)
    Does anybody know how to achieve this using the ajax-proxy.php?

    Any help welcome!
    Thanks

  52. Caitlyn:

    The Keurig B40 then puts a hole into the lid in the K-cup,
    in addition to bottom in the cup, and drives the
    h2o down in the cup. Also, in case you bought a type made completely from glass, the brewed coffee could not be confronted with other materials, that ruin its taste.
    And yes, many of these fancy machines do brew a greater
    tasting coffee.

  53. Janette:

    Even though understanding who can benefit from using an air purifier, you may also be wondering
    exactly how they work. Simply place the unit in a central location in your home and select the
    desired cleaning setting to match the size of your environment as well as fan
    speed and Fresh – Air Surround’s mold-killing vapor will arrest mold
    growth in as little as 2- 4 hours. Instead, these products seem to be nothing more
    than fancy scented oil diffusers.

  54. Imogen:

    Polymers in the cleaning product used in this process encapsulate or “crystalise” dirt particles into dry residue after which will be
    removed at the end of the process. Whether you buy OEM vacuum
    cleaner bags or after-market ones, you do not need to
    buy them straight from the company. It mainly features two
    roller brushes that can sweep the dirt on tight spaces like upholstery, kitchen and car.

  55. Kaylene:

    Different services can help you find the perfect
    place for you and advice you what to do to reach it. With school books, notebooks, computers, video games, cell phones
    (not to mention snacks and water bottles), all having become
    “must have” items. To do this I still put a large plastic bag
    in my backpack and pack everything in this.

  56. Search Engine Marketing:

    App Marketing & App Marketing Services have the strong grip on these two areas for marketing purposes.
    It can also make readers to visit your blog and your
    site regularly. It would not be wrong to say that app marketing is the foremost
    strategy that software designers must chalk
    out before the launch of a mobile app in the market.


Leave your comment

Personal details

(required)

(will not be published, required)

Your comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>