Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting public_file_server.headers except for some files

I use this in production.rb :

config.public_file_server.headers = {
  'Cache-Control' => 'public, s-maxage=31536000, maxage=31536000',
  'Expires'       => "#{1.year.from_now.to_formatted_s(:rfc822)}"
}

I use public files through a cdn.mydomain.com, which is reading from www.mydomain.com and it copies the cache-control from www.mydomain.com, that I set with public_file_server.headers.

The issue is that I want some files from /public to not have those cache-control, for example for my service-worker.js

Is there a way to set those cache control only for one folder in /public for example?

The other solution would be to remove this public_file_server.headers configuration, and setting the cache control on the cdn level (I use cdn.mydomain.com/publicfile), and keeping www.mydomain.com/serviceworker without cache control, for the service worker.

But maybe there is a chance to config this at the Rails level?

like image 647
Nicolas Maloeuvre Avatar asked Oct 12 '18 00:10

Nicolas Maloeuvre


1 Answers

I had exactly the same problem: PWA built with Rails using CDN (Cloudfront). For the assets I want to use cache headers with far future expires, but the ServiceWorker needs Cache-control: No-cache.

Because CloudFront doesn't allow to add or change headers by itself, I need a solution on the app level. After some research I found a solution in a blogpost. The idea is to set headers via public_file_server.headers and add a middleware to change this for the ServiceWorker file.

Also, you wrote maxage=, it should be max-age=.

Here is the code I use:

production.rb:

config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
config.public_file_server.headers = {
  'Cache-Control' => 'public, s-maxage=31536000, max-age=15552000',
  'Expires' => 1.year.from_now.to_formatted_s(:rfc822)
}

if ENV['RAILS_SERVE_STATIC_FILES'].present?
  config.middleware.insert_before ActionDispatch::Static, ServiceWorkerManager, ['sw.js']
end

app/middleware/service_worker_manager.rb:

# Taken from https://codeburst.io/service-workers-rails-middleware-841d0194144d
#
class ServiceWorkerManager
  # We’ll pass 'service_workers' when we register this middleware.
  def initialize(app, service_workers)
    @app = app
    @service_workers = service_workers
  end

  def call(env)
    # Let the next middleware classes & app do their thing first…
    status, headers, response = @app.call(env)
    dont_cache = @service_workers.any? { |worker_name| env['REQUEST_PATH'].include?(worker_name) }

    # …and modify the response if a service worker was fetched.
    if dont_cache
      headers['Cache-Control'] = 'no-cache'
      headers.except!('Expires')
    end

    [status, headers, response]
  end
end
like image 136
Georg Ledermann Avatar answered Nov 01 '22 20:11

Georg Ledermann