Configure Apache 2 to Control Browser Caching

Original: https://www.liquidweb.com/kb/how-to-configure-apache-2-to-control-browser-caching/

by Jason Potter | Updated: June 4, 2020
Reading Time: 9 minutes

Today we are configuring browser caching control on common Apache 2 Dedicated servers or VPS servers. Caching is a great tool to reduce server resource consumption, bandwidth utilization and provide a faster end-user experience to visitors. To get familiar with caching concepts, simply review our ‘What is Caching?’ tutorial.

Preflight Check

This article covers all Apache 2 servers running the mod_expires and mod_headers Apache modules. This includes, but is not limited to, both traditional Dedicated Servers and Cloud VPS servers running a number of different Linux distributions:

  • Core-managed CentOS 7* Servers
  • Core-managed CentOS 6* Servers
  • Fully managed CentOS 7 cPanel Servers
  • Fully managed CentOS 6 cPanel Servers
  • Fully managed CentOS 7 Plesk Onyx 17 Linux Servers

Note:

Self-managed servers running a similar Linux distribution can take advantage of this article. However, instructions are not specifically provided for Self-managed configurations.

The article assumes familiarity with the following basic system administration concepts:

  • SSH Connections and basic navigation of the standard Linux command-line shell environment
  • Opening, Editing and Saving files in a chosen system editor (i.e. Vim, Nano, etc…).
  • Some familiarity with command-line piping via Linux I/O Redirection

Verify Modules

Our servers generally include both the mod_expires and mod_headers modules needed for browser cache control. However, before we configure the directives, we must first ensure the modules are installed and Apache 2 is ready to accept the directives. Verification is simple. We will be using the apachectl -M command to list the installed Apache modules while piping the output through the grep module_name command to filter the results down to showing only modules with the provided module_name, likes so:

Verifying mod_headers (also known as Headers_module) by copying & pasting the following command.

apachectl -M | grep header

… will return:

headers_module (shared)

Verifying mod_expires (also known as expires module) by copying and pasting the following command.

apachectl -M | grep expires

… will return:

expires_module (shared)

These modules must be present in the output when running the command. If they do not show up in the output, it will simply be blank, which indicates the modules are not installed. If the modules are missing then we will need to install them before we can continue.

Configuration Directives

We can use the following example of a generic configuration that serves to reduce the strain on server resources by prolonging the cache duration of common static files. These types of files typically do not change between visits. So, they do not need to be downloaded on every visit. Modern browsers are equipped to accept instructions from web servers that provide suggestions for how long content should be cached. This example works well for most sites. However, you may need to add/remove file types or adjust lifespan as needed for your particular content.

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/css “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

Explanation of Each Directive

These are opening tags and will only process directives between these if the module, mod_expires, is installed on the server.

<IfModule mod_expires.c> … </IfModule>

Download all files only if the cache has not been accessed in more than 2 days.

ExpiresDefault “access plus 2 days”
Download files only if the cached file has not been accessed in more than 1 month. This covers jpg, jpeg, gif, png, css, javascript, flash, ico, and x-icon file types.

ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/css “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”

Download files only if the cached copy hasn’t been accessed in 10 minutes.

ExpiresByType text/html “access plus 600 seconds”

You can find a more robust explanation of these directives and all that expires_module offers in the Apache mod_expires Online Docs.

Implementation

Now that we have an understanding of how these directives can be configured, we need to decide on our method of implementation. There are generally two methods of implementation for these directives. We classify these as either Portable or Include Methods.

Portable Method

The Portable Method uses .htaccess files to manage which directories are affected by the mod_expires configuration we are settings. These are handled like any other .htaccess file changes.

  1. SSH/FTP to the server
  2. Locate the directory which needs browser caching enabled.
  3. Modify the .htaccess file in that directory or create one if there is not one already.
  4. Add the needed directives from the Configuration Directives section above.
  5. Save the changes to the file.
  6. Done.

There is a small bottleneck warning associated with .htaccess files. This caution is not specific to mod_expires and is an overall Apache alert including .htaccess files in general. In order for .htaccess files to work, Apache must scan every directory leading up to a targeted file looking for and applying any .htaccess files it finds along the way. This can create an I/O bottleneck on some server configurations. We recommend using the Include Method on all Cloud VPS Servers to avoid this type of problem.

Include Method

In contrast to the Portable Method, the Include Method takes advantage of the Apache includes a system. Apache only reads include files at startup so this prevents the I/O Bottleneck discussed above in the Portable Method section.

There are generally two ways to use the Include Method: Globally or Per Website. Either method requires locating and modifying the correct include files on the server. The correct files to modify is dependent on both distribution and server management software. We will discuss the correct locations for both methods on the various Liquid Web CentOS servers we support and listed in the Preflight Check section above.

Global Includes

Applying the mod_expires directives globally is straight forward. It will have the effect of enabling the desired directives over the entire server, affecting every site running through Apache.

Core-managed CentOS 6 & 7 Servers

  1. Create a file named expires.confin /etc/httpd/conf.d/ by typing in the following command:

vim /etc/httpd/conf.d/expire.conf

  1. Add the necessary directives to the file and save the changes.
    The file should look like the following:

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

  1. To finish, reload Apache for the server to see the changes:

Service httpd reload

Fully managed CentOS 6 & 7 cPanel Servers

  1. Create file name pre_virtualhost_global.conf  in /usr/local/apache/conf/includes/ if it does not already exist.

vim /usr/local/apache/conf/includes/pre_virtualhost_global.conf

  1. Add the necessary directives to the bottom of the file and save the changes.
    Your file may contain additional directives in this file, but the bottom should look like this:

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

  1. Restart Apache Service:

/scripts/restartsrv_apache

If Running EasyApache 4: Restart Apache PHP-FPM Service

/scripts/restartsrv_apache_php_fpm

Fully managed CentOS 7 Plesk Onyx 17 Linux Servers

  1. Create file name expires.confin /etc/httpd/conf.d/

vim /etc/httpd/conf.d/expire.conf

  1. Add the necessary directives to the file and save the changes.
    The file should look like the following:

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

  1. Restart Apache Service:

Service httpd restart

Per Website Includes

We can also use Apache includes on a per virtual host level to enable browser caching on an individual website basis. We’ll go over how to configure these on our CentOS systems below.

Note:

Each website has two virtual hosts, one for HTTP (port 80) connections and another for HTTPS (port 443) connections. Each virtual host is independent of one another. Adding a change to the HTTP virtual host will not automatically apply to the HTTPS virtual host and vice versa.

Core-managed CentOS 6 & 7 Servers

The exact method of site management on Core-managed servers is left up to the server owner. This can vary dramatically depending on the person. We will use the default SSL site configuration file as an example on how to configure the Per Website includes for browser caching. Once you locate the necessary site’s configuration file, follow these steps:

  1. Locate and Open the configuration file for the site being modified. 

vim /etc/httpd/conf.d/ssl.conf

2.  Locate the Virtual Host line for the site within its config file.  A Virtual Host Stanza looks like the following example:

<VirtualHost _default_:443>

</VirtualHost

3. Apply the needed mod_expires directives between the virtual host lines.
The results should look similar to the following example:

<VirtualHost _default_:443>

   <IfModule mod_expires.c>
   # Turn on the module.
   ExpiresActive on
   # Set the default expiry times.
   ExpiresDefault “access plus 2 days”
   ExpiresByType image/jpg “access plus 1 month”
   ExpiresByType image/gif “access plus 1 month”
   ExpiresByType image/jpeg “access plus 1 month”
   ExpiresByType image/png “access plus 1 month”
   ExpiresByType text/javascript “access plus 1 month”
   ExpiresByType application/javascript “access plus 1 month”
   ExpiresByType application/x-shockwave-flash “access plus 1 month”
   ExpiresByType text/css “now plus 1 month”
   ExpiresByType image/ico “access plus 1 month”
   ExpiresByType image/x-icon “access plus 1 month”
   ExpiresByType text/html “access plus 600 seconds”
   </IfModule>

</VirtualHost>
4. Restart The Apache Service

service httpd restart

Fully managed CentOS 6 & 7 cPanel Servers

cPanel provides a rich template system that can be used to modify Apache behavior as needed. There is a specific directory structure needed to ensure our modifications persist through updates, upgrades, and restarts. This system works the same way on both EasyApache 3 and EasyApache 4 systems.

Each site can handle its set of custom includes files. These need to be located in the following locations:


HTTP Virtual Hosts:
/etc/apache2/conf.d/userdata/std/2_4/<USER>/<DOMAIN>/<INCLUDENAME>.conf


HTTPS Virtual Hosts:
/etc/apache2/conf.d/userdata/ssl/2_4/<USER>/<DOMAIN>/<INCLUDENAME>.conf

There are three variables in the path above that need to be reconciled:

  • <USER> replaced by the necessary account’s username.
  • <DOMAIN> replaced by the fully qualified domain.tld name of the site. (minus the www. prefix)
  • <INCLUDENAME> replace by the name of the include file. This should reflect the includes purpose. E.G. expires.conf
  1. These directories do not exist by default and will need to be created. Once you know the details this can be done easily with the mkdir -p command like so:

HTTP Virtual Host:

mkdir -p /etc/apache2/conf.d/userdata/std/2_4/myuser/example.com/

HTTPS Virtual Host:

mkdir -p /etc/apache2/conf.d/userdata/ssl/2_4/myuser/example.com/

  1. After the directories are created, we can now create our include files, calling it expires.conf.
    HTTP Virtual Host:

vim /etc/apache2/conf.d/userdata/std/2_4/myuser/example.com/expires.conf

HTTPS Virtual Host:
vim /etc/apache2/conf.d/userdata/ssl/2_4/myuser/example.com/expires.conf

  1. Add the necessary mod_expires directives to both expires.conf files. They should look similar to this when complete:

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

  1. Now we will need to have cPanel rebuild the Apache configuration to apply the new includes.

/usr/local/cpanel/scripts/rebuildhttpdconf

  1. Restart Apache to update the running configuration:
    /usr/local/cpanel/scripts/restartsrv_apache
  2. If running EasyApache 4, Restart Apache PHP-FPM service as well:
    /usr/local/cpanel/scripts/restartsrv_apache_php_fpm

There are additional methods for handling virtual hosts in cPanel. Applying includes to all hosts or all HTTPS hosts or even all hosts by one user. For a much more in-depth explanation of the cPanel Virtual Host Include system, visit the Official cPanel Online Docs.

Fully managed CentOS 7 Plesk Onyx 17 Linux Servers

Plesk provides a robust include and template system for modification of virtual host entries on an individual virtual host basis. These are done in the following files:

Note:

We will need to replace example.com with your domain name (minus www. prefix).
/var/www/vhosts/system/example.com/conf/vhost_ssl.conf

The directory structure here should already exist. However, these vhost.conf and vhost_ssl.conf files do not exist by default and will need to be created.

  1. Create the needed include files:
    HTTP Virtual Host:

touch /var/www/vhosts/system/example.com/conf/vhost.conf

HTTPS Virtual Host:
touch /var/www/vhosts/system/example.com/conf/vhost_ssl.conf

  1. Modify both vhost.conf and vhost_ssl.conf applying the necessary mod_expires directives. When finish each file should look similar to the following:

<IfModule mod_expires.c>
# Turn on the module.
ExpiresActive on
# Set the default expiry times.
ExpiresDefault “access plus 2 days”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/javascript “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
ExpiresByType text/css “now plus 1 month”
ExpiresByType image/ico “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType text/html “access plus 600 seconds”
</IfModule>

  1. Have Plesk rebuild the configuration for the site in question.
    /usr/local/psa/admin/sbin/httpdmng –reconfigure-domain example.com
  2. Restart Apache Service:
    service httpd restart

The Plesk templates and includes systems are very robust and permits the integration of many other common Apache directives. Visit the Plesk Onyx Online Documentation to learn more about leveraging its capabilities.