Apache Basics: A Beginner's Walkthrough for a Basic WordPress Setup

Apache web server can be daunting for a beginner, and the official documentation can be cryptic for someone just getting started. Apache does have getting started documentation, but I found it to be lacking in how to actually set up a real server. In particular, I was concerned about ensuring my first setup for a WordPress site was secure. I found that the best way to understand Apache was to walk through a sample configuration until I knew what each line was for.

This walkthrough is based on the default Apache configuration of a DigitalOcean Ubuntu 14.04 WordPress droplet running Apache 2.4. I will be walking through this example apache2.conf file section-by-section.

Getting Started

The first step is knowing where to look. Apache’s configuration files are kept in /etc/apache2, and all files will be relative to this directory for the rest of this walkthrough.

Of particular interest are apache2.conf, which is the entry point for all configuration, and envvars, which contains environment variable declarations. The variables declared here correspond to the shell-style variable interpolations seen in apache2.conf, such as ${APACHE_RUN_USER}. In a simple setup like this, these can be safely left as the defaults.

The envvars file is read when Apache is started by the apache2ctl command-line tool. apache2ctl is what you should use for interacting with Apache; see the man pages (man apache2ctl) for more details.

In Apache, each “setting” in the configuration file is called a directive. Common directives include Require, <Directory>, <VirtualHost>, and DocumentRoot.

Front Matter

Here’s the beginning of the file:

ServerRoot "/etc/apache2"
Mutex file:${APACHE_LOCK_DIR} default
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel crit
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf

Most of this is basic Apache configuration that can be glanced over pretty quickly. For a simple setup, your server’s defaults are probably fine. Line-by-line, here’s what all this does:

  • ServerRoot "/etc/apache2" is the directory in which the server lives. The two important things to note here are:
    1. This is NOT the directory files are served from; that is configured separately.
    2. Relative paths in other directives are relative to ServerRoot.
  • Mutex file:${APACHE_LOCK_DIR} default: Configures mutual exclusion for Apache. See the docs for more info.
  • PidFile ${APACHE_PID_FILE} is the location of the Apache daemon’s process id file.
  • Timeout 300 - How long Apache will wait before failing a request in various circumstances.
  • KeepAlive On - Whether or not to allow multiple requests over the same TCP connection. Leave this on.
  • MaxKeepAliveRequests 100 - Number of requests allowed per connection when KeepAlive is on.
  • KeepAliveTimeout 5 - How long Apache will wait (in seconds) for another request before closing the connection.
  • User ${APACHE_RUN_USER} and Group ${APACHE_RUN_GROUP} - The user and group Apache will run as. These are usually the default www-data. The important thing here is that these are NOT root, and that these do not have access to anything beyond what it needs to run your server. Commands the server needs to execute, such as PHP scripts, will be run as this user and group.
  • HostnameLookups Off - If turned on, this enables DNS lookups so the host names of requests can be logged. However, that increases server latency and network traffic. This should be left off.
  • ErrorLog ${APACHE_LOG_DIR}/error.log - File to log errors to.
  • LogLevel crit - How verbose logging should be. See the official documentation for descriptions of all available levels.
  • IncludeOptional mods-enabled/*.load and IncludeOptional mods-enabled/*.conf - One of the core features of Apache is that it is composed of modules which expand its functionality. For example, PHP is enabled via a module. The IncludeOptional directive will include the specified modules, but only if they exist. Note that this is new in Apache 2.4. Also note that the contents of mods-enabled are symbolic links to the corresponding files in mods-available; this makes it easy to remove a module by simply deleting the symlink and reloading the server.
  • Include ports.conf - This includes the ports.conf file, which contains directives such as Listen 80, which tell Apache what ports to listen on for incoming traffic. Include differs from IncludeOptional in that if the file does not exist or can’t be accessed, the server will fail to start.

<Directory>

The next group of directives is more interesting and relevant to actually configuring a server. Consider the following:

<Directory />
    Options FollowSymLinks
    AllowOverride None
    <Limit PUT DELETE CONNECT OPTIONS PATCH PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
        deny from all
    </Limit>
</Directory>

<Directory /var/www>
        Options FollowSymLinks
        AllowOverride FileInfo
</Directory>

<Directory /usr/share>
        AllowOverride None
        Require all granted
</Directory>

<Directory> is an important directive, as it encloses directives that will apply to specific directories on your server. As with ServerRoot, note that this does NOT configure which directories files will actually be served from.

Options enables various options for the directory. In this case, we are telling the server to follow symbolic links in the file system root / and in /var/www, which is a common default location to serve files from.

AllowOverride None will stop Apache from reading .htaccess files in the directory. This is an important security consideration in the case of /, since .htaccess files can contain directives that compromise server security. In /var/www we also AllowOverride FileInfo, which allows .htaccess to use various directives controlling document types, such as ErrorDocument for specifying a 404 file; see the docs for more info on what this does.

The inclusion of the <Limit> directive here by DigitalOcean was confusing to me. <Limit> will restrict a directive to the specific HTTP methods, which here means everything except GET, POST, HEAD, and TRACE; see the docs for more on how this directive works. Furthermore, the contained deny from all directive is deprecated in Apache 2.4; the newer Require style syntax should be used instead. Regardless, files should never be served from / anyway, so you may choose to include or exclude something like this at your discretion.

For WordPress, the directives in <Directory /usr/share> don’t really matter, and we can safely ignore this. See this Server Fault post for more info on what this directory is for.

Distributed Configuration Files

Consider these directives:

AccessFileName .htaccess

<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>

We discussed .htaccess earlier, but now we go into a little more detail. AccessFileName .htaccess specifies that the name of the distributed configuration file is .htaccess. It turns out that this is also the default name, but it’s good to explicitly state it. It’s worth mentioning that distributed configuration files slow down your server, so if you have access to the main Apache configuration file (which you probably do if you’re reading this), then you may prefer to put the contents of that file there instead.

As for <FilesMatch>, that will apply the enclosed directives to any files matching a certain regex pattern. In this case, we are applying a directive to any file that starts with .ht, which includes .htaccess and .htpasswd. Through Require all denied, we are preventing access to those files. This is a good security practice, since it is common to include .htaccess files in directories that also contain served files, but we do not want the .htaccess file itself to be served.

Logging

Consider these directives:

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

I won’t go into too much detail here, but these lines all specify nicknames for different log formats that can be used with CustomLog. See here for more on log formats and here.

Include Additional Sites and Configuration Files

Similar to how we optionally included any available modules earlier, we also include additional configuration files and sites:

IncludeOptional conf-enabled/*.conf
IncludeOptional sites-enabled/*.conf

The sites-enabled dir should contain symlinks to files in sites-available, which are configurations containing <VirtualHost> directives. In this case I didn’t have any, but this is common on servers serving multiple sites.

The conf-enabled directory contains some extra miscellaneous configuration. In particular, my installation contained a file called other-vhosts-access-log.conf, which contained this directive:

CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined

This sets up a default access log for virtual hosts that don’t use CustomLog to define their own log file, using the vhost_combined log format we defined earlier.

WordPress-Specific Security

Consider these directives:

<DirectoryMatch ^.*/wp-content/uploads/>
  AllowOverride None
  php_flag engine off
  php_admin_value engine Off
</DirectoryMatch>

<DirectoryMatch ^.*/wp-content/blogs.dir/>
  AllowOverride None
  php_flag engine off
  php_admin_value engine Off
</DirectoryMatch>

Here, we are defining rules that should be applied to any wp-content/uploads/ or wp-content/blogs.dir/ directory, no matter where we’ve placed it on the server. These are uploads directories for single and multisite WordPress installations, respectively. We’ve disabled .htaccess again, because we don’t want someone overriding server configs by uploading a .htaccess file here. Even more importantly, we’ve turned off PHP. PHP was included earlier when we loaded in modules, but we need to disable PHP here because otherwise someone could upload a PHP script that executes malicious code on the server when the URL of the script is visited. Not good.

Password Protection

In my installation, I wanted to password-protect the admin panel beyond WordPress’ default login screen:

<DirectoryMatch ^.*/wp-admin/>
  AuthType Basic
  AuthName "Restricted Area"
  AuthUserFile /etc/apache2/.htpasswd
  Require valid-user
</DirectoryMatch>

When the wp-admin section of the site is visited, the browser will present a popup requiring a user name and password to be entered. The list of valid users and their (hashed) passwords is contained in the file specified by AuthUserFile. See the docs for more information on each of these settings.

To work with the .htpasswd file, use the htpasswd command line tool. If this is missing, you can install it via sudo apt-get install apache2-utils.

Enabling a Site

It took us a long time, but we’ve finally reached the point where we actually define a website, or “virtual host”:

<VirtualHost *:80>
   ServerAdmin webmaster@example.com
   DocumentRoot /var/www
   ServerName XX.XX.XXX.XX
   ServerAlias example.com www.example.com
 </VirtualHost>

We use the <VirtualHost> directive to do this. Here, we are defining a virtual host on port 80, which is the well-known port used for HTTP traffic (we told Apache to listen on port 80 earlier). The ServerAdmin directive specifies an email address that will be displayed to users when Apache returns an error. DocumentRoot specifies the directory on the server that files will actually be served from.

ServerName sets the name by which this server identifies itself, and ServerAlias is used to set other names for it. In this case, since this is the only site running off of this server, we’ve identified it by its IP address, and set example.com and www.example.com as aliases for it. Of course, you could leave out the ServerAlias and just set ServerName www.example.com if you wanted. See the docs for more info.

Conclusion

We should now have a basic understanding of a simple yet real-world Apache server and some of the security concerns involved in setting it up for a simple WordPress site. Knowing the basics and where to look makes it much easier to understand the official documentation and the security implications of a given setup.