A missing blog post image

Oops, I did it again ! I should have said “httpd” of course, the famous Apache web server.


I was so bored of starting from scratch for each new VHOST created, so the idea of writing an article keeping a template with the best security measures (taken from many places, see Sources below) has come up.
And here we are, a brand new Markdown post about it ! :tada:

Let’s start with the first part !

Part 1 - Securing Apache

If you have never secured Apache globally, you should start with this part (although, if you do, skip it and go to the second part) !

In a few words, the configuration below will… :

  • Block all requests (your VHOSTs will have to allow them explicitly on their side)

  • Turn off Apache verbosity

  • Block all requests to a hidden resource (handful for .git/ folder for instance)

  • Enforce OWASP headers recommendations

  • Authorize Cookies only on secured connections

  • Reduce the default timeout for sessions and limit requests size to 1 MB (you can increase it if you need to)

  • Force clients to negotiate only with secure ciphers, on TLS v1.2 (see Sources)

Just create this file :

# nano /etc/apache2/conf.d/security.local.conf

… and paste the following :

<Directory />
AllowOverride None
Order Deny,Allow
Deny from All

ServerTokens Prod
ServerSignature Off
TraceEnable Off
FileETag None

RedirectMatch 404 ".*\/\..*"

Header set X-Content-Type-Options: "nosniff"
Header set X-XSS-Protection: "1; mode=block"
Header set X-Frame-Options: "sameorigin"

Header edit Set-Cookie "^(.*)$" "$1;HttpOnly;Secure"

TimeOut 60
LimitRequestBody 1024000

SSLHonorCipherOrder On
SSLProtocol -ALL +TLSv1.2
# For Apache >= 2.4
SSLUseStapling On
SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
# For Apache >= 2.4.3
SSLCompression Off
# For Apache >= 2.4.11
SSLSessionTickets Off

BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

You’ll need some new modules to enforce these measures (and those during the second part) :

# a2enmod headers rewrite ssl

I’d advise you to catch a glimpse of the modules already loaded with :
# apachectl -M
You can easily remove some of them you won’t use, as below :
# a2dismod cgi autoindex <...>

If you need any protection from DDoS or BruteForce attacks, I advise you to follow this tutorial.

Reload Apache to parse and apply the new configuration :

# service apache2 reload

Now, you’re all set for the second part :wink:

Part 2 - VHOST specific configuration

Below, you’ll find two configurations :

  1. The first one is supposed to be named 000-default.conf : It will listen to the port 80, and redirect to each HTTPS VHOST in function of the domain name requested (just complete it with as many RewriteCond as you need)

  2. The second one is the content you’ll have to duplicate for each of your VHOST. They will listen to the port 443 (let’s say with Let’s Encrypt support).

<VirtualHost _default_:80>

ServerName your.domain.name
ServerAlias www.your.domain.name
ServerAdmin webmaster@your.domain.name

LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/000-default_error.log
CustomLog ${APACHE_LOG_DIR}/000-default_access.log combined

RewriteEngine On
RewriteCond %{HTTP_HOST} ^your.domain.name$ [OR]
RewriteCond %{HTTP_HOST} ^www.your.domain.name$
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]


<IfModule mod_ssl.c>
VirtualHost _default_:443>

ServerName your.domain.name
ServerAlias www.your.domain.name
ServerAdmin webmaster@your.domain.name

DocumentRoot /path/to/webroot/

<Directory /path/to/webroot/>
Options -Indexes -FollowSymLinks -Includes -ExecCGI
AllowOverride None
Order Allow,Deny
Allow from All

Header set Strict-Transport-Security: "max-age=63072000; includeSubDomains; preload; always"

RewriteEngine On
RewriteCond %{THE_REQUEST} !HTTP/1\.1$
RewriteRule .* - [F]

SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/your.domain.name/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your.domain.name/privkey.pem

LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/your.domain.name_error.log
CustomLog ${APACHE_LOG_DIR}/your.domain.name_access.log combined


This VHOST needs some explanations :

  • Set the webroot and harden the website authorization

  • Set HSTS header (see Sources)

  • Disable (only) HTTP 1.0 requests

  • Loads Let’s Encrypt SSL certificate

    We should say “TLS”, shouldn’t we ? (Coucou Nico’ :wave:)

  • Set dedicated logs (useful to debug and monitor which website is under attack)

Don’t forget to enable each one of your websites with :
# a2ensite <website>


You may encounter a side effect of your VHOST naming.
Try to access your web server directly with its IP, instead of one of the VHOST’s domain names) : http://you_server_ip/.
It’s very likely that you don’t want this website to answer when crawlers will start dealing with your server.
To recover from this behavior, just rename the VHOST you want to answer by default to something like 001-your-domain-name.conf.
When your IP will be accessed on the port 443, Apache will just redirect the query to the first VHOST name in a “top-down” way.

Last words

If you want to use a development Framework, it will surely shipped with a .htaccess file. Don’t forget to pass the AllowOverride to All to authorize your default settings to be overridden for a specific VHOST.
On the idea, some of them (hello CakePHP :wave:) use symbolic link to enable their plugins. So, you may need to activate FollowSymLinks too…

It’s good to secure your web server, but if you are hosting a PHP application for instance, it won’t block any attack occurring at its “logic level”…

So this is the end, I hope it helped you :+1:

Please do propose some changes if you think there is something bad (or something missing) ; I’ll try to keep this article up-to-date in the future ! :ok_hand:


History of revisions

21/11/17 - Adds SSLStapling and Internet Explorer specific directives