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).
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.
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.
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;
}
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.
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.
There are no comments yet.
Lightrail – a solid, secure platform for insurance websites.
Find out what makes Lightrail awesome »
© 2024 nicknotfound.com