Ubuntu - nginx - Configure Nginx - Sane Caching Configuration

# Sample Nginx config with sane caching settings for modern web development
#
# Motivation: 
# Modern web development often happens with developer tools open, e. g. the Chrome Dev Tools. 
# These tools automatically deactivate all sorts of caching for you, so you always have a fresh 
# and juicy version of your assets available.
# At some point, however, you want to show your work to testers, your boss or your client. 
# After you implemented and deployed their feedback, they reload the testing page – and report 
# the exact same issues as before! What happened? Of course, they did not have developer tools 
# open, and of course, they did not empty their caches before navigating to your site. 
# 
# This gist provides you with a sample Nginx config with sane caching settings to prevent the 
# above scenario. 
#
# Specifically, it 
#  - deactivates caching by default, 
#  - allows storing of js and css files, but requires the browser to first check whether newer 
#    versions are available
#  - activates caching for static assets such as images for 5 minutes
# 
# Why to cache at all, you wonder? 
# This gist should highlight the possibilities you have, and might even be used in a beta testing 
# environment with many users yet regular hotfixes. Therefore, I included basic caching for you to 
# be adjusted. 
#
# Further readings: 
# http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/
# https://www.mnot.net/cache_docs/
# http://www.ietf.org/rfc/rfc2616.txt (for a deep dive)

server {
  # .domain.com will match both domain.com and anything.domain.com
  server_name .example.com;
 
  # It is best to place the root of the server block at the server level, and not the location level
  # any location block path will be relative to this root. 
  root /usr/local/www/$server_name;

  # It's always good to set logs, note however you cannot turn off the error log
  # setting error_log off; will simply create a file called 'off'.
  access_log /var/log/nginx/$host.access.log;
  error_log /var/log/nginx/$host.error.log;

  # This can also go in the http { } level
  index index.html index.htm index.php;

  # web app
  location / {
    try_files $uri $uri/ =404;

    # The Expires HTTP header is a basic means of controlling caches; it tells all caches how long 
    # the associated representation is fresh for. After that time, caches will always check back with 
    # the origin server to see if a document is changed.
    #
    # "If a request includes the no-cache directive, it SHOULD NOT include min-fresh, max-stale, or max-age." 
    # (source: http://www.ietf.org/rfc/rfc2616.txt, p114)
    #
    # A negative value means that the response expires immediately.
    # Nginx automatically sets the `Cache-Control: no-cache` header, if `expires` is negative
    #
    expires           -1;
  }

  # this block will catch files that might need to change immediately (e. g. to deploy hotfixes), such as js or css
  # The ?: prefix is a 'non-capturing' mark, meaning we do not require
  # the pattern to be captured into $1 which should help improve performance
  location ~* \.(?:css|js)$ {
    access_log        off;
    log_not_found     off;

    # no-cache:         forces caches to submit the request to the origin server for validation before releasing a 
    #                   cached copy, every time. This is useful to assure that authentication is respected 
    #                   (in combination with public), or to maintain rigid freshness, without sacrificing all of the 
    #                   benefits of caching.
    # 
    # public:           marks authenticated responses as cacheable; normally, if HTTP authentication is required, 
    #                   responses are automatically private.
    #
    # must-revalidate:  tells caches that they must obey any freshness information you give them about a 
    #                   representation. HTTP allows caches to serve stale representations under special conditions;
    #                   by specifying this header, you’re telling the cache that you want it to strictly follow 
    #                   your rules.
    # 
    # proxy-revalidate: similar to must-revalidate, except that it only applies to proxy caches.
    # 
    add_header        Cache-Control "no-cache, public, must-revalidate, proxy-revalidate";
  }

  # This block will catch static file requests, such as images
  # The ?: prefix is a 'non-capturing' mark, meaning we do not require
  # the pattern to be captured into $1 which should help improve performance
  location ~* \.(?:jpg|jpeg|gif|png|ico|xml)$ {
    access_log        off;
    log_not_found     off;

    # The Expires HTTP header is a basic means of controlling caches; it tells all caches how long 
    # the associated representation is fresh for. After that time, caches will always check back with 
    # the origin server to see if a document is changed.
    #
    # "If a request includes the no-cache directive, it SHOULD NOT include min-fresh, max-stale, or max-age." 
    # (source: http://www.ietf.org/rfc/rfc2616.txt, p114)
    #
    # Nginx automatically sets the `Cache-Control: max-age=t` header, if `expires` is present, where t is a time 
    # specified in the directive, in seconds. Shortcuts for time can be used, for example 5m for 5 minutes.
    #
    expires           5m;

    # public:           marks authenticated responses as cacheable; normally, if HTTP authentication is required, 
    #                   responses are automatically private.
    # 
    add_header        Cache-Control "public";
  }
  
  # This block will catch static file requests of fonts and allows fonts to be requested via CORS
  # The ?: prefix is a 'non-capturing' mark, meaning we do not require
  # the pattern to be captured into $1 which should help improve performance
  location ~* \.(?:eot|woff|woff2|ttf|svg|otf) {
    access_log        off;
    log_not_found     off;

    # The Expires HTTP header is a basic means of controlling caches; it tells all caches how long
    # the associated representation is fresh for. After that time, caches will always check back with
    # the origin server to see if a document is changed.
    #
    # "If a request includes the no-cache directive, it SHOULD NOT include min-fresh, max-stale, or max-age."
    # (source: http://www.ietf.org/rfc/rfc2616.txt, p114)
    #
    # Nginx automatically sets the `Cache-Control: max-age=t` header, if `expires` is present, where t is a time
    # specified in the directive, in seconds. Shortcuts for time can be used, for example 5m for 5 minutes.
    #
    expires           5m;

    # public:           marks authenticated responses as cacheable; normally, if HTTP authentication is required,
    #                   responses are automatically private.
    #
    add_header        Cache-Control "public";

    # allow CORS requests
    add_header        Access-Control-Allow-Origin *;

    types     {font/opentype otf;}
    types     {application/vnd.ms-fontobject eot;}
    types     {font/truetype ttf;}
    types     {application/font-woff woff;}
    types     {font/x-woff woff2;}
  }

  # this prevents hidden files (beginning with a period) from being served
  location ~ /\. { 
    access_log        off; 
    log_not_found     off; 
    deny              all; 
  }
}