Service Worker Setup in a Phoenix app

elixirphoenixservice workernginxedeliver

The problem

I was setting a service worker for some of my sites and wanted to use Workbox from Google. The docs suggested that the file should have cache-control: no-cache. New versions of Chrome do this automatically but to make sure all browsers get the header, some config changes were needed.

Phoenix and Cowboy set long expiry dates for all static assets and I didn’t find a way to change the header just for one file. The next obvious place to have this configuration was nginx that serves the site. Cowboy had been serving the static assets so far so that needed to change. Static assets are often served by nginx anyway so this felt like a move to the right direction.

The project uses Distillery to generate releases togerher with Edeliver, which then deploys them. The static assets are packages as a part of the release and they needed to be accessible to nginx.

Changes required

Then system needed two changes:

  • make static assets from a Distillery release available to the web server
  • configure nginx to serve the files and set the header

Edeliver config change

The assets are packages inside a release folder, which has a version number such as 0.1.4+035fded. Nginx does not have to know about the specific releases but should be configured to serve the static assets from one path. This path is a symbolic link to the specific release that has been deployed. This is similar to what happens with Rails/Capistrano (IIRC).

Edeliver allows for defining hooks that can happen at specific stages of the deployment. A suitable one in this case was the post_extract_release_archive hook. Once the release is extracted, a symbolic link is created from the new app version to the directory from where nginx is configured to serve the files.

post_extract_release_archive() {
  symlink_static
}
symlink_static(){
  status "running post update!"
  __sync_remote "
    ln -sfn $DELIVER_TO/$APP/lib/$WEBAPP-$VERSION/priv/static $DELIVER_TO/$APP/public
  "
}

Nginx config change

It is recommended to serve the service worker file with cache-control: no-cache even though Chrome 68+ makes the request with that setting anyway. The following nginx config block shows this change. The root points to the directory where the assets directory is linked to by Edeliver.

root: "/opt/apps/<app_name>/public”

location = /service-worker.js {
  expires off;
  add_header Cache-Control no-cache;
  access_log off;
}

# This is for setting a long expiry time
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
  expires 365d;
}

The static assets can have a long expiry date as mix phx.digest generates a cache breaking file name when the assets change.

Summary

The change worked well and the WorkBox warning about the cache header is gone, which was the idea with this exercise.