How to Improve Performance with HTTP Cache Headers

How to Improve Performance with HTTP cache headersIn this blog post I will show you a simple technique to improve your web application performance by modifying headers. Please keep in mind, if you're using HTTP reverse proxy caching applications such as Varnish you might harm your application performance or your settings could be ignored.

This technique could help improve page loads for authenticated users where reverse proxy caching disabled.

Important: Drupal already already provides all the required headers when Performance settings properly configured. This is information is generic and could be very helpful for decoupled projects or any other frameworks.

There are two primary cache headers, Cache-Control and Expires. You can set caching either time-based, content-based or on Expire date.

Cache-Control

The Cache-Control general-header field is used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain.

Settings

Example header:
Cache-Control: max-age=900, public

public - Indicates that the response MAY be cached by any cache, even if it would normally be non-cacheable or cacheable only within a non- shared cache.

private - Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache. This allows an origin server to state that the specified parts of the response are intended for only one user and are not a valid response for requests by other users. This parameter doesn't provide the same level of privacy as SSL does.

max-age - Indicates that the client is willing to accept a response whose age is no greater than the specified time in seconds. Unless max- stale directive is also included, the client is not willing to accept a stale response. (The value is in seconds)

See all available options RFC 2616

Expires

Note: If both Expires and max-age are set max-age will take precedence.

This header parameter tells the browser when to next retrieve the resource from the network.

Example:

Cache-Control:public
Expires: Mon, 25 May 2016 11:31:12 GMT

 

Time-based

Last-Modified - The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified.

Example:
Last-Modified: Wed, 25 May 2016 11:45:26 GMT

If-Modified-Since - The If-Modified-Since request-header field is used with a method to make it conditional: if the requested variant has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message-body.

Example:
If-Modified-Since: Wed, 25 May 2016 11:45:26 GMT

Content-based

ETag - Short for "entity-tag", the ETag is a unique identifier for the resource being requested, typically comprised of the hash of that resource, or a hash of the timestamp the resource was updated. Basically, this lets a client ask smarter questions of the CDNs, like "give me X if it's different than the ETag I already have."

Note: This tag is useful when for when the last modified date is difficult to determine.

Cache-Control:public, max-age=604800
ETag: "4345717de182e49e8d7bd9994af537ed" 

On subsequent browser requests the If-None-Match request header is sent with the ETag value of the last requested version of the resource.

If-None-Match: "4345717de182e49e8d7bd9994af537ed"

Drupal 8 Example

/**
 * Example Even Subscriber.
 */
class AddCustomHTTPheaders implements EventSubscriberInterface {

  /**
   * Sets extra HTTP headers.
   */
  public function onRespond(FilterResponseEvent $event) {
    $response = $event->getResponse();
    $current_user = \Drupal::currentUser();
    // Set Cache-Control for authenticated users
    if ( !$current_user->isAnonymous() ) {
      $response->headers->set('Cache-Control', 'public, max-age: 604800');
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::RESPONSE][] = ['onRespond'];
    return $events;
  }

}

Please read How to Register an Event Subscriber in Drupal 8 to learn more about the code example. And download Drupal 8 module to see how this can be implemented.

Useful links

  1. Increasing Application Performance with HTTP Cache Headers
  2. HTTP caching - Web Fundamentals - Google Developers
  3. Analysis of HTTP Performance problems
  4. Bloated Request & Response Headers
  5. Improve Performance with Cache-Control Headers
  6. Caching Tutorial
  7. A Guide to Caching with NGINX
  8. RFC 2616 - 14.9 Cache-Control