• Cross-domain XHR with Varnish

    09-07-2010Author: basdenooijer

    Cross-domain XHR (AJAX) requests can be an issue. There are workarounds like JSONP but sometimes JSONP is not available, or you need a POST request, or you have other special requirements that require usage of XHR. In that case a webproxy on your own domain is often used to resolve the crossdomain issue.

    There are many examples how to solve this inside your application, but when you just need to relay a request you can do this easily with Varnish. That way you don’t need to build anything in your application and you get a high-performance ‘proxy’.

    I’ve created a dummy service at http://www.raspberry.nl/test/ajax.php?input=helloworld to test with. It is a very simple PHP script that echo’s the GET or POST ‘input’ variable and the current timestamp:

    echo date('Y-m-d H:i:s'). ' : ';
    $input = $_REQUEST['input'];
    if (ctype_alnum($input))
        echo $input;
        echo 'Input missing or invalid!';

    You can make this service available via Varnish based on a special URL.
    First you need to define a backend:

    backend raspberrynl {
        .host = "www.raspberry.nl";

    In the vcl_recv routine the special URL is detected:

    sub vcl_recv {
        if (req.url ~ "^/ajax/raspberry")
           set req.backend = raspberrynl;
           set req.http.host = "www.raspberry.nl";
           set req.url = regsub(req.url,"^/ajax/raspberry","/test/ajax.php");

    Some important things to note:

    • The URL detection is done using a regular expression because of a possible GET query string
    • req.http.host is adjusted to the correct hostname, else the server for www.raspberry.nl would get a request for the host www.yourvarnishsite.com
    • req.url is adjusted using a regular expression replace that only replaces the fixed path, again to allow for a GET query string

    With these two parts added to you VCL file you should have a working service at www.yourvarnishsite.com/ajax/raspberry?input=helloworld
    It will support both GET and POST requests and since it’s on the same domain as your site there are no crossdomain XHR issues. You can test it by sending different values in the ‘input’ param, using either POST or GET requests, your input values should be returned in the response.

    With a small addition you could also add some caching to take some load of the service you use. This can be usefull when the backend service is slow, when you have a usage quota for the service, or pay for transactions.
    A basic example:

    sub vcl_fetch {
       if (req.backend == raspberrynl) {
         set beresp.ttl = 10s;

    In this example any headers from the service are ignored and the lifetime is fixed to 10 seconds. The timestamp in the response can be used to validate the caching. The caching only works for requests with the same ‘input’ value. You can add all kinds of complex caching rules just like you would for any other backend using standard VCL.

    , ,
  • Comments are closed.