Managing and organizing Apache virtual host files
I’m in the midst of building new servers, inventorying the existing ones, and creating a migration plan to move over anything that needs to make it over. This is the perfect time to look at the big picture and assess whether everything is implemented in the best way possible.
One of the items I’m reviewing are the virtual host files, and let me tell you, they’re a mess.
The permissions are all over the place, there doesn’t seem to be a clear naming convention, some virtual host files are purely redirects, and there are so many includes containing additional rules and rewrites, that according to the error logs, they’ve started stepping on each other’s toes (“Request exceeded the limit of 10 internal redirects due to probable configuration error”).
Guidelines
After thinking about the above situation, I came up with a list of guidelines that should be followed when working with virtual host files:
- All virtual host files should live in
/etc/httpd/vhosts.d. - All virtual host files should have
644permissions and be owned byroot:root. - All virtual host files should be named according to the complete website address, e.g.,
blog.domain.com.conf. - Each website must be in its own virtual host file, e.g.,
blog.domain.com.conf,cloud.domain.com.conf. - A virtual host file may contain more than one
VirtualHostdirective. - A
VirtualHostdirective should use an asterisk, not a specific IP address, and then be followed by one port number, e.g.,<VirtualHost *:80>,<VirtualHost *:443>. - A
VirtualHostdirective should contain anIncludedirective to include other directives, likeServerName,DocumentRoot, etc., that may be shared across additionalVirtualHostdirectives for the same website, e.g.,Include vhosts.d/includes/blog.domain.com.conf. - The file referenced in the
Includedirective should have the same name as the master virtual host file and be created in theincludesdirectory, e.g.,vhosts.d/includes/blog.domain.com.conf. - The first virtual host file should be called
0.confand contain all global rules and rewrites, and a defaultVirtualHostdirective that includes a file in theincludesdirectory calleddefault.conf. - Files that are synchronized between multiple servers, like IP whitelists, may be placed within the
shareddirectory. - Other supporting files, like rewrite maps, may be placed within the
otherdirectory. - There should be a
.templatefile for a basic website in thevhosts.dandincludesdirectory that can be copied and used as a starting point when creating a virtual host for a new website.
File and Folder Structure
To help reiterate the above guidelines, here is a sample file and folder structure of everything that you might find in the vhosts.d directory:
+-- vhosts.d/
|-- .template
|–- 0.conf
|–- blog.domain.com.conf
|–- cloud.domain.com.conf
+-- includes/
| |-- .template
| |-- blog.domain.com.conf
| |-- cloud.domain.com.conf
| |-- default.conf
| |-- some-other-domain.com.conf
+-- other/
| |-- rewrite_map_master
+-- shared/
| |-- ip_whitelist_error_master
| |-- ip_whitelist_redirect_master
|–- some-other-domain.com.confTemplate Files
The template files serve as a consistent base when creating a virtual host for a new website. While one could manually copy and rename both template files, and then edit them to match the website, I actually use these files within a custom script I created to automatically copy and replace the values. Then, if needed, I can go back and add anything else that’s specific to the website.
Sample of the master virtual host file:
# .template
<VirtualHost *:80>
Include vhosts.d/includes/FULL_DOMAIN.conf
</VirtualHost>Sample of the virtual host include file:
# includes/.template
ServerName FULL_DOMAIN
ServerAlias www.FULL_DOMAIN
DocumentRoot /var/www/domains/ROOT_DOMAIN/SUB_DOMAIN/htdocs
ErrorLog "|/usr/local/sbin/cronolog -l /var/www/domains/ROOT_DOMAIN/SUB_DOMAIN/logs/error_log /var/www/domains/ROOT_DOMAIN/SUB_DOMAIN/logs/%Y/%m/%Y-%m-%d-error_log"
CustomLog "|/usr/local/sbin/cronolog -l /var/www/domains/ROOT_DOMAIN/SUB_DOMAIN/logs/access_log /var/www/domains/ROOT_DOMAIN/SUB_DOMAIN/logs/%Y/%m/%Y-%m-%d-access_log" combined
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.FULL_DOMAIN$ [NC]
RewriteRule ^/(.*)$ http://FULL_DOMAIN/$1 [R=301,L]Global Rules and Rewrites
The first virtual host file, 0.conf, and its include file, default.conf, are for rules and rewrites that need to either apply to all virtual hosts, or act as a catch-all for domains that do not have a virtual host file.
Sample of the default master virtual host file:
# includes/default.conf
ServerName server-name.domain.com
RewriteEngine On
RewriteMap redirect_map_master txt:vhosts.d/other/redirect_map_master
RewriteCond %{HTTP_HOST} ^(www\.)?(.+)
RewriteCond ${redirect_map_master:%2|domain.com} ^(.+)$ [NC]
RewriteRule ^/$ http://%1 [L,R=301]Conclusion
By setting up a few rules and guidelines, and writing proper documentation to outline them, it becomes much easier to manage and overview all of the virtual host files, global rules and redirects.
I hope you find this useful as a starting point, and if you have any questions, issues or recommendations with regard to the approach above, I’d love to hear them in the comments below.
Featured image by Kelly Sikkema.
Comments (2)
Previously posted in WordPress and transferred to Ghost.
Alistair Buxton
February 3, 2017 at 1:49 pm
Not bad, but there are a couple of problems with this method:
1. If you have a lot of domains and subdomains, the directory listing will be all mixed up, which makes it hard to see what is what.
2. If your top level domain has “ServerAlias *.example.com” inexample.com.conf, then only subdomains which are alphabetically before the TLD will work. eg:aaa.example.com.confwill work,www.example.com.confwill be shadowed.
The first problem can be solved by using reverse domain name notation, egcom.example.conf. The second one doesn’t seem to have a nice solution other than adding extra stuff to the filename to make it sort properly.
Ryan Sechrest
February 3, 2017 at 2:58 pm
Hi Alex,
For one, I would just run a grep to see everything for a domain. That said, more often than not, I go to manage a specific virtual host. I don’t use this to see what’s setup on the server.
For two, you are right about that. I haven’t run into this issue, because I’m always very explicit in my aliases. Of course that doesn’t work when you’re utilizing subdomains on the fly within your application.
I’ve been following the strategy outlined above for quite some time and it has worked out great, but if you need more granular control, you could put that project’s virtual host files in their own directory and custom order them to meet your needs.