about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2023-03-16 02:55:54 +0100
committerGitHub <noreply@github.com>2023-03-16 02:55:54 +0100
commitf0e727f958cd9428b2c56a3c6a65bbbf176bfa0d (patch)
treee751fd45f4edd451c04d98163439da68bf1cc890
parentbe488adf711e75ab05a9b22b6241c676e5615c71 (diff)
Add cache headers to static files served through Rails (#24120)
-rw-r--r--config/application.rb5
-rw-r--r--config/environments/development.rb5
-rw-r--r--config/environments/production.rb12
-rw-r--r--config/environments/test.rb5
-rw-r--r--lib/public_file_server_middleware.rb43
5 files changed, 49 insertions, 21 deletions
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