Configuring a reverse proxy for JavaScript apps
gga
#2012-04-26
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.
-
Make sure you have Apache installed somewhere. Macs include Apache as part of the operating system.
-
Create a directory
apache
to act as your web server root. -
Inside the
apache
directory, create directoriesconf
,logs
andwwwroot
. -
In your
apache/conf
directory, create a filehttpd.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 directoryapache/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 serverhttp://example.com
. In the case, the/api
at the beginning of the URL path will be removed. That is a request tohttp://localhost:8888/api/search/criteria
will be proxied tohttp://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. -
Copy the system installed
mime.types
file into your Apache set-up:cp /etc/apache2/mime.types apache/conf/
-
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} end
This will start the Apache server, and leave it running in the foreground. To exit it, press Contrl-C.
-
Change the AJAX URLs in your JavaScript code to point at
http://localhost:8888/
. -
Copy all your JavaScript, HTML and other static assets into your
apache/wwwroot
directory. -
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.