Similar to how I’ve had to configure build promotions twice recently, I’ve also had to set up a reverse proxy to make building JavaScript apps a bit simpler. Again, I’m blogging this at least for Future-Giles’ benefit.

The problem is that a complex JavaScript application will often call APIs running on a different server from where the JavaScript files were served. Browsers don’t like this: it can be a security hole and is prevented by same origin restrictions. Typically, once your app is deployed you can arrange your infrastructure so that the browser thinks that your JavaScript comes from your API host.

But what do you do while developing your JavaScript app?

Instead of loading your JavaScript directly from files, set up a local Apache server, arrange for this server to serve your HTML and JavaScript. Also configure this server as a reverse proxy for the API. And finally, change your JavaScript so that all API calls point at this local Apache server.

A reverse proxy is a web server configured to pretend to be another server, without clients being aware this is happening. Every request the reverse proxy receives, it will forward on to another server, and then pass back any response.

  1. Make sure you have Apache installed somewhere. Macs include Apache as part of the operating system.

  2. Create a directory apache to act as your web server root.

  3. Inside the apache directory, create directories conf,logs and wwwroot.

  4. In your apache/conf directory, create a file httpd.conf and paste in the following.

    LoadModule ssl_module        /usr/libexec/apache2/mod_ssl.so
    LoadModule proxy_module      /usr/libexec/apache2/mod_proxy.so
    LoadModule proxy_http_module /usr/libexec/apache2/mod_proxy_http.so
    LoadModule headers_module    /usr/libexec/apache2/mod_headers.so
    LoadModule log_config_module /usr/libexec/apache2/mod_log_config.so
    LoadModule mime_module       /usr/libexec/apache2/mod_mime.so
    LoadModule dir_module        /usr/libexec/apache2/mod_dir.so
    LoadModule alias_module      /usr/libexec/apache2/mod_alias.so
    ErrorLog "logs/error.log"
    LogLevel Debug
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"" combined
    CustomLog "logs/access.log" combined
    PidFile logs/httpd.pid
    LockFile logs/run.lock
    TypesConfig conf/mime.types 
    Listen 8888
    ProxyRequests off
    ProxyPassMatch /api/(.*) http://example.com/$1
    SSLProxyEngine On
    DocumentRoot "wwwroot"
    DirectoryIndex index.html

    This file configures an Apache server running on port 8888. It will serve files out of the directory apache/wwwroot and log to the directory apache/logs.

    The critical line for the reverse proxy is the one that begins ProxyPassMatch. Any URL that matches the regular expression /api/(.*) will be proxied to the server http://example.com. In the case, the /api at the beginning of the URL path will be removed. That is a request to http://localhost:8888/api/search/criteria will be proxied to http://example.com/search/criteria.

    You will need to adjust this line to match your API set up. If necessary, you can have a number of lines.

    It’s worth reading the mod_proxy documentation to see what can be done with this. It’s pretty powerful.

  5. Copy the system installed mime.types file into your Apache set-up:

    cp /etc/apache2/mime.types apache/conf/
  6. To start the Apache server, add a task to your Rakefile.

    task :apache do
      puts "Starting Apache on port 8888"
      exec %Q{httpd -d apache -f conf/httpd.conf -e DEBUG -DNO_DETACH -DFOREGROUND}

    This will start the Apache server, and leave it running in the foreground. To exit it, press Contrl-C.

  7. Change the AJAX URLs in your JavaScript code to point at http://localhost:8888/.

  8. Copy all your JavaScript, HTML and other static assets into your apache/wwwroot directory.

  9. Start apache: rake apache

And that’s it, if you browse to http://localhost:8888 you should now see your app running, with it able to call your APIs.

This is the simplest thing that will work. Typically, there is a lot of refinement I apply to this setup. In particular, the Apache configuration is usually generated from a template to make it easy to switch between different environments, and the static assets are copied into the apache/wwwroot directory by the build system when Apache is started.

Of course, there are two other solutions to the same-origin policy problem: CORS and JSONP. The disadvantage to both of these is that they each require changes on the API servers, and if you’re only having this issue in development it’s probably better to go for a development-side solution like this.