Set up Automatic Virtual Hosts with Nginx and Apache

When starting new applications, a developer often needs to set up a new virtual host. This includes setting up new configuration files for Apache or new Nginx site entries. Sometimes you need to do this manually. Other times, it’s handled automatically if you just define a site-to-folder mapping. But wouldn’t it be practical if we could skip that step, too, and just have any URL ending in, for example, .local.com look for its files automatically, so all we need to do is change our etc/hosts file only onceever? Whoa!

In this tutorial, we’ll set up Nginx and Apache to automatically look inside certain folders when it detects a given URL format. For example, the URL mynewsite.local.com will automatically look inside the DOC_ROOT/nynewsite/public folder for the index.php file. You will then be able to define other patterns suitable for generic scripts, standard MVC apps (public/index.php) and Symfony/Silex based apps (web/app.php) – all depending on the URL format (e.g. mynewsite.sf2local.com could look inside web/ for app.php, as is typical of those frameworks).

This tutorial will be Unix-centric, but is Windows-friendly if you use our Homestead Improved box.

Before we begin, make sure you have something.local.com in your /etc/hosts file, so that we can test this URL as we go along. If desired, modify it to your liking. I’ll be focusing on the local.com suffix for triggering Vhost lookups inside the something folder in our document root, but this can be literally anything that’s valid in a URL charset.

Setting up for Nginx

Assuming you have an Nginx setup running, either via Homestead Improved or any other means, let’s create a new entry in the folder Nginx typically uses to read site configurations from: sites-available. In a standard installation, Nginx will include everything from the folder sites-enabled in its configuration. Putting the file we’re about to write into sites-available will require us to activate it later.

vim /etc/nginx/sites-available/wildcard.conf

We first define a server block and tell it to listen on port 80:

server {
listen 80;
}

Everything else we write will be put inside the server block, below the listen statement.

server_name ~^(www\.)?(?<sname>.+?).local.com$;

This right here is the most important part. It tells Nginx “The server name will come in the form of this regular expression. Pull out the sname bit, save it for later”.

root /home/vagrant/Code/$sname;

This tells Nginx “The root folder of this web application is in this location”. Since it appends sname from the above regex, the path becomes dynamic, corresponding to the URL. If you’re not running a virtual machine, or have a differently set up root folder in general, feel free to change this – just make sure it ends in $sname;. If your apps typically have their front controller in the public folder, feel free to append public so the root statement looks like this:

root /home/vagrant/Code/$sname/public;

This approach is what I’ll be using in the final version of the file.

The rest of the configuration can stay the same as usual. That’s more or less it, unless you would also like to have dynamic error logs, in which case the error log entries in the file should look like this:

It’s possible that your Nginx installation is set up differently, so you might need to look around for site configuration files. It’s also possible that all your Nginx configuration is in a single file, in which case just add the above configuration to it – that might also be a file called default in your sites-available folder. Doesn’t matter where this configuration is, as long as it’s loaded – see configuration auto-include locations by peeking inside etc/nginx/nginx.conf.

Finally, we need to enable the new configuration by creating a symlink in the sites-enabled folder:

If you now visit something.local.com in your browser, the files from (in my case) vagrant/Code/something/public will be loaded.

Setting up for Apache

In this section, I’ll assume you have a running Apache instance that serves your PHP sites as usual. This could be via PuPHPet or with your friendly neighborhood *AMP stack, irrelevant – as long as it works.

First, we need to make sure mod_vhost_alias is enabled. This is the Apache mod used to give us the functionality we need. There are two ways to do this. In more streamlined installations, the command a2enmod will be available in the terminal, and all it takes is sudo a2enmod vhost_alias. In others, again due to the curse of Linux, you’ll need to look inside the main configuration file, either /etc/apache2/apache2.conf or /etc/apache2/httpd.conf or the like. If this is your case, try and find a line containing mod_vhost_alias. If it’s not there, add the following line:

LoadModule vhost_alias_module modules/mod_vhost_alias.so

Note that the path at the end needs to match the location of Apache modules on your system. This also varies due to the curse of Linux, but is often in /etc/apache2/mods-available.

Just like Nginx, Apache usually loads its Vhost configurations from /etc/apache2/sites-available and by extension /etc/apache2/sites-enabled. It usually also comes with a module for easy enabling and disabling of sites, like so: sudo a2ensite mysite.conf. This is, for all intents and purposes, identical to the Nginx section above on creating a symlink to enable a new configuration. It loads these alphabetically, so we’ll make a new configuration file in sites-enabled (or do it in sites-available and enable the site subsequently) called 000-default.conf.

The first statement we put in there will be:

UseCanonicalName Off

This is required so that we can dynamically assign aliases to the URLs Apache receives. Then, we define the VirtualHost block:

The %1 means “Take the first part of the dot-separated domain name”. For other options, and if you wish to configure it differently, see the docs.

If we now put the folder something into /var/www and inside it index.php with some Hello World content, it should all work. But in this case, it’ll output the contents of the PHP file as text. We need to configure the rest of the VirtualHost’s parameters as usual. I’ll paste my PuPHPet version below – your configuration may vary.

Now, if you reload Apache configuration with sudo service apache2 restart or whichever command works on your distribution, you should be able to access something.local.com in your browser and once again see it working if you put some PHP files into var/www/something/public.

Optional: dnsmasq

Dnsmasq is a tool which, among other things, forwards a range of URLs to a given IP. It’s like regex support for /etc/hosts. With this tool, you can set all URLs ending in .local.com or whatever else you choose to redirect to your virtual machine’s IP or to your localhost. This means you never have to edit your hosts file again – whenever you start a new project, your URLs and Vhosts can be assumed ready and you can start working right away, minimizing devops dramatically.

If you want to further automate this, you can add dnsmasq as a Vagrant plugin.

To set up dnsmasq to forward a URL pattern to your IP, and to set up your OS to forward all URL calls to dnsmasq so it can actually do this, see this excellent guide.

Conclusion

In this tutorial, we’ve learned how to automate that part of devops we all regularly deal with – setting up new Vhosts and their domains. It’s only a couple of minutes on every project, especially with packages like Homestead Improved, but it’s still a couple of minutes – and being able to quickly switch into another mock project for testing that one script you just thought of or need to try out is priceless for one’s workflow.

Did this tutorial help you? Would you have liked to get more information on certain topics? Let us know!

Bruno is a blockchain developer and code auditor from Croatia with Master’s Degrees in Computer Science and English Language and Literature. He's been a web developer for 10 years until JavaScript drove him away. He now runs a cryptocurrency business at Bitfalls.com via which he makes blockchain tech approachable to the masses, and runs Coinvendor, an on-boarding platform for people to easily buy cryptocurrency. He’s also a developer evangelist for Diffbot.com, a San Francisco-based AI-powered machine vision web scraper. Read more about his background with the blockchain here.