From 58699153719e1a49b581ae614b94ba1790317ba7 Mon Sep 17 00:00:00 2001
From: Tom Robinson <tlrobinson@gmail.com>
Date: Wed, 31 Oct 2018 14:13:06 -0700
Subject: [PATCH] Far-future cache header for JS/CSS files with cache-busting
 query strings

---
 src/metabase/middleware.clj | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/src/metabase/middleware.clj b/src/metabase/middleware.clj
index b8e3e7fc422..00b5117cfbd 100644
--- a/src/metabase/middleware.clj
+++ b/src/metabase/middleware.clj
@@ -39,6 +39,11 @@
   [{:keys [uri]}]
   (re-matches #"^/embed/.*$" uri))
 
+(defn- cacheable?
+  "Can the ring request be permanently cached?"
+  [{:keys [uri query-string]}]
+  ;; match requests that are js/css and have a cache-busting query string
+  (and query-string (re-matches #"^/app/dist/.*\.(js|css)$" uri)))
 
 ;;; ------------------------------------------- AUTH & SESSION MANAGEMENT --------------------------------------------
 
@@ -168,6 +173,11 @@
    "Expires"        "Tue, 03 Jul 2001 06:00:00 GMT"
    "Last-Modified"  (du/format-date :rfc822)})
 
+ (defn- cache-far-future-headers
+   "Headers that tell browsers to cache a static resource for a long time."
+   []
+   {"Cache-Control" "public, max-age=31536000"})
+
 (def ^:private ^:const strict-transport-security-header
   "Tell browsers to only access this resource over HTTPS for the next year (prevent MTM attacks). (This only applies if
   the original request was HTTPS; if sent in response to an HTTP request, this is simply ignored)"
@@ -216,10 +226,12 @@
   (when-let [k (ssl-certificate-public-key)]
     {"Public-Key-Pins" (format "pin-sha256=\"base64==%s\"; max-age=31536000" k)}))
 
-(defn- security-headers [& {:keys [allow-iframes?]
-                            :or   {allow-iframes? false}}]
+(defn- security-headers [& {:keys [allow-iframes? allow-cache?]
+                            :or   {allow-iframes? false, allow-cache? false}}]
   (merge
-   (cache-prevention-headers)
+   (if allow-cache?
+     (cache-far-future-headers)
+     (cache-prevention-headers))
    strict-transport-security-header
    content-security-policy-header
    #_(public-key-pins-header)
@@ -239,7 +251,8 @@
   (fn [request]
     (let [response (handler request)]
       ;; add security headers to all responses, but allow iframes on public & embed responses
-      (update response :headers merge (security-headers :allow-iframes? ((some-fn public? embed?) request))))))
+      (update response :headers merge (security-headers :allow-iframes? ((some-fn public? embed?) request)
+                                                        :allow-cache?   (cacheable? request))))))
 
 (defn add-content-type
   "Add an appropriate Content-Type header to response if it doesn't already have one. Most responses should already
-- 
GitLab