Disable Domain Bare Request HTML Cache for SPA
Tags: nginx, worknote
Published: 2021-09-13 07:51:21

<sub>Cover image is taken from [pixabay/chanwity](https://pixabay.com/photos/fisherman-fishing-fish-boat-net-4305368/)</sub>

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](https://stackoverflow.com/a/57849060/3625171).

The solution that worked for us was using the [expires](https://stackoverflow.com/a/57849060/3625171) Nginx directive with conditions to only disable cache for html documents.

`nginx.conf`

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

server { 
 ...
 expires $expires;
 ...
```


```nginx
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;
 }
}
```


### 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.
```js
import { expires } from 'nginx';

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

const siteConfig = {
  // ...
  expires: (response) => expires($expires(response)),
  // ...
};
```

* [Official Docs](https://nginx.org/en/docs/http/ngx_http_map_module.html)
* [Graham Weldon - Nginx maps and geo](https://grahamweldon.com/post/2019/06/nginx-maps-and-geo/)
  

#### $sent_http_content_type

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

From the [docs](https://nginx.org/en/docs/http/ngx_http_core_module.html) 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

* [Oreilly Nginx Http Server Docs](https://www.oreilly.com/library/view/nginx-http-server/9781788623551/26a2d52c-9fbe-4e0e-9ab4-6d4c045e66eb.xhtml)



#### 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](http://nginx.org/en/docs/http/ngx_http_headers_module.html)

> 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/