May 16, 2014
4 min read time

Getting virtual hosts right with Varnish Cache.

When answering questions in the forums, the mailing lists or our support, one of the most common topics to come up is virtual host. Virtual hosts are tricky and with Varnish and Apache/Nginx it is common to misconfigure it. Here I’ll explain how they actually work, how to verify it is working and how to set it up.

The first thing you need to understand about virtual hosts on the web is how the web server separates the different servers from each other. It uses the “Host” HTTP header and nothing else. The browser sends a host header that reflects what’s in the URL field in your browser. The server looks at the Host header and picks the right directory. That's it.

The IP address the browser connects to is determined by what the host points to. However, what the IP address points to is not related to the way it is set up. Not at all. You can set your webserver to handle incoming request to google.com and if the guys at Google should by accident happen to misconfigure their DNS and they would happen to enter your IP address your server will actually respond to the requests. At least until it is run into the ground by the amount of traffic.

So, lets start up by setting up virtual hosts in Apache. I’m doing this on Ubuntu 13.10 but it is hopefully not too different on your system. First we need to make apache listen to port 8080 instead of 80.

In /etc/apache2/ there is a file called ports.conf. Here I replaced

Listen 80

with

Listen 8080 

Notice my choice of port. I will refer to port 8080 in just a minute. If you don't have a ports.conf you can do the change in the main apache configuration instead. It should work just as well.

Next phase is to set up a couple of virtual hosts. Ubuntu has a directory for adding virtual hosts. So, I add a file into /etc/apache2/hosts-available and symlink it into /etc/apache2/hosts-enabled. The file I made contains two virtual hosts and looks like this:

<VirtualHost *:8080>
  Servername foo.com
  DocumentRoot /var/www/foo
  ErrorLog ${APACHE_LOG_DIR}/error.foo.log
  CustomLog ${APACHE_LOG_DIR}/access.foo.log combined
</VirtualHost>

<VirtualHost *:8080>
  Servername bar.com
  DocumentRoot /var/www/bar
  ErrorLog ${APACHE_LOG_DIR}/error.bar.log
  CustomLog ${APACHE_LOG_DIR}/access.bar.log combined
</VirtualHost>

Note how each VirtualHost starts. *:8080 actually refers to what IP address and port Apache is listening to. You can have Apache listening on multiple IP addresses and ports and you can bind a virtualhost to any one of those. In my config Apache is set to listen to every IP address the system has (*) and port 8080, hence *:8080. I save the file and restart apache. Now I need to make sure it works.  I’ve added some helpful text to index.html on each virtual host. I use curl to fire of a HTTP request. Here I need to override the built in virtual host since the names I've picked "foo.com" and "bar.com" don't really point to my server. 

$ curl -H "Host: foo.com" http://localhost:8080/index.html
This is foo
$

Success! Let's try the other:

$ curl -H "Host: bar.com" http://localhost:8080/index.html
This is bar
$ 

Yes. We have funtioning virtual hosts. Now let's bring Varnish into the mix. First a bit about how Varnish deals with virtual hosts. It's very easy. Varnish doesn't really care. Varnish accepts requests and stores the URL in memory along with the output. You don't need to set up anything for Varnish to be capable of virtual hosting. The only thing you need to take care of is making sure the requests reach the right webserver if you have more than one.  Since we have only one in this example we don't care.

In the VCL file we're using we need a backend definition that matches Apache so Varnish can connect to it and fetch content. Here is the backend definition:

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

Then you need to make sure Varnish is listening on port 80. 

$ ps ax|grep varnishd
17242 ?        Ss     0:00 /usr/sbin/varnishd -P /var/run/varnishd.pid -a :80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,256m

As you can see varnishd is running with -a :80 which means it is listening on port 80. Great. Let's try it and see if it works. We'll reuse our curl statements and have it connect to port 80 instead of 8080.

$ curl -H "Host: foo.com" http://localhost/index.html
This is foo 

Great. And the other one:

$ curl -H "Host: bar.com" http://localhost/index.html
This is bar

See. There was nothing to configure to get virtual hosts in Varnish to work. 

The nice picture is (c) 2012 Bigpresh and used under a Cc license.