diff --git a/config-templates/config.php b/config-templates/config.php
index f6de5cba9ef599b0a417640d3dda6d748a74177b..9d267be49d2f608b3e9b3d17d279ae3146b183f2 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -861,6 +861,31 @@ $config = [
      */
     'production' => true,
 
+    /*
+     * SimpleSAMLphp modules can host static resources which are served through PHP.
+     * The serving of the resources can be configured through these settings.
+     */
+    'assets' => [
+        /*
+         * These settings adjust the caching headers that are sent
+         * when serving static resources.
+         */
+        'caching' => [
+            /*
+             * Amount of seconds before the resource should be fetched again
+             */
+            'max_age' => 86400,
+            /*
+             * Calculate a checksum of every file and send it to the browser
+             * This allows the browser to avoid downloading assets again in situations
+             * where the Last-Modified header cannot be trusted,
+             * for example in cluster setups
+             *
+             * Defaults false
+             */
+            'etag' => false,
+        ],
+    ],
 
 
     /*********************
diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php
index e0bf063a5c4f59b7da97ed64927f250333936786..31e970bbb031e758a488bea57779fecd0dafd2da 100644
--- a/lib/SimpleSAML/Module.php
+++ b/lib/SimpleSAML/Module.php
@@ -264,12 +264,21 @@ class Module
             }
         }
 
+        $assetConfig = $config->getConfigItem('assets', new Configuration([], '[assets]'));
+        $cacheConfig = $assetConfig->getConfigItem('caching', new Configuration([], '[assets][caching]'));
         $response = new BinaryFileResponse($path);
-        $response->setCache(['public' => true, 'max_age' => 86400]);
-        $response->setExpires(new \DateTime(gmdate('D, j M Y H:i:s \G\M\T', time() + 10 * 60)));
-        $response->setLastModified(new \DateTime(gmdate('D, j M Y H:i:s \G\M\T', filemtime($path))));
+        $response->setCache([
+            // "public" allows response caching even if the request was authenticated,
+            // which is exactly what we want for static resources
+            'public' => true,
+            'max_age' => (string)$cacheConfig->getInteger('max_age', 86400)
+        ]);
+        $response->setAutoLastModified();
+        if ($cacheConfig->getBoolean('etag', false)) {
+            $response->setAutoEtag();
+        }
+        $response->isNotModified($request);
         $response->headers->set('Content-Type', $contentType);
-        $response->headers->set('Content-Length', sprintf('%u', filesize($path))); // force file size to an unsigned
         $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE);
         $response->prepare($request);
         return $response;