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;
...
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.
import { expires } from 'nginx';
const $expires = ({ contentType }) => {
if (contentType === 'text/html') return 'epoch';
return 'off';
}
const siteConfig = {
// ...
expires: (response) => expires($expires(response)),
// ...
};
$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/
Top comments (0)