Want to host more than one website on your Raspberry Pi, without having to pay for multiple IP addresses? You can do this easily using Apache’s name-based VirtualHost configuration feature. This feature allows someone to connect to your Raspberry Pi (or other server) and get served different content based on the host header they sent with their request. This is automatic, and the user is none the wiser: they simply type your web address in the header, and your server uses that information to decide which website to display. Unless you tell them, they won’t know the Pi is also hosting other content.
General Rules
Out of the box, Apache2 on Raspbian has two files with VirtualHost configuration parameters inside. One is at /etc/apache2/sites-available/default
and the other is at /etc/apache2/sites-available/default-ssl
. The default file is enabled (which symlinks it to /etc/apache2/sites-enabled
) and the default-ssl is not. You can have as many or as few VHosts as you like; you can put all the VirtualHosts in the same file, or store them separately – Apache2 will work just the same either way. If you opt for the different files method, here are some useful commands. Use this one to enable a site you’ve added to /etc/apache2/sites-available/site
:
sudo a2ensite site
And use this one to disable it again:
sudo a2dissite site
Apache reads configuration files stored in /etc/apache2/sites-enabled/
in alphanumerical order. The first VirtualHost block it reads is set as the default, and is used if no host header information is sent in the request. If more than one VirtualHost is defined, then Apache matches the host header against the ServerName
and ServerAlias
directives defined in the VirtualHosts to decide which content to serve. If no host header is sent, or a match cannot be found, then the default VHost is used. You can test which is the default VHost by typing the Pi’s IP address into your browser’s address bar. If you’re confused by this, don’t worry. It’ll probably make sense when you see some examples. I’m going to cover HTTP hosts first because they’re easiest, and then move on to SSL/TLS HTTPS hosts because they have some extra bits to consider.
HTTP Hosts
Here is a typical VirtualHost block:
<VirtualHost *:80> ServerAdmin webmaster@samhobbs.co.uk ServerName www.samhobbs.co.uk:80 ServerAlias samhobbs.co.uk DocumentRoot /var/www <Directory /var/www/> Options Indexes FollowSymLinks MultiViews AllowOverride all Order allow,deny allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/samhobbs/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/samhobbs/access.log combined </VirtualHost>
- The VirtualHost line tells Apache to match any IP address on port 80
- ServerAdmin defines the email address that is sent in error messages so that users can contact you about problems
- ServerName is the fully qualified domain name of the site. You can optionally include the port, e.g. www.example.com:80 for port 80
- ServerAlias provides additional names to match against host headers when Apache is deciding on which virtual host to use.
- DocumentRoot is the path to the directory that contains all the site data. In Debian, Apache’s data directory is /var/www. If you have lots of sites, then you probably want to make a directory for each site like /var/www/site1 /var/www/site2, and specify the appropriate directory here
- Directory specific options can be set inside a directory block. Options starts the list
- Indexes means that if there is no index.html or index.php file inside the directory, Apache will create an auto formatted list of everything in the directory.
- FollowSymLinks allows Apache to follow symbolic links
- MultiViews allows content driven negotiation to take place, where the server can decide which version of a page (if more than one version exists) to send based on the client’s browser preferences
- AllowOverride all means that the global defaults for Apache can be overridden with an .htaccess file placed inside the directory.
- The “order allow,deny…” section allows you to specify IP ranges to block or enable. This one allows everyone.
- ErrorLog specifies where Apache should write the error log file for this VHost. ${APACHE_LOG_DIR} is /var/log/apache2. I like to create a new directory for each VHost like /var/log/apache2/samhobbs and then stick the log files in there.
- LogLevel specifies the severity of event you’d like to be written to the log.
- The CustomLog format combines information about access, agent and referrer into one file.
Adding More VHosts
#============================== ANTI PROXY SPAM ============================= <VirtualHost *:80> ServerName default.only <Location /> Order allow,deny Deny from all </Location> ErrorLog ${APACHE_LOG_DIR}/spam/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/spam/access.log combined </VirtualHost> #================================= WEBSITE =================================== <VirtualHost *:80> ServerAdmin webmaster@samhobbs.co.uk ServerName www.samhobbs.co.uk:80 ServerAlias samhobbs.co.uk DocumentRoot /var/www/samhobbs/ <Directory /var/www/samhobbs/> Options Indexes FollowSymLinks MultiViews AllowOverride all Order allow,deny allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/samhobbs/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/samhobbs/access.log combined </VirtualHost> #============================= SECOND WEBSITE =============================== <VirtualHost *:80> ServerAdmin webmaster@samhobbs.co.uk ServerName tomhobbs.co.uk:80 ServerAlias www.tomhobbs.co.uk DocumentRoot /var/www/tomhobbs/ <Directory /var/www/tomhobbs/> Options Indexes FollowSymLinks MultiViews AllowOverride all Order allow,deny allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/tomhobbs/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/tomhobbs/access.log combined </VirtualHost>
In the above file, there are two “real” websites (www.samhobbs.co.uk and tomhobbs.co.uk) plus that default VHost that blocks everything else. Notice that:
- Each “real” website has its own directory (e.g. /var/www/site1 and /var/www/site2. If you place one website inside another’s directory then you’ll be able to access website 2 from within website 1, e.g. www.site1.com/site2/
- Each website’s logs are written to a separate location (/var/log/apache2/site1 and /var/log/apache2/site2) for ease of use. This is also handy if you want to use something like Webalizer for analytics.
- The webmaster email address is the same for both because in both cases I’m the admin
The default.only VHost is just there to block drive-by spam and automated script attacks that hammer IP addresses at random. My public IP address is 195.166.151.235 – try pasting this into your search bar to see how the default VHost will look to anyone trying the server by its IP. This blocks more spam than you might think, and takes a bit of load off your server: no legitimate user is going to type an IP address into their browser to visit your page, so your processing power and bandwidth are saved for legitimate users. Here’s an example of a post that was blocked by this technique. I don’t know exactly what this is, maybe comment spam, but you don’t have to be a genius to realise it wasn’t good news:
173.230.149.43 - - [07/Jan/2014:15:46:32 +0000] "POST //%63%67%69%2D%62%69%6E/%70%68%70?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%6E HTTP/1.1" 403 465 "-" "-"
A note about VHost file locations
Installation instructions for some software like Squirrelmail (including my own tutorial) ask you to create symbolic links from that program’s configuration folder to Apache’s /etc/apache2/conf.d/
folder. Remember how I said that the first VirtualHost block that Apache reads is used as the default? If the symlinked configuration files contain VirtualHost blocks then they may be loaded before your default.only VirtualHost block, and become the default. So, you have two options: you can either symlink somewhere else, like /etc/apache2/sites-available/squirrelmail
and then enable them (e.g. sudo a2ensite squirrelmail
). This should allow you to control the order. Alternatively, you could add that default.only VirtualHost to the start of the squirrelmail config file, so that it is still read first.
HTTPS Hosts
Name based HTTPS hosts are a little more complicated that HTTP hosts. You can still get Apache to choose which content to serve based on the host header, but since the encrypted connection is established before this negotiation happens, every HTTPS VirtualHost must use the same SSL Certificate. The SSL cert that is defined in the first SSL/TLS VirtualHost block that is read is used for all HTTPS VirtualHosts on the server. This can lead to certificate errors in a web browser, since the Common Name on the certificate won’t match the domain name for the second site (assuming you use a cert that matches your main site as the default cert). For websites like my brother’s, that’s probably not an issue as you may not be serving any HTTPS content to normal users, i.e. only the admin backend uses SSL/TLS. Your communications will still be encrypted, but you’ll have to click through a warning since the identity of the site can’t be verified. If you don't have your own SSL certificate, you might like to generate one yourself and get it signed by CAcert Here’s an example:
<IfModule mod_ssl.c> NameVirtualHost *:443 #=============================== ANTI SPAM ================================ <VirtualHost *:443> ServerName default.only <Location /> Order allow,deny Deny from all </Location> SSLEngine on SSLCertificateFile /path/to/your/cert.crt SSLCertificateKeyFile /path/to/your/key.key </VirtualHost> #================================ WEBSITE =================================== <VirtualHost *:443> ServerAdmin webmaster@samhobbs.co.uk ServerName www.samhobbs.co.uk ServerAlias samhobbs.co.uk DocumentRoot /var/www/samhobbs <Directory /var/www/samhobbs> Options Indexes FollowSymLinks MultiViews AllowOverride all Order allow,deny allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/samhobbs/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/samhobbs/ssl_access.log combined SSLEngine on SSLCertificateFile /path/to/your/cert.crt SSLCertificateKeyFile /path/to/your/key.key <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars </FilesMatch> <Directory /usr/lib/cgi-bin> SSLOptions +StdEnvVars </Directory> BrowserMatch "MSIE [2-6]" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 # MSIE 7 and newer should be able to use keepalive BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown </VirtualHost> #============================= SECOND WEBSITE ================================ <VirtualHost *:443> ServerAdmin webmaster@samhobbs.co.uk ServerName tomhobbs.co.uk ServerAlias www.tomhobbs.co.uk DocumentRoot /var/www/tomhobbs <Directory /var/www/tomhobbs> Options Indexes FollowSymLinks MultiViews AllowOverride all Order allow,deny allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/tomhobbs/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/tomhobbs/ssl_access.log combined SSLEngine on SSLCertificateFile /path/to/your/cert.crt SSLCertificateKeyFile /path/to/your/key.key <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars </FilesMatch> <Directory /usr/lib/cgi-bin> SSLOptions +StdEnvVars </Directory> BrowserMatch "MSIE [2-6]" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 # MSIE 7 and newer should be able to use keepalive BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown </VirtualHost> </IfModule>
Some additional things worth noting:
NameVirtualHost *:443
is required before your first HTTPS VirtualHost, and tells Apache you want to use name based SSL/TLS VirtualHosts.- You may see an IfModule statement bracketing your SSL hosts in
/etc/apache2/sites-available/default-ssl
– this is used to make sure if mod_ssl is missing, Apache can still start properly (everything inside the statement is ignored). To enable the SSL module, usesudo a2enmod ssl
.
The same rules apply with HTTPS VirtualHosts as applied with HTTP VirtualHosts with regard to programs like Squirrelmail. You can check which is the default SSL VHost by typing your public WAN IP address into the address bar preceded with https:// . This will also show you which cert is in use (click the padlock icon location next to the address bar to see cert details); expect to get a certificate error regardless of which cert you’ve set as your IP won’t match the certificate’s Common Name!
Redirecting HTTP to HTTPS
You may find it useful to redirect all HTTP traffic to HTTPS for things like webmail. Here’s an example configuration for Squirrelmail. Define an HTTP virtualhost that just redirects traffic, and then add a HTTPS virtualhost as normal:
#=========================== HTTP redirect to HTTPS ================================== <VirtualHost *:80> ServerName webmail.samhobbs.co.uk <IfModule mod_rewrite.c> <IfModule mod_ssl.c> <Location /> RewriteEngine on RewriteCond %{HTTPS} !^on$ [NC] RewriteRule . https://%{HTTP_HOST}%{REQUEST_URI} [L] </Location> </IfModule> </IfModule> </VirtualHost> #================================ SQUIRRELMAIL ===================================== <IfModule mod_ssl.c> <VirtualHost *:443> DocumentRoot /usr/share/squirrelmail ServerName webmail.samhobbs.co.uk <Directory /usr/share/squirrelmail> Options FollowSymLinks <IfModule mod_php5.c> php_flag register_globals off </IfModule> <IfModule mod_dir.c> DirectoryIndex index.php </IfModule> # access to configtest is limited by default to prevent information leak <Files configtest.php> order deny,allow deny from all allow from 127.0.0.1 </Files> </Directory> ErrorLog ${APACHE_LOG_DIR}/squirrelmail/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/squirrelmail/ssl_access.log combined SSLEngine on SSLCertificateFile /path/to/your/cert.crt SSLCertificateKeyFile /path/to/your/key.key </VirtualHost> </IfModule>
Perfect Forward Secrecy, and HTTP Strict Transport Security (HSTS)
Perfect forward secrecy is a tool that helps mitigate the effects of an attacker obtaining your private key, and HTTP Strict Transport Security helps to prevent Man In The Middle (MITM) attacks. I have covered both of these things in this tutorial about configuring this site to be SSL-only. Hopefully, that has helped you get your head around SSL/TLS VirtualHosts in Apache2. If you’re stuck, let me know and I’ll try to help out!
Comments
Definitely get rid of warning
Hi Sam,
Thanks. This get rid of the warnings except "updating apache." Maybe need to wait the new version to update. I want to know how to get known these prompts and your directive. It is so precisely.
Jeff
HTTPS with local ip
I have followed your tutorial to set up squirrlemail and in doing so now if i type in my local ip address of a different site hosted on the same server it tries to resolve this to HTTPS. what section do i need to change to undo this?
turn off https
ServerName
andServerAlias
in your virtualhost(s) in the apache configuration files. If you want to get another site (yourdomain2.com instead of the default virtualhost yourdomain.com) you need to send a different hostname in the request (i.e. don't type an IP address into your browser, use a hostname). One simple way to change the hostname if you don't have "proper" global DNS resolution is to change the hosts file on your computer to map the hostname you're using in the virtualhost to the local IP address. At the moment, if all your content is in one virtualhost, then that block of code is rewriting everything to https. If you split it up, you can redirect some to https without affecting the rest. SamMisRead the instructions.
Hi Sam,
thanks for the quick reply.
Thats exactly it. got sites foo and bar and 5 virtual host files to redirect to each from my subdomains. I will reread that section and make the changes, i thought it meant if you want the squirrelmail to only use HTTPS :(.
Thanks for the help, cant wait to read more of your tutorials
Thanks
Hi Sam,
I've slowly been working through a good chunk of the tutorials on your site. Each one is written in an easy to understand, concise way that have been a dream to follow. Thanks for the effort you've put into this excellent resource and for sharing it & your follow up in answering questions. Great job.
mark
Thanks for the feedback
Problem about apache, a depricated setting and virtualhost
Hi Sam,
Long time no see. How are you? I just got a newest a post about a instant messenger application. Thanks. I got a problem on apache. It maybe happened on few days ago. I can not visit from the external domain and only be available to visit my website and squirrelmail web page on the internal domain. I use "sudo service apache2 status" to check it out. I got these prompts. It seems there is a depricated settings. The other hand there is a hint of NameVitrualHost no service matter when the next release come. Are these to influence the apache service? If yes, how can I do? Thanks a lot.
● apache2.service - LSB: Apache2 web server
Loaded: loaded (/etc/init.d/apache2)
Drop-In: /lib/systemd/system/apache2.service.d
└─forking.conf
Active: active (running) since Thu 2016-09-29 18:39:22 CST; 37min ago
Process: 3517 ExecStop=/etc/init.d/apache2 stop (code=exited, status=0/SUCCESS)
Process: 3482 ExecReload=/etc/init.d/apache2 reload (code=exited, status=0/SUCCESS)
Process: 3543 ExecStart=/etc/init.d/apache2 start (code=exited, status=0/SUCCESS)
CGroup: /system.slice/apache2.service
├─3559 /usr/sbin/apache2 -k start
├─3566 /usr/sbin/apache2 -k start
├─3567 /usr/sbin/apache2 -k start
├─3568 /usr/sbin/apache2 -k start
├─3569 /usr/sbin/apache2 -k start
├─3578 /usr/sbin/apache2 -k start
├─3581 /usr/sbin/apache2 -k start
├─3599 /usr/sbin/apache2 -k start
├─3604 /usr/sbin/apache2 -k start
├─3605 /usr/sbin/apache2 -k start
└─3606 /usr/sbin/apache2 -k start
Sep 29 18:39:19 raspberrypi apache2[3543]: Starting web server: apache2SecReadStateLimit is depricated, use SecConnReadStateLimit instead.
Sep 29 18:39:19 raspberrypi apache2[3543]: AH00548: NameVirtualHost has no effect and will be removed in the next release /etc/apache2/sites-enabled/default-ssl.conf:2
Sep 29 18:39:22 raspberrypi apache2[3543]: .
Sep 29 18:39:22 raspberrypi systemd[1]: Started LSB: Apache2 web server.
Hi!
Thanks to Sam. I got fixed on it.
Hi Sam,
Wish the new house bring the prosperous for you. I found a package, "ufw" I have been installing on my raspberry pi. This is an misunderstanding. I remove it and the external visiting go well now. To immigrate to VPS is cool. I haven't used VPS service yet. GoDaddy may be the most famous one. But, it seems a service intertwining with virtual machine that the hardware can be arbitrarily used. For economical comparison, is the virtual host much budget due to the hardware architecture sharing? I already have a virtual host on my raspberry pi. I would like to share my IP and boardband to provide a virtual host when you move house this moment.
Jeff
In case you were curious...
In case you were curious about what that blocked post was in your example, it translates to the following after being decoded. It's not comment spam as you speculated, rather it looks like some kind of attack attempt:
cgi-bin/php?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -d auto_prepend_file=php://input -n
Add new comment