diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,9 @@
 # Changelog for wai-extra
 
+## 3.1.12
+
+* Added gzip caching based on `ETag` [#885](https://github.com/yesodweb/wai/pull/885):
+
 ## 3.1.11
 
 * Overhaul to `Network.Wai.Middleware.Gzip` [#880](https://github.com/yesodweb/wai/pull/880):
diff --git a/Network/Wai/Header.hs b/Network/Wai/Header.hs
--- a/Network/Wai/Header.hs
+++ b/Network/Wai/Header.hs
@@ -5,6 +5,7 @@
     , parseQValueList
     , replaceHeader
     , splitCommas
+    , trimWS
     ) where
 
 import Control.Monad (guard)
diff --git a/Network/Wai/Middleware/Gzip.hs b/Network/Wai/Middleware/Gzip.hs
--- a/Network/Wai/Middleware/Gzip.hs
+++ b/Network/Wai/Middleware/Gzip.hs
@@ -57,13 +57,13 @@
     hContentType,
     hUserAgent,
  )
-import Network.HTTP.Types.Header (hAcceptEncoding, hVary)
+import Network.HTTP.Types.Header (hAcceptEncoding, hETag, hVary)
 import Network.Wai
 import Network.Wai.Internal (Response (..))
 import System.Directory (createDirectoryIfMissing, doesFileExist)
 import qualified System.IO as IO
 
-import Network.Wai.Header (contentLength, parseQValueList, replaceHeader, splitCommas)
+import Network.Wai.Header (contentLength, parseQValueList, replaceHeader, splitCommas, trimWS)
 
 -- $howto
 --
@@ -170,6 +170,15 @@
       GzipCompress
     | -- | Compress files, caching the compressed version in the given directory.
       GzipCacheFolder FilePath
+    | -- | Takes the ETag response header into consideration when caching
+      -- files in the given folder. If there's no ETag header,
+      -- this setting is equivalent to 'GzipCacheFolder'.
+      --
+      -- N.B. Make sure the 'gzip' middleware is applied before
+      -- any 'Middleware' that will set the ETag header.
+      --
+      -- @since 3.1.12
+      GzipCacheETag FilePath
     | -- | If we use compression then try to use the filename with \".gz\"
       -- appended to it. If the file is missing then try next action.
       --
@@ -223,7 +232,11 @@
                 _ | not $ isCorrectMime (responseHeaders res) -> sendResponse res
                 -- Use static caching logic
                 (ResponseFile s hs file Nothing, GzipCacheFolder cache) ->
-                    compressFile s hs file cache sendResponse
+                    compressFile s hs file Nothing cache sendResponse
+                -- Use static caching logic with "ETag" signatures
+                (ResponseFile s hs file Nothing, GzipCacheETag cache) ->
+                    let mETag = lookup hETag hs
+                     in compressFile s hs file mETag cache sendResponse
                 -- Use streaming logic
                 _ -> compressE res sendResponse
         in runAction (res, gzipFiles set)
@@ -231,7 +244,7 @@
     isCorrectMime =
         maybe False (gzipCheckMime set) . lookup hContentType
     sendResponse = sendResponse' . mapResponseHeaders mAddVary
-    acceptEncoding = "Accept-Encoding"
+    acceptEncoding   = "Accept-Encoding"
     acceptEncodingLC = "accept-encoding"
     -- Instead of just adding a header willy-nilly, we check if
     -- "Vary" is already present, and add to it if not already included.
@@ -285,9 +298,8 @@
 minimumLength :: Integer
 minimumLength = 860
 
--- TODO: Add ETag functionality
-compressFile :: Status -> [Header] -> FilePath -> FilePath -> (Response -> IO a) -> IO a
-compressFile s hs file cache sendResponse = do
+compressFile :: Status -> [Header] -> FilePath -> Maybe S.ByteString -> FilePath -> (Response -> IO a) -> IO a
+compressFile s hs file mETag cache sendResponse = do
     e <- doesFileExist tmpfile
     if e
         then onSucc
@@ -330,8 +342,10 @@
             sendResponse $ responseFile s hs file Nothing
         | otherwise = throwIO e
 
+    -- If there's an ETag, use it as the suffix of the cached file.
+    eTag = maybe "" (map safe . S8.unpack . trimWS) mETag
+    tmpfile = cache ++ '/' : map safe file ++ eTag
 
-    tmpfile = cache ++ '/' : map safe file
     safe c
         | 'A' <= c && c <= 'Z' = c
         | 'a' <= c && c <= 'z' = c
diff --git a/wai-extra.cabal b/wai-extra.cabal
--- a/wai-extra.cabal
+++ b/wai-extra.cabal
@@ -1,5 +1,5 @@
 Name:                wai-extra
-Version:             3.1.11
+Version:             3.1.12
 Synopsis:            Provides some basic WAI handlers and middleware.
 description:
   Provides basic WAI handler and middleware functionality:
