DEV Community

Cover image for Disable Domain Bare Request HTML Cache for SPA
Eyal Lapid
Eyal Lapid

Posted on • Updated on

Disable Domain Bare Request HTML Cache for SPA

Cover image is taken from pixabay/chanwity

This is a #worknote.

Scenario

  • A SPA app that is served by a server (static Nginx or Nodejs ssr)
  • A reverse proxy that direct traffic from outside to the app server
  • All SPA assets are generated with fingerprints in the file names. Only the main index.html stays as is with no fingerprint.

When deploying a new version of the SPA app, clients need to manually clear browser cache, because they go to the index.html which stays with the same name for each version. The browsers do not know no to cache index.html coming from domain request.

We want to disable cache for the Bare domain response which returns the index.html, but keep the other cache mechanism in place for all other assets.

Solution

The kind global community provides some suggestions and solutions.

The solution that worked for us was using the expires Nginx directive with conditions to only disable cache for html documents.

nginx.conf

map $sent_http_content_type $expires {
  default                    off;
  text/html                  epoch; # means no-cache
}

server { 
 ...
 expires $expires;
 ...
Enter fullscreen mode Exit fullscreen mode
map $sent_http_content_type $expires {
  default                    off;
  text/html                  epoch; # means no-cache
}

server { 
 listen 80;
 server_name frontend;
 expires $expires;

 location / {
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-NginX-Proxy true;
   proxy_pass http://localhost:8082;
   proxy_ssl_session_reuse off;
   proxy_set_header Host $http_host;
   proxy_cache_bypass $http_upgrade;
   proxy_redirect off;
 }
}
Enter fullscreen mode Exit fullscreen mode

Solution Walkthrough

Nginx Map Directive

The ngx_http_map_module module creates variables whose values depend on values of other variables.

The map directive can be thought of as a function that wrap a switch-case statement. The function accept an input parameters and return the result of the switch-case for that input value.

In this case, the expression $expires in the statement expires $expires in converted to a function that gets the content-type header when the expires directive comes to action.

Pseudo Javascript code representing the expansion.

import { expires } from 'nginx';

const $expires = ({ contentType }) => {
  if (contentType === 'text/html') return 'epoch';
  return 'off';
}

const siteConfig = {
  // ...
  expires: (response) => expires($expires(response)),
  // ...
};
Enter fullscreen mode Exit fullscreen mode

$sent_http_content_type

We can access the response header content type using this variable.

From the docs of Nginx core Embedded Variables:

$sent_http_name
arbitrary response header field; the last part of a variable name is the field name converted to lower case with dashes replaced by underscores

Other References

NGinx Expires Directive

The main purpose of the directive is to add headers to the response related to cache and cache expiration.

From the docs

Enables or disables adding or modifying the “Expires” and “Cache-Control” response header fields

The off value

The off parameter disables adding or modifying the “Expires” and “Cache-Control” response header fields.

The epoch value

The epoch parameter sets “Expires” to the value “Thu, 01 Jan 1970 00:00:01 GMT”, and “Cache-Control” to “no-cache”.

Other References

https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching

https://www.docker.com/blog/how-to-use-the-official-nginx-docker-image/

Discussion (0)