Streamlining Web App Development With Zeroconf
The sites which are using Shardine do not only have separate data storage - they all have their own domain names. I frequently need to validate that every site is able to work correctly with the changes I am making. At Cheddar we are also using multiple domains, which is a good security practice due to CORS and CSP. Until recently I didn’t really have a good setup for developing with multiple domains, but that has changed - and the setup I ended up with works really, really well. So, let’s dive in - it could work just as well for you too!
The problem of multiple hostnames
When you have an application (let’s assume it is a Rails application, for simplicity) and you run bin/rails s
or bin/dev
the app boots and binds to localhost:127.0.0.1
. It means that it will respond on requests to your localhost IP only (regardless of which domain name is used), and it usually runs on port 3000
- which is a Rails default.
When you have multiple subdomains and you want to test them, the standard approach is editing your /etc/hosts
and adding the following segment to it:
127.0.0.1 myapp-site1
127.0.0.1 myapp-site2
127.0.0.1 myapp-site3
You then need to flush your DNS caches and change the Puma config to bind not to localhost
but to your link-local IP instead:
bind "tcp://0.0.0.0:3000"
This is so that Puma listens not only to localhost
but to any request that comes in to your link-local IP.
This approach works, but it has some disadvantages. First, you need to edit your /etc/hosts
and add (or remove) every app’s domains that you are working on. This is annoying and wasteful. Second, you can encounter problems with .local
, .home
and .example
TLDs if you use them - so you may bump into your DNS lookups taking 5 seconds
Also, the /etc/hosts
approach only makes these new names resolvable on the machine the app is running on. It is fine as long as you do not want to test your site on a mobile device, for example - a very valid use case! You want to open your app on your smartphone and examine the mobile-optimized layout, for example - as well as test the JS-heavy bits that may be slow on mobile. But just resolving to localhost will not allow your device to access your workstation where the app is running!
You can also use .lvh.me
which gives you a DNS resolution to 127.0.0.1
for anything you throw at it. This removes the need to edit /etc/hosts
but introduces another big problem. Some ISPs will actually filter out any DNS responses that you receive which resolve to 127.0.0.1
- this is called DNS rebinding protection and one of the dominant ISPs here in the Netherlands does it so heavy-handedly and badly that even if you configure your own DNS servers their responses get filtered out as well. This manifests as another severe DNS timeout when you try to access locally-bound hosts. And the only remedy for this is… adding those hosts to your /ect/hosts
.
Bah.