From f0e727f958cd9428b2c56a3c6a65bbbf176bfa0d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 16 Mar 2023 02:55:54 +0100 Subject: [PATCH] Add cache headers to static files served through Rails (#24120) --- config/application.rb | 5 ++++ config/environments/development.rb | 5 ---- config/environments/production.rb | 12 +------- config/environments/test.rb | 5 ---- lib/public_file_server_middleware.rb | 43 ++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 lib/public_file_server_middleware.rb diff --git a/config/application.rb b/config/application.rb index c51eacd68..43631c551 100644 --- a/config/application.rb +++ b/config/application.rb @@ -35,6 +35,7 @@ require_relative '../lib/terrapin/multi_pipe_extensions' require_relative '../lib/mastodon/snowflake' require_relative '../lib/mastodon/version' require_relative '../lib/mastodon/rack_middleware' +require_relative '../lib/public_file_server_middleware' require_relative '../lib/devise/two_factor_ldap_authenticatable' require_relative '../lib/devise/two_factor_pam_authenticatable' require_relative '../lib/chewy/strategy/mastodon' @@ -181,6 +182,10 @@ module Mastodon config.active_job.queue_adapter = :sidekiq config.action_mailer.deliver_later_queue_name = 'mailers' + # We use our own middleware for this + config.public_file_server.enabled = false + + config.middleware.use PublicFileServerMiddleware if Rails.env.development? || ENV['RAILS_SERVE_STATIC_FILES'] == 'true' config.middleware.use Rack::Attack config.middleware.use Mastodon::RackMiddleware diff --git a/config/environments/development.rb b/config/environments/development.rb index f5f6cbed8..32c5cce8b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -16,12 +16,7 @@ Rails.application.configure do # Run rails dev:cache to toggle caching. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true - config.cache_store = :redis_cache_store, REDIS_CACHE_PARAMS - - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}", - } else config.action_controller.perform_caching = false config.cache_store = :null_store diff --git a/config/environments/production.rb b/config/environments/production.rb index b8536c53a..00d783477 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -19,24 +19,14 @@ Rails.application.configure do # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). # config.require_master_key = true - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - ActiveSupport::Logger.new(STDOUT).tap do |logger| logger.formatter = config.log_formatter config.logger = ActiveSupport::TaggedLogging.new(logger) end - # Compress JavaScripts and CSS. - # config.assets.js_compressor = Uglifier.new(mangle: false) - # config.assets.css_compressor = :sass - # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - # Specifies the header that your server uses for sending files. config.action_dispatch.x_sendfile_header = ENV['SENDFILE_HEADER'] if ENV['SENDFILE_HEADER'].present? @@ -66,7 +56,7 @@ Rails.application.configure do # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # English when a translation cannot be found). - config.i18n.fallbacks = [:en] + config.i18n.fallbacks = true # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify diff --git a/config/environments/test.rb b/config/environments/test.rb index 9cbf31e8d..1328e155a 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -12,11 +12,6 @@ Rails.application.configure do # preloads Rails for running tests, you may have to set it to true. config.eager_load = false - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" - } config.assets.digest = false # Show full error reports and disable caching. diff --git a/lib/public_file_server_middleware.rb b/lib/public_file_server_middleware.rb new file mode 100644 index 000000000..3799230a2 --- /dev/null +++ b/lib/public_file_server_middleware.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'action_dispatch/middleware/static' + +class PublicFileServerMiddleware + SERVICE_WORKER_TTL = 7.days.to_i + CACHE_TTL = 28.days.to_i + + def initialize(app) + @app = app + @file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first) + end + + def call(env) + file = @file_handler.attempt(env) + + # If the request is not a static file, move on! + return @app.call(env) if file.nil? + + status, headers, response = file + + # Set cache headers on static files. Some paths require different cache headers + headers['Cache-Control'] = begin + request_path = env['REQUEST_PATH'] + + if request_path.start_with?('/sw.js') + "public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate" + elsif request_path.start_with?(paperclip_root_url) + "public, max-age=#{CACHE_TTL}, immutable" + else + "public, max-age=#{CACHE_TTL}, must-revalidate" + end + end + + [status, headers, response] + end + + private + + def paperclip_root_url + ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + end +end