How to configure Fastly so it doesn't cache /sw.js in our PWA app?

Hi, We have a Rails app hosted on Heroku that has been set up to act as a PWA (at least that’s what we’re attempting) but one requirement of PWAs is that /sw.js not be cached on the endpoints. It needs to be served directly from /public/sw.js. Is it possible to get Fastly to ignore this one static file from our public folder?

Hi @mbd2

If you’re not in control of the Fastly service that is fronting the Heroku hosted app, then I think you’ll need to configure your Rails app to send back a Cache-Control response header to indicate to any intermediary proxies/caches (like Fastly) how a resource should be cached (or not at all).

So I imagine a Cache-Control header like the following would work:

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.

Thank you for your response.

We do have access to the Fastly service (through our Heroku add-on). What’s not clear to me is where to tell our server that we don’t want this one particular file cached.

Can we do this in some Fastly config. Tell Fastly to ignore this one file and send requests for it straight to origin (our server). This would be ideal, I think?

Or maybe there’s a way to isolate a file in Rails so Heroku can keep Fastly from trying to cache it (which may be harder to accomplish).

Is there a way to have Fastly ignore a file at a specific path and send those requests on to origin?

Thanks @mbd2 for clarifying your setup.

I’m not that familiar with the Fastly Heroku add-on but looking at the documentation it suggests you can access the Fastly UI using the CLI command: heroku addons:open fastly.

It’s very likely the Heroku add-on is setting up a Fastly VCL service for you.

I would say if you want a specific file to be skipped from the Fastly cache lookup, 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 in 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:

I agree with @Integralist that the best place to set these headers is in your origin. Rails serves files from public/ using ActionDispatch::Static, which in turn delegates to Rack::File. Those classes don’t offer a way to apply different headers to specific paths, but you can use a custom middleware to do that:

# application.rb

class ServiceWorkerMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @app.call(env).tap do |(status, headers, body)|
      headers['Cache-Control'] = 'no-cache' if env['PATH_INFO'] == '/sw.js'
    end
  end
end

config.middleware.insert_before(ActionDispatch::Static, ServiceWorkerMiddleware)