Nick not found

Using xip.io for Local and Staging Environments

Mar 25 2013

One of the Rails apps I work on is at its core a multi-site content management system. I have the need to test locally and in a staging environment using production data. Using curl -H "Host: www.example.com only gets you so far, and when you need to test in a browser, that's not going to cut it.

For years I've been experimenting with different methods to accomplish this, almost all of which involved editing my /etc/hosts file. The first and most straightforward (but most error prone) way was just adding an entry to point website requests to localhost:

127.0.0.1       www.example.com

This is a quick and easy way to do a spot check, but it makes it impossible to compare against production, because with this setup all requests for the production site are actually going to localhost.

To solve this problem, I started appending -local to the end of the host name in the hosts file:

127.0.0.1       www.example.com-local

This has the advantage of being able to test a site locally and in production simultaneously, making it easy to compare the two.

This method also requires changes to either the web server or to your app. In my case, I added the following to my nginx config to strip off the -local suffix so that the app isn't aware of it:

set $real_host  $host;
# strip '-local' from the host
if ($real_host ~ (.*)-local$) {
  set $real_host  $1;
}
# pass the modified host to the app
proxy_set_header  Host  $real_host;

The major disadvantage is that it requires an entry in the hosts file for each site, which is not a big deal unless you need to test lots of them (as I do).


Using xip.io to Get the Best of Both Worlds

I later realized that I could add xip.io to the mix and avoid modifications to my hosts file altogether. This also requires a slight modification to the web server config or to the app.

I prefer to modify the nginx config so that the app doesn't have to behave differently in different environments. The nginx config is similar to what I did before:

set $real_host  $host;
# strip '.1.2.3.4.xip.io' from the host
if ($real_host ~ '(.*)(\.\d+){4}\.xip\.io$') {
  set $real_host  $1;
}
# pass the modified host to the app
proxy_set_header  Host  $real_host;

With these few lines of configuration, I can now make requests to any site on my CMS app, and I can point to different environments by simply changing the IP address in the xip.io URL. For example, here's the URL for testing www.example.com locally:

http://www.example.com.127.0.0.1.xip.io

xip.io resolves this to the IP address 127.0.0.1, and nginx strips .127.0.0.1.xip.io from the $host, passing just www.example.com to the Rails app. Presto.


Modifying the Rails App Instead

If you aren't running a web server in front of your local app server, or if you have the need to handle these requests intelligently in your app, you can add the following to your ApplicationController:

def request_host
  @request_host ||= request.host.to_s.sub(/(\.\d+){4}\.xip\.io$/, '')
end

Then simply replace references to request.host with request_host.

One advantage to this is that redirects will work as expected. Specifically, if the Rails app thinks the host is www.example.com and the app issues a redirect to a site-relative path like /login, you'll actually be redirected to www.example.com/login instead of to www.example.com.127.0.0.1.xip.io/login because Rails includes the host in the redirect URL.


Combining the Two Approaches

In my setup, I use both approaches in conjunction with each other. I alter the host in nginx so that static files can be served for each site, but I pass the original, unmodified $host to the Rails app so that redirects and such work properly.

To accomplish this, I use the request_host method shown above with this (simplified) nginx configuration:

set $real_host  $host;
# strip '.1.2.3.4.xip.io' from the host
if ($real_host ~ '(.*)(\.\d+){4}\.xip\.io$') {
  set $real_host  $1;
}
# pass the unmodified host
proxy_set_header  Host  $host;

location / {
  try_files  /${real_host}${uri}  @app;
}

Understand What's Going On

When making changes to something as core as modifying the request host, it's important to fully understand exactly what's being changed and where. If you use this technique for your app, be sure to experiment with it to make sure it meets your needs and functions the way you expect.

And a Quick Thank You

I'd also like to thank Sam Stephenson and 37signals for making xip.io available (and for free!). Obviously without it this technique wouldn't be possible.

Article Comments

There are no comments yet.

Be the First to Leave a Comment * indicates a required field


(Do not fill out this field, or your comment will be ignored. This field is here to help us protect against automated comments.

Your name, URL, and comment will appear on this page after I have reviewed it. (I do this to prevent spam). Some Markdown is allowed. I may make formatting adjustments to your comment. Your email address will not be published.

RSS

« Back to all articles

More articles about:


Lightrail Online Platform

Lightrail – a solid, secure platform for insurance websites.

Find out what makes Lightrail awesome »


A little bit about me

© 2016 nicknotfound.com