0

Rails 7 application running on Heroku with a Fastly CDN for static assets in /public and we're now experimenting with PWA compatibility.

All our assets (image, css, js, etc.) are hashed and minified so their cache can be busted if a file changes.

We need our service worker sw.js file to be served directly from /public and not cached by the browser or CDN.

Having trouble getting this to work on Heroku with CDN. What are best practices?

Edit: Adding vcl_recv subroutine from our default VCL created with Heroku's Fastly add-on

sub vcl_recv {
#--FASTLY RECV BEGIN
  if (req.restarts == 0) {
    if (!req.http.X-Timer) {
      set req.http.X-Timer = "S" time.start.sec "." time.start.usec_frac;
    }
    set req.http.X-Timer = req.http.X-Timer ",VS0";
  }
    set req.http.Fastly-Orig-Host = req.http.host;
    set req.http.host = "my-app.herokuapp.com";
  declare local var.fastly_req_do_shield BOOL;
  set var.fastly_req_do_shield = (req.restarts == 0);
  # default conditions
  set req.backend = F_kinscape_production_herokuapp_com__Heroku_;
    # end default conditions
#--FASTLY RECV END
    if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
      return(pass);
    }
    return(lookup);
}
Meltemi
  • 37,979
  • 50
  • 195
  • 293

1 Answers1

1

Best practice would be to ensure the resource (that you don't want to be cached by either the browser or an intermediary proxy/cache/cdn) has the appropriate Cache-Control header set on its response:

Cache-Control: no-cache, no-store, private, max-age=0, max-stale=0

The example contains several directives that control caching behavior:

  1. no-cache: This directive indicates that the response should not be served from cache without first revalidating it with the server. The client must send a request to the server to check if the cached response is still valid before using it.
  2. no-store: This directive specifies that the response should not be stored in any cache, including the browser cache or intermediate caches. Each time the client needs the resource, it must make a request to the server.
  3. private: This directive indicates that the response is intended for a single user and should not be cached by shared caches, such as proxy servers. Private responses are typically specific to a user and contain sensitive information.
  4. max-age=0: This directive sets the maximum time (in seconds) that the response can be considered fresh or valid. A value of zero indicates that the response has already expired and must be revalidated with the server before it can be used.
  5. max-stale=0: This directive indicates that the client should not accept a stale response from the cache. If the cached response is no longer fresh, the client must revalidate it with the server.

EDIT: Just to be clear, this Cache-Control header should only be set for the resource you want to not have cached. I would not recommend setting this generally for all responses.

Additionally, if you have access to the CDN (in this case Fastly), then you'll need to add some custom VCL.

The Fastly documentation has a page on uploading custom VCL so I would recommend having a read-through that (there might already be a custom “main” VCL file; if so just edit that existing file).

NOTE: You'll find more info for "Adding VCL to your service configuration" on the Fastly Developer Hub.

If you're editing an existing VCL file you'll be looking for a vcl_recv subroutine.

If you're creating a custom VCL for the first time, then you'll want to start by using the Fastly boilerplate VCL.

In the vcl_recv you’ll want to add the following condition:

if (req.url ~ "^/path/to/specific/file\.ext$") {
    return(pass);
}

The return(pass) is what tells Fastly to skip looking up the resource in its cache and to go straight to the origin server to retrieve it instead.

Additional References

I've already linked to various documentation resources, but here are some more that might be of interest:

Integralist
  • 495
  • 3
  • 7
  • This is super helpful! Thanks! I've added the `vcl_recv` subroutine. It's not clear where I'd want to insert the conditional (`if`) that you mentioned above. – Meltemi Jun 14 '23 at 22:16
  • @Meltemi you'd want to make sure it's placed before the `return(lookup)` which is what tells Fastly to lookup the requested resource in its cache. – Integralist Jun 15 '23 at 08:15