diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,9 @@
 # wai-app-static changelog
 
+## 3.1.9
+
+* Added `NoCache` constructor to `MaxAge` [#977](https://github.com/yesodweb/wai/pull/977)
+
 ## 3.1.8
 
 * Added `NoStore` constructor to `MaxAge` [#938](https://github.com/yesodweb/wai/pull/938)
diff --git a/Network/Wai/Application/Static.hs b/Network/Wai/Application/Static.hs
--- a/Network/Wai/Application/Static.hs
+++ b/Network/Wai/Application/Static.hs
@@ -1,38 +1,42 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell, CPP #-}
-{-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE PatternGuards #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+
 -- | Static file serving for WAI.
-module Network.Wai.Application.Static
-    ( -- * WAI application
-      staticApp
-      -- ** Default Settings
-    , defaultWebAppSettings
-    , webAppSettingsWithLookup
-    , defaultFileServerSettings
-    , embeddedSettings
-      -- ** Settings
-    , StaticSettings
-    , ssLookupFile
-    , ssMkRedirect
-    , ssGetMimeType
-    , ssListing
-    , ssIndices
-    , ssMaxAge
-    , ssRedirectToIndex
-    , ssAddTrailingSlash
-    , ss404Handler
-    ) where
+module Network.Wai.Application.Static (
+    -- * WAI application
+    staticApp,
 
-import Prelude hiding (FilePath)
-import qualified Network.Wai as W
-import qualified Network.HTTP.Types as H
+    -- ** Default Settings
+    defaultWebAppSettings,
+    webAppSettingsWithLookup,
+    defaultFileServerSettings,
+    embeddedSettings,
+
+    -- ** Settings
+    StaticSettings,
+    ssLookupFile,
+    ssMkRedirect,
+    ssGetMimeType,
+    ssListing,
+    ssIndices,
+    ssMaxAge,
+    ssRedirectToIndex,
+    ssAddTrailingSlash,
+    ss404Handler,
+) where
+
+import Control.Monad.IO.Class (liftIO)
 import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as S8
 import qualified Data.ByteString.Lazy as L
 import Data.ByteString.Lazy.Char8 ()
-import Control.Monad.IO.Class (liftIO)
+import qualified Network.HTTP.Types as H
+import qualified Network.Wai as W
+import Prelude hiding (FilePath)
 
 import Data.ByteString.Builder (toLazyByteString)
 
@@ -42,51 +46,63 @@
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
 
-import Network.HTTP.Date (parseHTTPDate, epochTimeToHTTPDate, formatHTTPDate)
+import Network.HTTP.Date (epochTimeToHTTPDate, formatHTTPDate, parseHTTPDate)
 
-import WaiAppStatic.Types
+import Network.Mime (MimeType)
 import Util
-import WaiAppStatic.Storage.Filesystem
 import WaiAppStatic.Storage.Embedded
-import Network.Mime (MimeType)
+import WaiAppStatic.Storage.Filesystem
+import WaiAppStatic.Types
 
-data StaticResponse =
-      -- | Just the etag hash or Nothing for no etag hash
+data StaticResponse
+    = -- | Just the etag hash or Nothing for no etag hash
       Redirect Pieces (Maybe ByteString)
     | RawRedirect ByteString
     | NotFound
     | FileResponse File H.ResponseHeaders
     | NotModified
-    -- TODO: add file size
-    | SendContent MimeType L.ByteString
+    | -- TODO: add file size
+      SendContent MimeType L.ByteString
     | WaiResponse W.Response
 
-safeInit  :: [a] -> [a]
+safeInit :: [a] -> [a]
 safeInit [] = []
 safeInit xs = init xs
 
 filterButLast :: (a -> Bool) -> [a] -> [a]
 filterButLast _ [] = []
 filterButLast _ [x] = [x]
-filterButLast f (x:xs)
+filterButLast f (x : xs)
     | f x = x : filterButLast f xs
     | otherwise = filterButLast f xs
 
 -- | Serve an appropriate response for a folder request.
-serveFolder :: StaticSettings -> Pieces -> W.Request -> Folder -> IO StaticResponse
-serveFolder StaticSettings {..} pieces req folder =
+serveFolder
+    :: StaticSettings -> Pieces -> W.Request -> Folder -> IO StaticResponse
+serveFolder StaticSettings{..} pieces req folder =
     case ssListing of
-        Just _ | Just path <- addTrailingSlash req, ssAddTrailingSlash ->
-            return $ RawRedirect path
+        Just _
+            | Just path <- addTrailingSlash req
+            , ssAddTrailingSlash ->
+                return $ RawRedirect path
         Just listing -> do
             -- directory listings turned on, display it
             builder <- listing pieces folder
-            return $ WaiResponse $ W.responseBuilder H.status200
-                [ ("Content-Type", "text/html; charset=utf-8")
-                ] builder
-        Nothing -> return $ WaiResponse $ W.responseLBS H.status403
-            [ ("Content-Type", "text/plain")
-            ] "Directory listings disabled"
+            return $
+                WaiResponse $
+                    W.responseBuilder
+                        H.status200
+                        [ ("Content-Type", "text/html; charset=utf-8")
+                        ]
+                        builder
+        Nothing ->
+            return $
+                WaiResponse $
+                    W.responseLBS
+                        H.status403
+                        [ ("Content-Type", "text/plain")
+                        ]
+                        "Directory listings disabled"
 
 addTrailingSlash :: W.Request -> Maybe ByteString
 addTrailingSlash req
@@ -96,16 +112,18 @@
   where
     rp = W.rawPathInfo req
 
-checkPieces :: StaticSettings
-            -> Pieces                    -- ^ parsed request
-            -> W.Request
-            -> IO StaticResponse
+checkPieces
+    :: StaticSettings
+    -> Pieces
+    -- ^ parsed request
+    -> W.Request
+    -> IO StaticResponse
 -- If we have any empty pieces in the middle of the requested path, generate a
 -- redirect to get rid of them.
-checkPieces _ pieces _ | any (T.null . fromPiece) $ safeInit pieces =
-    return $ Redirect (filterButLast (not . T.null . fromPiece) pieces) Nothing
-
-checkPieces ss@StaticSettings {..} pieces req = do
+checkPieces _ pieces _
+    | any (T.null . fromPiece) $ safeInit pieces =
+        return $ Redirect (filterButLast (not . T.null . fromPiece) pieces) Nothing
+checkPieces ss@StaticSettings{..} pieces req = do
     res <- lookupResult
     case res of
         Left location -> return $ RawRedirect location
@@ -115,32 +133,35 @@
   where
     lookupResult :: IO (Either ByteString LookupResult)
     lookupResult = do
-      nonIndexResult <- ssLookupFile pieces
-      case nonIndexResult of
-          LRFile{} -> return $ Right nonIndexResult
-          _ -> do
-              eIndexResult <- lookupIndices (map (\ index -> dropLastIfNull pieces ++ [index]) ssIndices)
-              return $ case eIndexResult of
-                  Left redirect -> Left redirect
-                  Right indexResult -> case indexResult of
-                      LRNotFound -> Right nonIndexResult
-                      LRFile file | ssRedirectToIndex ->
-                          let relPath =
-                                  case reverse pieces of
-                                      -- Served at root
-                                      [] -> fromPiece $ fileName file
-                                      lastSegment:_ ->
-                                          case fromPiece lastSegment of
-                                              -- Ends with a trailing slash
-                                              "" -> fromPiece $ fileName file
-                                              -- Lacks a trailing slash
-                                              lastSegment' -> T.concat
-                                                  [ lastSegment'
-                                                  , "/"
-                                                  , fromPiece $ fileName file
-                                                  ]
-                           in Left $ TE.encodeUtf8 relPath
-                      _ -> Right indexResult
+        nonIndexResult <- ssLookupFile pieces
+        case nonIndexResult of
+            LRFile{} -> return $ Right nonIndexResult
+            _ -> do
+                eIndexResult <-
+                    lookupIndices (map (\index -> dropLastIfNull pieces ++ [index]) ssIndices)
+                return $ case eIndexResult of
+                    Left redirect -> Left redirect
+                    Right indexResult -> case indexResult of
+                        LRNotFound -> Right nonIndexResult
+                        LRFile file
+                            | ssRedirectToIndex ->
+                                let relPath =
+                                        case reverse pieces of
+                                            -- Served at root
+                                            [] -> fromPiece $ fileName file
+                                            lastSegment : _ ->
+                                                case fromPiece lastSegment of
+                                                    -- Ends with a trailing slash
+                                                    "" -> fromPiece $ fileName file
+                                                    -- Lacks a trailing slash
+                                                    lastSegment' ->
+                                                        T.concat
+                                                            [ lastSegment'
+                                                            , "/"
+                                                            , fromPiece $ fileName file
+                                                            ]
+                                 in Left $ TE.encodeUtf8 relPath
+                        _ -> Right indexResult
 
     lookupIndices :: [Pieces] -> IO (Either ByteString LookupResult)
     lookupIndices (x : xs) = do
@@ -153,14 +174,14 @@
     lookupIndices [] = return $ Right LRNotFound
 
 serveFile :: StaticSettings -> W.Request -> File -> IO StaticResponse
-serveFile StaticSettings {..} req file
+serveFile StaticSettings{..} req file
     -- First check etag values, if turned on
     | ssUseHash = do
         mHash <- fileGetHash file
+        -- FIXME: Doesn't support multiple hashes in 'If-None-Match' header
         case (mHash, lookup "if-none-match" $ W.requestHeaders req) of
             -- if-none-match matches the actual hash, return a 304
             (Just hash, Just lastHash) | hash == lastHash -> return NotModified
-
             -- Didn't match, but we have a hash value. Send the file contents
             -- with an ETag header.
             --
@@ -172,7 +193,6 @@
             -- > interoperating with older intermediaries that might not implement
             -- > If-None-Match.
             (Just hash, _) -> respond [("ETag", hash)]
-
             -- No hash value available, fall back to last modified support.
             (Nothing, _) -> lastMod
     -- etag turned off, so jump straight to last modified
@@ -187,10 +207,8 @@
             -- Question: should the comparison be, date <= lastSent?
             (Just mdate, Just lastSent)
                 | mdate == lastSent -> return NotModified
-
             -- Did not match, but we have a new last-modified header
             (Just mdate, _) -> respond [("last-modified", formatHTTPDate mdate)]
-
             -- No modification time available
             (Nothing, _) -> respond []
 
@@ -215,12 +233,14 @@
         MaxAgeSeconds i -> (:) ("Cache-Control", maxAgeValue i)
         MaxAgeForever -> (:) ("Cache-Control", maxAgeValue oneYear)
         NoStore -> (:) ("Cache-Control", "no-store")
+        NoCache -> (:) ("Cache-Control", "no-cache")
     headerExpires =
-      case maxage of
-        NoMaxAge        -> id
-        MaxAgeSeconds _ -> id -- FIXME
-        MaxAgeForever   -> (:) ("Expires", "Thu, 31 Dec 2037 23:55:55 GMT")
-        NoStore -> id
+        case maxage of
+            NoMaxAge -> id
+            MaxAgeSeconds _ -> id -- FIXME
+            MaxAgeForever -> (:) ("Expires", "Thu, 31 Dec 2037 23:55:55 GMT")
+            NoStore -> id
+            NoCache -> id
 
 -- | Turn a @StaticSettings@ into a WAI application.
 staticApp :: StaticSettings -> W.Application
@@ -228,61 +248,84 @@
 
 staticAppPieces :: StaticSettings -> [Text] -> W.Application
 staticAppPieces _ _ req sendResponse
-    | notElem (W.requestMethod req) ["GET", "HEAD"] = sendResponse $ W.responseLBS
-        H.status405
-        [("Content-Type", "text/plain")]
-        "Only GET or HEAD is supported"
-staticAppPieces _ [".hidden", "folder.png"] _ sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "image/png")] $ L.fromChunks [$(makeRelativeToProject "images/folder.png" >>= embedFile)]
-staticAppPieces _ [".hidden", "haskell.png"] _ sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "image/png")] $ L.fromChunks [$(makeRelativeToProject "images/haskell.png" >>= embedFile)]
+    | notElem (W.requestMethod req) ["GET", "HEAD"] =
+        sendResponse $
+            W.responseLBS
+                H.status405
+                [("Content-Type", "text/plain")]
+                "Only GET or HEAD is supported"
+staticAppPieces _ [".hidden", "folder.png"] _ sendResponse =
+    sendResponse $
+        W.responseLBS H.status200 [("Content-Type", "image/png")] $
+            L.fromChunks [$(makeRelativeToProject "images/folder.png" >>= embedFile)]
+staticAppPieces _ [".hidden", "haskell.png"] _ sendResponse =
+    sendResponse $
+        W.responseLBS H.status200 [("Content-Type", "image/png")] $
+            L.fromChunks [$(makeRelativeToProject "images/haskell.png" >>= embedFile)]
 staticAppPieces ss rawPieces req sendResponse = liftIO $ do
     case toPieces rawPieces of
         Just pieces -> checkPieces ss pieces req >>= response
-        Nothing -> sendResponse $ W.responseLBS H.status403
-            [ ("Content-Type", "text/plain")
-            ] "Forbidden"
+        Nothing ->
+            sendResponse $
+                W.responseLBS
+                    H.status403
+                    [ ("Content-Type", "text/plain")
+                    ]
+                    "Forbidden"
   where
     response :: StaticResponse -> IO W.ResponseReceived
     response (FileResponse file ch) = do
         mimetype <- ssGetMimeType ss file
         -- let filesize = fileGetSize file
-        let headers = ("Content-Type", mimetype)
+        let headers =
+                ("Content-Type", mimetype)
                     -- Let Warp provide the content-length, since it takes
                     -- range requests into account
                     -- : ("Content-Length", S8.pack $ show filesize)
                     : ch
         sendResponse $ fileToResponse file H.status200 headers
-
     response NotModified =
-            sendResponse $ W.responseLBS H.status304 [] ""
-
+        sendResponse $ W.responseLBS H.status304 [] ""
     response (SendContent mt lbs) = do
-            -- TODO: set caching headers
-            sendResponse $ W.responseLBS H.status200
+        -- TODO: set caching headers
+        sendResponse $
+            W.responseLBS
+                H.status200
                 [ ("Content-Type", mt)
-                  -- TODO: set Content-Length
-                ] lbs
-
+                -- TODO: set Content-Length
+                ]
+                lbs
     response (Redirect pieces' mHash) = do
-            let loc = ssMkRedirect ss pieces' $ L.toStrict $ toLazyByteString (H.encodePathSegments $ map fromPiece pieces')
-            let qString = case mHash of
-                  Just hash -> replace "etag" (Just hash) (W.queryString req)
-                  Nothing   -> remove "etag" (W.queryString req)
+        let loc =
+                ssMkRedirect ss pieces' $
+                    L.toStrict $
+                        toLazyByteString (H.encodePathSegments $ map fromPiece pieces')
+        let qString = case mHash of
+                Just hash -> replace "etag" (Just hash) (W.queryString req)
+                Nothing -> remove "etag" (W.queryString req)
 
-            sendResponse $ W.responseLBS H.status302
+        sendResponse $
+            W.responseLBS
+                H.status302
                 [ ("Content-Type", "text/plain")
                 , ("Location", S8.append loc $ H.renderQuery True qString)
-                ] "Redirect"
-
+                ]
+                "Redirect"
     response (RawRedirect path) =
-            sendResponse $ W.responseLBS H.status302
+        sendResponse $
+            W.responseLBS
+                H.status302
                 [ ("Content-Type", "text/plain")
                 , ("Location", path)
-                ] "Redirect"
-
+                ]
+                "Redirect"
     response NotFound = case ss404Handler ss of
         Just app -> app req sendResponse
-        Nothing  -> sendResponse $ W.responseLBS H.status404
-                        [ ("Content-Type", "text/plain")
-                        ] "File not found"
-
+        Nothing ->
+            sendResponse $
+                W.responseLBS
+                    H.status404
+                    [ ("Content-Type", "text/plain")
+                    ]
+                    "File not found"
     response (WaiResponse r) = sendResponse r
diff --git a/Util.hs b/Util.hs
--- a/Util.hs
+++ b/Util.hs
@@ -1,28 +1,32 @@
-{-# LANGUAGE OverloadedStrings, ViewPatterns #-}
-module Util
-    ( relativeDirFromPieces
-    , defaultMkRedirect
-    , replace
-    , remove
-    , dropLastIfNull
-    ) where
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ViewPatterns #-}
 
-import WaiAppStatic.Types
-import qualified Data.Text as T
+module Util (
+    relativeDirFromPieces,
+    defaultMkRedirect,
+    replace,
+    remove,
+    dropLastIfNull,
+) where
+
 import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as S8
+import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
+import WaiAppStatic.Types
 
 -- alist helper functions
 replace :: Eq a => a -> b -> [(a, b)] -> [(a, b)]
-replace k v [] = [(k,v)]
-replace k v (x:xs) | fst x == k = (k,v):xs
-                   | otherwise  = x:replace k v xs
+replace k v [] = [(k, v)]
+replace k v (x : xs)
+    | fst x == k = (k, v) : xs
+    | otherwise = x : replace k v xs
 
 remove :: Eq a => a -> [(a, b)] -> [(a, b)]
 remove _ [] = []
-remove k (x:xs) | fst x == k = xs
-                  | otherwise  = x:remove k xs
+remove k (x : xs)
+    | fst x == k = xs
+    | otherwise = x : remove k xs
 
 -- | Turn a list of pieces into a relative path to the root folder.
 relativeDirFromPieces :: Pieces -> T.Text
@@ -31,8 +35,10 @@
 -- | Construct redirects with relative paths.
 defaultMkRedirect :: Pieces -> ByteString -> S8.ByteString
 defaultMkRedirect pieces newPath
-    | S8.null newPath || S8.null relDir ||
-      S8.last relDir /= '/' || S8.head newPath /= '/' =
+    | S8.null newPath
+        || S8.null relDir
+        || S8.last relDir /= '/'
+        || S8.head newPath /= '/' =
         relDir `S8.append` newPath
     | otherwise = relDir `S8.append` S8.tail newPath
   where
diff --git a/WaiAppStatic/CmdLine.hs b/WaiAppStatic/CmdLine.hs
--- a/WaiAppStatic/CmdLine.hs
+++ b/WaiAppStatic/CmdLine.hs
@@ -1,29 +1,40 @@
-{-# LANGUAGE CPP, RecordWildCards #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE RecordWildCards #-}
+
 -- | Command line version of wai-app-static, used for the warp-static server.
-module WaiAppStatic.CmdLine
-    ( runCommandLine
-    , Args (..)
-    ) where
+module WaiAppStatic.CmdLine (
+    runCommandLine,
+    Args (..),
+) where
 
-import Network.Wai (Middleware)
-import Network.Wai.Application.Static (staticApp, defaultFileServerSettings)
-import Network.Wai.Handler.Warp
-    ( runSettings, defaultSettings, setHost, setPort
-    )
-import Options.Applicative
-import Text.Printf (printf)
-import System.Directory (canonicalizePath)
+import Control.Arrow (second, (***))
 import Control.Monad (unless)
-import Network.Wai.Middleware.RequestLogger (logStdout)
-import Network.Wai.Middleware.Gzip
-import qualified Data.Map as Map
 import qualified Data.ByteString.Char8 as S8
-import Data.Text (pack)
-import Data.String (fromString)
-import Network.Mime (defaultMimeMap, mimeByExt, defaultMimeType)
-import WaiAppStatic.Types (ssIndices, toPiece, ssGetMimeType, fileName, fromPiece)
+import qualified Data.Map as Map
 import Data.Maybe (mapMaybe)
-import Control.Arrow (second, (***))
+import Data.String (fromString)
+import Data.Text (pack)
+import Network.Mime (defaultMimeMap, defaultMimeType, mimeByExt)
+import Network.Wai (Middleware)
+import Network.Wai.Application.Static (defaultFileServerSettings, staticApp)
+import Network.Wai.Handler.Warp (
+    defaultSettings,
+    runSettings,
+    setHost,
+    setPort,
+ )
+import Network.Wai.Middleware.Gzip
+import Network.Wai.Middleware.RequestLogger (logStdout)
+import Options.Applicative
+import System.Directory (canonicalizePath)
+import Text.Printf (printf)
+import WaiAppStatic.Types (
+    fileName,
+    fromPiece,
+    ssGetMimeType,
+    ssIndices,
+    toPiece,
+ )
 #if __GLASGOW_HASKELL__ < 804
 import Data.Monoid ((<>))
 #endif
@@ -47,44 +58,59 @@
 #endif
 
 args :: Parser Args
-args = Args
-    <$> strOption
+args =
+    Args
+        <$> strOption
             ( long "docroot"
-           <> short 'd'
-           <> metavar "DOCROOT"
-           <> value "."
-           <> help "directory containing files to serve")
-    <*> (defIndex <$> many (strOption
-            ( long "index"
-           <> short 'i'
-           <> metavar "INDEX"
-           <> help "index files to serve when a directory is required"
-            )))
-    <*> option'
+                <> short 'd'
+                <> metavar "DOCROOT"
+                <> value "."
+                <> help "directory containing files to serve"
+            )
+        <*> ( defIndex
+                <$> many
+                    ( strOption
+                        ( long "index"
+                            <> short 'i'
+                            <> metavar "INDEX"
+                            <> help "index files to serve when a directory is required"
+                        )
+                    )
+            )
+        <*> option'
             ( long "port"
-           <> short 'p'
-           <> metavar "PORT"
-           <> value 3000)
-    <*> switch
+                <> short 'p'
+                <> metavar "PORT"
+                <> value 3000
+            )
+        <*> switch
             ( long "noindex"
-           <> short 'n')
-    <*> switch
+                <> short 'n'
+            )
+        <*> switch
             ( long "quiet"
-           <> short 'q')
-    <*> switch
+                <> short 'q'
+            )
+        <*> switch
             ( long "verbose"
-           <> short 'v')
-    <*> many (toPair <$> strOption
-            ( long "mime"
-           <> short 'm'
-           <> metavar "MIME"
-           <> help "extra file extension/mime type mappings"))
-    <*> strOption
+                <> short 'v'
+            )
+        <*> many
+            ( toPair
+                <$> strOption
+                    ( long "mime"
+                        <> short 'm'
+                        <> metavar "MIME"
+                        <> help "extra file extension/mime type mappings"
+                    )
+            )
+        <*> strOption
             ( long "host"
-           <> short 'h'
-           <> metavar "HOST"
-           <> value "*"
-           <> help "interface to bind to, special values: *, *4, *6")
+                <> short 'h'
+                <> metavar "HOST"
+                <> value "*"
+                <> help "interface to bind to, special values: *, *4, *6"
+            )
   where
     toPair = second (drop 1) . break (== '=')
     defIndex [] = ["index.html", "index.htm"]
@@ -95,29 +121,39 @@
 -- Since 2.0.1
 runCommandLine :: (Args -> Middleware) -> IO ()
 runCommandLine middleware = do
-    clArgs@Args {..} <- execParser $ info (helperOption <*> args) fullDesc
+    clArgs@Args{..} <- execParser $ info (helperOption <*> args) fullDesc
     let mime' = map (pack *** S8.pack) mime
     let mimeMap = Map.fromList mime' `Map.union` defaultMimeMap
     docroot' <- canonicalizePath docroot
-    unless quiet $ printf "Serving directory %s on port %d with %s index files.\n" docroot' port (if noindex then "no" else show index)
-    let middle = gzip def { gzipFiles = GzipCompress }
-               . (if verbose then logStdout else id)
-               . middleware clArgs
+    unless quiet $
+        printf
+            "Serving directory %s on port %d with %s index files.\n"
+            docroot'
+            port
+            (if noindex then "no" else show index)
+    let middle =
+            gzip def{gzipFiles = GzipCompress}
+                . (if verbose then logStdout else id)
+                . middleware clArgs
     runSettings
-        ( setPort port
-        $ setHost (fromString host)
-          defaultSettings
+        ( setPort port $
+            setHost
+                (fromString host)
+                defaultSettings
         )
-        $ middle $ staticApp (defaultFileServerSettings $ fromString docroot)
-        { ssIndices = if noindex then [] else mapMaybe (toPiece . pack) index
-        , ssGetMimeType = return . mimeByExt mimeMap defaultMimeType . fromPiece . fileName
-        }
-    where
-      helperOption :: Parser (a -> a)
-      helperOption =
+        $ middle
+        $ staticApp
+            (defaultFileServerSettings $ fromString docroot)
+                { ssIndices = if noindex then [] else mapMaybe (toPiece . pack) index
+                , ssGetMimeType =
+                    return . mimeByExt mimeMap defaultMimeType . fromPiece . fileName
+                }
+  where
+    helperOption :: Parser (a -> a)
+    helperOption =
 #if MIN_VERSION_optparse_applicative(0,16,0)
         abortOption (ShowHelpText Nothing) $
 #else
         abortOption ShowHelpText $
 #endif
-        mconcat [long "help", help "Show this help text", hidden]
+            mconcat [long "help", help "Show this help text", hidden]
diff --git a/WaiAppStatic/Listing.hs b/WaiAppStatic/Listing.hs
--- a/WaiAppStatic/Listing.hs
+++ b/WaiAppStatic/Listing.hs
@@ -1,16 +1,17 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ViewPatterns #-}
-module WaiAppStatic.Listing
-    ( defaultListing
-    ) where
 
-import qualified Text.Blaze.Html5.Attributes as A
-import qualified Text.Blaze.Html5            as H
-import           Text.Blaze                  ((!))
+module WaiAppStatic.Listing (
+    defaultListing,
+) where
+
 import qualified Data.Text as T
 import Data.Time
 import Data.Time.Clock.POSIX
+import Text.Blaze ((!))
+import qualified Text.Blaze.Html5 as H
+import qualified Text.Blaze.Html5.Attributes as A
 import WaiAppStatic.Types
 #if !MIN_VERSION_time(1,5,0)
 import System.Locale (defaultTimeLocale)
@@ -28,33 +29,37 @@
     let isTop = null pieces || map Just pieces == [toPiece ""]
     let fps'' :: [Either FolderName File]
         fps'' = (if isTop then id else (Left (unsafeToPiece "") :)) contents -- FIXME emptyParentFolder feels like a bit of a hack
-    return $ HU.renderHtmlBuilder
-           $ H.html $ do
-             H.head $ do
-                 let title = T.intercalate "/" $ map fromPiece pieces
-                 let title' = if T.null title then "root folder" else title
-                 H.title $ H.toHtml title'
-                 H.style $ H.toHtml $ unlines [ "table { margin: 0 auto; width: 760px; border-collapse: collapse; font-family: 'sans-serif'; }"
-                                              , "table, th, td { border: 1px solid #353948; }"
-                                              , "td.size { text-align: right; font-size: 0.7em; width: 50px }"
-                                              , "td.date { text-align: right; font-size: 0.7em; width: 130px }"
-                                              , "td { padding-right: 1em; padding-left: 1em; }"
-                                              , "th.first { background-color: white; width: 24px }"
-                                              , "td.first { padding-right: 0; padding-left: 0; text-align: center }"
-                                              , "tr { background-color: white; }"
-                                              , "tr.alt { background-color: #A3B5BA}"
-                                              , "th { background-color: #3C4569; color: white; font-size: 1.125em; }"
-                                              , "h1 { width: 760px; margin: 1em auto; font-size: 1em; font-family: sans-serif }"
-                                              , "img { width: 20px }"
-                                              , "a { text-decoration: none }"
-                                              ]
-             H.body $ do
-                 let hasTrailingSlash =
-                        case map fromPiece $ reverse pieces of
-                            "":_ -> True
-                            _ -> False
-                 H.h1 $ showFolder' hasTrailingSlash $ filter (not . T.null . fromPiece) pieces
-                 renderDirectoryContentsTable (map fromPiece pieces) haskellSrc folderSrc fps''
+    return $
+        HU.renderHtmlBuilder $
+            H.html $ do
+                H.head $ do
+                    let title = T.intercalate "/" $ map fromPiece pieces
+                    let title' = if T.null title then "root folder" else title
+                    H.title $ H.toHtml title'
+                    H.style $
+                        H.toHtml $
+                            unlines
+                                [ "table { margin: 0 auto; width: 760px; border-collapse: collapse; font-family: 'sans-serif'; }"
+                                , "table, th, td { border: 1px solid #353948; }"
+                                , "td.size { text-align: right; font-size: 0.7em; width: 50px }"
+                                , "td.date { text-align: right; font-size: 0.7em; width: 130px }"
+                                , "td { padding-right: 1em; padding-left: 1em; }"
+                                , "th.first { background-color: white; width: 24px }"
+                                , "td.first { padding-right: 0; padding-left: 0; text-align: center }"
+                                , "tr { background-color: white; }"
+                                , "tr.alt { background-color: #A3B5BA}"
+                                , "th { background-color: #3C4569; color: white; font-size: 1.125em; }"
+                                , "h1 { width: 760px; margin: 1em auto; font-size: 1em; font-family: sans-serif }"
+                                , "img { width: 20px }"
+                                , "a { text-decoration: none }"
+                                ]
+                H.body $ do
+                    let hasTrailingSlash =
+                            case map fromPiece $ reverse pieces of
+                                "" : _ -> True
+                                _ -> False
+                    H.h1 $ showFolder' hasTrailingSlash $ filter (not . T.null . fromPiece) pieces
+                    renderDirectoryContentsTable (map fromPiece pieces) haskellSrc folderSrc fps''
   where
     image x = T.unpack $ T.concat [relativeDirFromPieces pieces, ".hidden/", x, ".png"]
     folderSrc = image "folder"
@@ -69,7 +74,7 @@
     showFolder :: Bool -> Pieces -> H.Html
     showFolder _ [] = "/" -- won't happen
     showFolder _ [x] = H.toHtml $ showName $ fromPiece x
-    showFolder hasTrailingSlash (x:xs) = do
+    showFolder hasTrailingSlash (x : xs) = do
         let len = length xs - (if hasTrailingSlash then 0 else 1)
             href
                 | len == 0 = "."
@@ -86,64 +91,72 @@
 -- a new page template to wrap around this HTML.
 --
 -- see also: 'getMetaData', 'renderDirectoryContents'
-renderDirectoryContentsTable :: [T.Text] -- ^ requested path info
-                             -> String
-                             -> String
-                             -> [Either FolderName File]
-                             -> H.Html
+renderDirectoryContentsTable
+    :: [T.Text]
+    -- ^ requested path info
+    -> String
+    -> String
+    -> [Either FolderName File]
+    -> H.Html
 renderDirectoryContentsTable pathInfo' haskellSrc folderSrc fps =
-           H.table $ do H.thead $ do H.th ! A.class_ "first" $ H.img ! A.src (H.toValue haskellSrc)
-                                     H.th "Name"
-                                     H.th "Modified"
-                                     H.th "Size"
-                        H.tbody $ mapM_ mkRow (zip (sortBy sortMD fps) $ cycle [False, True])
-    where
-      sortMD :: Either FolderName File -> Either FolderName File -> Ordering
-      sortMD Left{} Right{} = LT
-      sortMD Right{} Left{} = GT
-      sortMD (Left a) (Left b) = compare a b
-      sortMD (Right a) (Right b) = compare (fileName a) (fileName b)
+    H.table $ do
+        H.thead $ do
+            H.th ! A.class_ "first" $ H.img ! A.src (H.toValue haskellSrc)
+            H.th "Name"
+            H.th "Modified"
+            H.th "Size"
+        H.tbody $ mapM_ mkRow (zip (sortBy sortMD fps) $ cycle [False, True])
+  where
+    sortMD :: Either FolderName File -> Either FolderName File -> Ordering
+    sortMD Left{} Right{} = LT
+    sortMD Right{} Left{} = GT
+    sortMD (Left a) (Left b) = compare a b
+    sortMD (Right a) (Right b) = compare (fileName a) (fileName b)
 
-      mkRow :: (Either FolderName File, Bool) -> H.Html
-      mkRow (md, alt) =
-          (if alt then (! A.class_ "alt") else id) $
-          H.tr $ do
-                   H.td ! A.class_ "first"
-                        $ case md of
-                            Left{} -> H.img ! A.src (H.toValue folderSrc)
-                                            ! A.alt "Folder"
-                            Right{} -> return ()
-                   let name =
-                           case either id fileName md of
-                               (fromPiece -> "") -> unsafeToPiece ".."
-                               x -> x
-                   let href = addCurrentDir $ fromPiece name
-                       addCurrentDir x =
-                           case reverse pathInfo' of
-                               "":_ -> x -- has a trailing slash
-                               [] -> x -- at the root
-                               currentDir:_ -> T.concat [currentDir, "/", x]
-                   H.td (H.a ! A.href (H.toValue href) $ H.toHtml $ fromPiece name)
-                   H.td ! A.class_ "date" $ H.toHtml $
-                       case md of
-                           Right File { fileGetModified = Just t } ->
-                                   formatCalendarTime defaultTimeLocale "%d-%b-%Y %X" t
-                           _ -> ""
-                   H.td ! A.class_ "size" $ H.toHtml $
-                       case md of
-                           Right File { fileGetSize = s } -> prettyShow s
-                           Left{} -> ""
-      formatCalendarTime a b c =  formatTime a b $ posixSecondsToUTCTime (realToFrac c :: POSIXTime)
-      prettyShow x
+    mkRow :: (Either FolderName File, Bool) -> H.Html
+    mkRow (md, alt) =
+        (if alt then (! A.class_ "alt") else id) $
+            H.tr $ do
+                H.td ! A.class_ "first" $
+                    case md of
+                        Left{} ->
+                            H.img
+                                ! A.src (H.toValue folderSrc)
+                                ! A.alt "Folder"
+                        Right{} -> return ()
+                let name =
+                        case either id fileName md of
+                            (fromPiece -> "") -> unsafeToPiece ".."
+                            x -> x
+                let href = addCurrentDir $ fromPiece name
+                    addCurrentDir x =
+                        case reverse pathInfo' of
+                            "" : _ -> x -- has a trailing slash
+                            [] -> x -- at the root
+                            currentDir : _ -> T.concat [currentDir, "/", x]
+                H.td (H.a ! A.href (H.toValue href) $ H.toHtml $ fromPiece name)
+                H.td ! A.class_ "date" $
+                    H.toHtml $
+                        case md of
+                            Right File{fileGetModified = Just t} ->
+                                formatCalendarTime defaultTimeLocale "%d-%b-%Y %X" t
+                            _ -> ""
+                H.td ! A.class_ "size" $
+                    H.toHtml $
+                        case md of
+                            Right File{fileGetSize = s} -> prettyShow s
+                            Left{} -> ""
+    formatCalendarTime a b c = formatTime a b $ posixSecondsToUTCTime (realToFrac c :: POSIXTime)
+    prettyShow x
         | x > 1024 = prettyShowK $ x `div` 1024
         | otherwise = addCommas "B" x
-      prettyShowK x
+    prettyShowK x
         | x > 1024 = prettyShowM $ x `div` 1024
         | otherwise = addCommas "KB" x
-      prettyShowM x
+    prettyShowM x
         | x > 1024 = prettyShowG $ x `div` 1024
         | otherwise = addCommas "MB" x
-      prettyShowG x = addCommas "GB" x
-      addCommas s = (++ (' ' : s)) . reverse . addCommas' . reverse . show
-      addCommas' (a:b:c:d:e) = a : b : c : ',' : addCommas' (d : e)
-      addCommas' x = x
+    prettyShowG x = addCommas "GB" x
+    addCommas s = (++ (' ' : s)) . reverse . addCommas' . reverse . show
+    addCommas' (a : b : c : d : e) = a : b : c : ',' : addCommas' (d : e)
+    addCommas' x = x
diff --git a/WaiAppStatic/Storage/Embedded.hs b/WaiAppStatic/Storage/Embedded.hs
--- a/WaiAppStatic/Storage/Embedded.hs
+++ b/WaiAppStatic/Storage/Embedded.hs
@@ -1,12 +1,12 @@
-module WaiAppStatic.Storage.Embedded(
+module WaiAppStatic.Storage.Embedded (
     -- * Basic
-      embeddedSettings
+    embeddedSettings,
 
     -- * Template Haskell
-    , Etag
-    , EmbeddableEntry(..)
-    , mkSettings
-    ) where
+    Etag,
+    EmbeddableEntry (..),
+    mkSettings,
+) where
 
 import WaiAppStatic.Storage.Embedded.Runtime
 import WaiAppStatic.Storage.Embedded.TH
diff --git a/WaiAppStatic/Storage/Embedded/Runtime.hs b/WaiAppStatic/Storage/Embedded/Runtime.hs
--- a/WaiAppStatic/Storage/Embedded/Runtime.hs
+++ b/WaiAppStatic/Storage/Embedded/Runtime.hs
@@ -1,21 +1,22 @@
 {-# LANGUAGE CPP #-}
+
 -- | Lookup files stored in memory instead of from the filesystem.
-module WaiAppStatic.Storage.Embedded.Runtime
-    ( -- * Settings
-      embeddedSettings
-    ) where
+module WaiAppStatic.Storage.Embedded.Runtime (
+    -- * Settings
+    embeddedSettings,
+) where
 
-import WaiAppStatic.Types
+import Control.Arrow (second, (&&&))
 import Data.ByteString (ByteString)
-import Control.Arrow ((&&&), second)
-import Data.List (groupBy, sortBy)
+import qualified Data.ByteString as S
 import Data.ByteString.Builder (byteString)
-import qualified Network.Wai as W
-import qualified Data.Map as Map
 import Data.Function (on)
-import qualified Data.Text as T
+import Data.List (groupBy, sortBy)
+import qualified Data.Map as Map
 import Data.Ord
-import qualified Data.ByteString as S
+import qualified Data.Text as T
+import qualified Network.Wai as W
+import WaiAppStatic.Types
 #ifdef MIN_VERSION_crypton
 import Crypto.Hash (hash, MD5, Digest)
 import Data.ByteArray.Encoding
@@ -23,14 +24,15 @@
 import Crypto.Hash.MD5 (hash)
 import Data.ByteString.Base64 (encode)
 #endif
-import WaiAppStatic.Storage.Filesystem (defaultFileServerSettings)
 import System.FilePath (isPathSeparator)
+import WaiAppStatic.Storage.Filesystem (defaultFileServerSettings)
 
 -- | Serve the list of path/content pairs directly from memory.
 embeddedSettings :: [(Prelude.FilePath, ByteString)] -> StaticSettings
-embeddedSettings files = (defaultFileServerSettings $ error "unused")
-    { ssLookupFile = embeddedLookup $ toEmbedded files
-    }
+embeddedSettings files =
+    (defaultFileServerSettings $ error "unused")
+        { ssLookupFile = embeddedLookup $ toEmbedded files
+        }
 
 type Embedded = Map.Map Piece EmbeddedEntry
 
@@ -40,10 +42,10 @@
 embeddedLookup root pieces =
     return $ elookup pieces root
   where
-    elookup  :: Pieces -> Embedded -> LookupResult
+    elookup :: Pieces -> Embedded -> LookupResult
     elookup [] x = LRFolder $ Folder $ fmap toEntry $ Map.toList x
     elookup [p] x | T.null (fromPiece p) = elookup [] x
-    elookup (p:ps) x =
+    elookup (p : ps) x =
         case Map.lookup p x of
             Nothing -> LRNotFound
             Just (EEFile f) ->
@@ -54,13 +56,15 @@
 
 toEntry :: (Piece, EmbeddedEntry) -> Either FolderName File
 toEntry (name, EEFolder{}) = Left name
-toEntry (name, EEFile bs) = Right File
-    { fileGetSize = fromIntegral $ S.length bs
-    , fileToResponse = \s h -> W.responseBuilder s h $ byteString bs
-    , fileName = name
-    , fileGetHash = return $ Just $ runHash bs
-    , fileGetModified = Nothing
-    }
+toEntry (name, EEFile bs) =
+    Right
+        File
+            { fileGetSize = fromIntegral $ S.length bs
+            , fileToResponse = \s h -> W.responseBuilder s h $ byteString bs
+            , fileName = name
+            , fileGetHash = return $ Just $ runHash bs
+            , fileGetModified = Nothing
+            }
 
 toEmbedded :: [(Prelude.FilePath, ByteString)] -> Embedded
 toEmbedded fps =
@@ -91,13 +95,14 @@
     go' x = EEFolder $ go $ filter (\y -> not $ null $ fst y) x
 
 bsToFile :: Piece -> ByteString -> File
-bsToFile name bs = File
-    { fileGetSize = fromIntegral $ S.length bs
-    , fileToResponse = \s h -> W.responseBuilder s h $ byteString bs
-    , fileName = name
-    , fileGetHash = return $ Just $ runHash bs
-    , fileGetModified = Nothing
-    }
+bsToFile name bs =
+    File
+        { fileGetSize = fromIntegral $ S.length bs
+        , fileToResponse = \s h -> W.responseBuilder s h $ byteString bs
+        , fileName = name
+        , fileGetHash = return $ Just $ runHash bs
+        , fileGetModified = Nothing
+        }
 
 runHash :: ByteString -> ByteString
 #ifdef MIN_VERSION_crypton
diff --git a/WaiAppStatic/Storage/Embedded/TH.hs b/WaiAppStatic/Storage/Embedded/TH.hs
--- a/WaiAppStatic/Storage/Embedded/TH.hs
+++ b/WaiAppStatic/Storage/Embedded/TH.hs
@@ -1,22 +1,27 @@
-{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings, MagicHash, CPP #-}
-module WaiAppStatic.Storage.Embedded.TH(
-    Etag
-  , EmbeddableEntry(..)
-  , mkSettings
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module WaiAppStatic.Storage.Embedded.TH (
+    Etag,
+    EmbeddableEntry (..),
+    mkSettings,
 ) where
 
-import Data.ByteString.Builder.Extra (byteStringInsert)
 import Codec.Compression.GZip (compress)
+import qualified Data.ByteString as B
+import Data.ByteString.Builder.Extra (byteStringInsert)
+import qualified Data.ByteString.Lazy as BL
 import Data.ByteString.Unsafe (unsafePackAddressLen)
 import Data.Either (lefts, rights)
-import GHC.Exts (Int(..))
+import GHC.Exts (Int (..))
 import Language.Haskell.TH
 import Network.Mime (MimeType, defaultMimeLookup)
 import System.IO.Unsafe (unsafeDupablePerformIO)
-import WaiAppStatic.Types
 import WaiAppStatic.Storage.Filesystem (defaultWebAppSettings)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as BL
+import WaiAppStatic.Types
 #if !MIN_VERSION_template_haskell(2, 8, 0)
 import qualified Data.ByteString.Char8 as B8
 import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -33,35 +38,37 @@
 type Etag = T.Text
 
 -- | Used at compile time to hold data about an entry to embed into the compiled executable.
-data EmbeddableEntry = EmbeddableEntry {
-    eLocation :: T.Text        -- ^ The location where this resource should be served from.  The
-                               --   location can contain forward slashes (/) to simulate directories,
-                               --   but must not end with a forward slash.
-  , eMimeType :: MimeType      -- ^ The mime type.
-  , eContent  :: Either (Etag, BL.ByteString) ExpQ
-                    -- ^ The content itself.  The content can be given as a tag and bytestring,
-                    --   in which case the content will be embedded directly into the execuatble.
-                    --   Alternatively, the content can be given as a template haskell expression
-                    --   returning @IO ('Etag', 'BL.ByteString')@ in which case this action will
-                    --   be executed on every request to reload the content (this is useful
-                    --   for a debugging mode).
-}
+data EmbeddableEntry = EmbeddableEntry
+    { eLocation :: T.Text
+    -- ^ The location where this resource should be served from.  The
+    --   location can contain forward slashes (/) to simulate directories,
+    --   but must not end with a forward slash.
+    , eMimeType :: MimeType
+    -- ^ The mime type.
+    , eContent :: Either (Etag, BL.ByteString) ExpQ
+    -- ^ The content itself.  The content can be given as a tag and bytestring,
+    --   in which case the content will be embedded directly into the execuatble.
+    --   Alternatively, the content can be given as a template haskell expression
+    --   returning @IO ('Etag', 'BL.ByteString')@ in which case this action will
+    --   be executed on every request to reload the content (this is useful
+    --   for a debugging mode).
+    }
 
 -- | This structure is used at runtime to hold the entry.
-data EmbeddedEntry = EmbeddedEntry {
-    embLocation   :: !T.Text
-  , embMime       :: !MimeType
-  , embEtag       :: !B.ByteString
-  , embCompressed :: !Bool
-  , embContent    :: !B.ByteString
-}
+data EmbeddedEntry = EmbeddedEntry
+    { embLocation :: !T.Text
+    , embMime :: !MimeType
+    , embEtag :: !B.ByteString
+    , embCompressed :: !Bool
+    , embContent :: !B.ByteString
+    }
 
 -- | This structure is used at runtime to hold the reload entries.
-data ReloadEntry = ReloadEntry {
-    reloadLocation :: !T.Text
-  , reloadMime     :: !MimeType
-  , reloadContent  :: IO (T.Text, BL.ByteString)
-}
+data ReloadEntry = ReloadEntry
+    { reloadLocation :: !T.Text
+    , reloadMime :: !MimeType
+    , reloadContent :: IO (T.Text, BL.ByteString)
+    }
 
 -- The use of unsafePackAddressLen is safe here because the length
 -- is correct and we will only be reading from the bytestring, never
@@ -102,90 +109,100 @@
 -- | A template haskell expression which creates either an EmbeddedEntry or ReloadEntry.
 mkEntry :: EmbeddableEntry -> ExpQ
 mkEntry (EmbeddableEntry loc mime (Left (etag, ct))) =
-        [| Left $ EmbeddedEntry (T.pack $locE)
-                                $(bytestringE mime)
-                                $(bytestringE $ T.encodeUtf8 etag)
-                                (1 == I# $compressedE)
-                                $(bytestringLazyE ct')
+    [|
+        Left $
+            EmbeddedEntry
+                (T.pack $locE)
+                $(bytestringE mime)
+                $(bytestringE $ T.encodeUtf8 etag)
+                (1 == I# $compressedE)
+                $(bytestringLazyE ct')
         |]
-    where
-        locE = litE $ stringL $ T.unpack loc
-        (compressed, ct') = tryCompress mime ct
-        compressedE = litE $ intPrimL $ if compressed then 1 else 0
-
+  where
+    locE = litE $ stringL $ T.unpack loc
+    (compressed, ct') = tryCompress mime ct
+    compressedE = litE $ intPrimL $ if compressed then 1 else 0
 mkEntry (EmbeddableEntry loc mime (Right expr)) =
-        [| Right $ ReloadEntry (T.pack $locE)
-                               $(bytestringE mime)
-                               $expr
+    [|
+        Right $
+            ReloadEntry
+                (T.pack $locE)
+                $(bytestringE mime)
+                $expr
         |]
-    where
-        locE = litE $ stringL $ T.unpack loc
+  where
+    locE = litE $ stringL $ T.unpack loc
 
 -- | Converts an embedded entry to a file
 embeddedToFile :: EmbeddedEntry -> File
-embeddedToFile entry = File
-    { fileGetSize = fromIntegral $ B.length $ embContent entry
-    , fileToResponse = \s h ->
-        let h' = if embCompressed entry
-                    then h ++ [("Content-Encoding", "gzip")]
-                    else h
-         in W.responseBuilder s h' $ byteStringInsert $ embContent entry
-
-    -- Usually the fileName should just be the filename not the entire path,
-    -- but we need the whole path to make the lookup within lookupMime
-    -- possible.  lookupMime is provided only with the File and from that
-    -- we must find the mime type. Putting the path here is OK since
-    -- within staticApp the fileName is used for directory listings which
-    -- we have disabled.
-    , fileName = unsafeToPiece $ embLocation entry
-    , fileGetHash = return $ if B.null (embEtag entry)
-                                 then Nothing
-                                 else Just $ embEtag entry
-    , fileGetModified = Nothing
-    }
+embeddedToFile entry =
+    File
+        { fileGetSize = fromIntegral $ B.length $ embContent entry
+        , fileToResponse = \s h ->
+            let h' =
+                    if embCompressed entry
+                        then h ++ [("Content-Encoding", "gzip")]
+                        else h
+             in W.responseBuilder s h' $ byteStringInsert $ embContent entry
+        , -- Usually the fileName should just be the filename not the entire path,
+          -- but we need the whole path to make the lookup within lookupMime
+          -- possible.  lookupMime is provided only with the File and from that
+          -- we must find the mime type. Putting the path here is OK since
+          -- within staticApp the fileName is used for directory listings which
+          -- we have disabled.
+          fileName = unsafeToPiece $ embLocation entry
+        , fileGetHash =
+            return $
+                if B.null (embEtag entry)
+                    then Nothing
+                    else Just $ embEtag entry
+        , fileGetModified = Nothing
+        }
 
 -- | Converts a reload entry to a file
 reloadToFile :: ReloadEntry -> IO File
 reloadToFile entry = do
     (etag, ct) <- reloadContent entry
     let etag' = T.encodeUtf8 etag
-    return $ File
-        { fileGetSize = fromIntegral $ BL.length ct
-        , fileToResponse = \s h -> W.responseLBS s h ct
-        -- Similar to above the entire path needs to be in the fileName.
-        , fileName = unsafeToPiece $ reloadLocation entry
-        , fileGetHash = return $ if T.null etag then Nothing else Just etag'
-        , fileGetModified = Nothing
-        }
-
+    return $
+        File
+            { fileGetSize = fromIntegral $ BL.length ct
+            , fileToResponse = \s h -> W.responseLBS s h ct
+            , -- Similar to above the entire path needs to be in the fileName.
+              fileName = unsafeToPiece $ reloadLocation entry
+            , fileGetHash = return $ if T.null etag then Nothing else Just etag'
+            , fileGetModified = Nothing
+            }
 
 -- | Build a static settings based on a filemap.
 filemapToSettings :: M.HashMap T.Text (MimeType, IO File) -> StaticSettings
-filemapToSettings mfiles = (defaultWebAppSettings "")
-                              { ssLookupFile = lookupFile
-                              , ssGetMimeType = lookupMime
-                              }
-    where
-        piecesToFile p = T.intercalate "/" $ map fromPiece p
+filemapToSettings mfiles =
+    (defaultWebAppSettings "")
+        { ssLookupFile = lookupFile
+        , ssGetMimeType = lookupMime
+        }
+  where
+    piecesToFile p = T.intercalate "/" $ map fromPiece p
 
-        lookupFile [] = return LRNotFound
-        lookupFile p =
-            case M.lookup (piecesToFile p) mfiles of
-                Nothing -> return LRNotFound
-                Just (_,act) -> LRFile <$> act
+    lookupFile [] = return LRNotFound
+    lookupFile p =
+        case M.lookup (piecesToFile p) mfiles of
+            Nothing -> return LRNotFound
+            Just (_, act) -> LRFile <$> act
 
-        lookupMime (File { fileName = p }) =
-            case M.lookup (fromPiece p) mfiles of
-                Just (mime,_) -> return mime
-                Nothing -> return $ defaultMimeLookup $ fromPiece p
+    lookupMime (File{fileName = p}) =
+        case M.lookup (fromPiece p) mfiles of
+            Just (mime, _) -> return mime
+            Nothing -> return $ defaultMimeLookup $ fromPiece p
 
 -- | Create a 'StaticSettings' from a list of entries.  Executed at run time.
 entriesToSt :: [Either EmbeddedEntry ReloadEntry] -> StaticSettings
 entriesToSt entries = hmap `seq` filemapToSettings hmap
-    where
-        embFiles = [ (embLocation e, (embMime e, return $ embeddedToFile e)) | e <- lefts entries]
-        reloadFiles = [ (reloadLocation r, (reloadMime r, reloadToFile r)) | r <- rights entries]
-        hmap = M.fromList $ embFiles ++ reloadFiles
+  where
+    embFiles =
+        [(embLocation e, (embMime e, return $ embeddedToFile e)) | e <- lefts entries]
+    reloadFiles = [(reloadLocation r, (reloadMime r, reloadToFile r)) | r <- rights entries]
+    hmap = M.fromList $ embFiles ++ reloadFiles
 
 -- | Create a 'StaticSettings' at compile time that embeds resources directly into the compiled
 --   executable.  The embedded resources are precompressed (depending on mime type)
@@ -197,17 +214,17 @@
 --
 -- > {-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}
 -- > module A (mkEmbedded) where
--- > 
+-- >
 -- > import WaiAppStatic.Storage.Embedded
 -- > import Crypto.Hash.MD5 (hashlazy)
 -- > import qualified Data.ByteString.Lazy as BL
 -- > import qualified Data.ByteString.Base64 as B64
 -- > import qualified Data.Text as T
 -- > import qualified Data.Text.Encoding as T
--- > 
+-- >
 -- > hash :: BL.ByteString -> T.Text
 -- > hash = T.take 8 . T.decodeUtf8 . B64.encode . hashlazy
--- > 
+-- >
 -- > mkEmbedded :: IO [EmbeddableEntry]
 -- > mkEmbedded = do
 -- >     file <- BL.readFile "test.css"
@@ -216,13 +233,13 @@
 -- >                 , eMimeType = "text/css"
 -- >                 , eContent  = Left (hash file, file)
 -- >                 }
--- > 
+-- >
 -- >     let reload = EmbeddableEntry {
 -- >                      eLocation = "anotherdir/test2.txt"
 -- >                    , eMimeType = "text/plain"
 -- >                    , eContent  = Right [| BL.readFile "test2.txt" >>= \c -> return (hash c, c) |]
 -- >                    }
--- > 
+-- >
 -- >     return [emb, reload]
 --
 -- The above @mkEmbedded@ will be executed at compile time.  It loads the contents of test.css and
@@ -241,37 +258,38 @@
 --
 -- > {-# LANGUAGE TemplateHaskell #-}
 -- > module B where
--- > 
+-- >
 -- > import A
 -- > import Network.Wai (Application)
 -- > import Network.Wai.Application.Static (staticApp)
 -- > import WaiAppStatic.Storage.Embedded
 -- > import Network.Wai.Handler.Warp (run)
--- > 
+-- >
 -- > myApp :: Application
 -- > myApp = staticApp $(mkSettings mkEmbedded)
--- > 
+-- >
 -- > main :: IO ()
 -- > main = run 3000 myApp
 mkSettings :: IO [EmbeddableEntry] -> ExpQ
 mkSettings action = do
     entries <- runIO action
-    [| entriesToSt $(listE $ map mkEntry entries) |]
+    [|entriesToSt $(listE $ map mkEntry entries)|]
 
 shouldCompress :: MimeType -> Bool
 shouldCompress m = "text/" `B.isPrefixOf` m || m `elem` extra
-    where
-        extra = [ "application/json"
-                , "application/javascript"
-                , "application/ecmascript"
-                ]
+  where
+    extra =
+        [ "application/json"
+        , "application/javascript"
+        , "application/ecmascript"
+        ]
 
 -- | Only compress if the mime type is correct and the compressed text is actually shorter.
 tryCompress :: MimeType -> BL.ByteString -> (Bool, BL.ByteString)
 tryCompress mime ct
-        | shouldCompress mime = (c, ct')
-        | otherwise = (False, ct)
-    where
-        compressed = compress ct
-        c = BL.length compressed < BL.length ct
-        ct' = if c then compressed else ct
+    | shouldCompress mime = (c, ct')
+    | otherwise = (False, ct)
+  where
+    compressed = compress ct
+    c = BL.length compressed < BL.length ct
+    ct' = if c then compressed else ct
diff --git a/WaiAppStatic/Storage/Filesystem.hs b/WaiAppStatic/Storage/Filesystem.hs
--- a/WaiAppStatic/Storage/Filesystem.hs
+++ b/WaiAppStatic/Storage/Filesystem.hs
@@ -1,31 +1,42 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 -- | Access files on the filesystem.
-module WaiAppStatic.Storage.Filesystem
-    ( -- * Types
-      ETagLookup
-      -- * Settings
-    , defaultWebAppSettings
-    , defaultFileServerSettings
-    , webAppSettingsWithLookup
-    ) where
+module WaiAppStatic.Storage.Filesystem (
+    -- * Types
+    ETagLookup,
 
-import WaiAppStatic.Types
-import System.FilePath ((</>))
-import System.IO (withBinaryFile, IOMode(..))
-import System.Directory (doesFileExist, doesDirectoryExist, getDirectoryContents)
-import Data.List (foldl')
+    -- * Settings
+    defaultWebAppSettings,
+    defaultFileServerSettings,
+    webAppSettingsWithLookup,
+) where
+
+import Control.Exception (SomeException, try)
 import Control.Monad (forM)
-import Util
 import Data.ByteString (ByteString)
-import Control.Exception (SomeException, try)
+import Data.List (foldl')
+import Data.Maybe (catMaybes)
+import Network.Mime
 import qualified Network.Wai as W
+import System.Directory (
+    doesDirectoryExist,
+    doesFileExist,
+    getDirectoryContents,
+ )
+import System.FilePath ((</>))
+import System.IO (IOMode (..), withBinaryFile)
+import System.PosixCompat.Files (
+    fileSize,
+    getFileStatus,
+    isRegularFile,
+    modificationTime,
+ )
+import Util
 import WaiAppStatic.Listing
-import Network.Mime
-import System.PosixCompat.Files (fileSize, getFileStatus, modificationTime, isRegularFile)
-import Data.Maybe (catMaybes)
+import WaiAppStatic.Types
 #ifdef MIN_VERSION_crypton
 import Data.ByteArray.Encoding
 import Crypto.Hash (hashlazy, MD5, Digest)
@@ -42,70 +53,88 @@
 
 -- | Settings optimized for a web application. Files will have aggressive
 -- caching applied and hashes calculated, and indices and listings are disabled.
-defaultWebAppSettings :: FilePath -- ^ root folder to serve from
-                      -> StaticSettings
-defaultWebAppSettings root = StaticSettings
-    { ssLookupFile = webAppLookup hashFileIfExists root
-    , ssMkRedirect  = defaultMkRedirect
-    , ssGetMimeType = return . defaultMimeLookup . fromPiece . fileName
-    , ssMaxAge  = MaxAgeForever
-    , ssListing = Nothing
-    , ssIndices = []
-    , ssRedirectToIndex = False
-    , ssUseHash = True
-    , ssAddTrailingSlash = False
-    , ss404Handler = Nothing
-    }
+defaultWebAppSettings
+    :: FilePath
+    -- ^ root folder to serve from
+    -> StaticSettings
+defaultWebAppSettings root =
+    StaticSettings
+        { ssLookupFile = webAppLookup hashFileIfExists root
+        , ssMkRedirect = defaultMkRedirect
+        , ssGetMimeType = return . defaultMimeLookup . fromPiece . fileName
+        , ssMaxAge = MaxAgeForever
+        , ssListing = Nothing
+        , ssIndices = []
+        , ssRedirectToIndex = False
+        , ssUseHash = True
+        , ssAddTrailingSlash = False
+        , ss404Handler = Nothing
+        }
 
 -- | Settings optimized for a file server. More conservative caching will be
 -- applied, and indices and listings are enabled.
-defaultFileServerSettings :: FilePath -- ^ root folder to serve from
-                          -> StaticSettings
-defaultFileServerSettings root = StaticSettings
-    { ssLookupFile = fileSystemLookup (fmap Just . hashFile) root
-    , ssMkRedirect = defaultMkRedirect
-    , ssGetMimeType = return . defaultMimeLookup . fromPiece . fileName
-    , ssMaxAge = NoMaxAge
-    , ssListing = Just defaultListing
-    , ssIndices = map unsafeToPiece ["index.html", "index.htm"]
-    , ssRedirectToIndex = False
-    , ssUseHash = False
-    , ssAddTrailingSlash = False
-    , ss404Handler = Nothing
-    }
+defaultFileServerSettings
+    :: FilePath
+    -- ^ root folder to serve from
+    -> StaticSettings
+defaultFileServerSettings root =
+    StaticSettings
+        { ssLookupFile = fileSystemLookup (fmap Just . hashFile) root
+        , ssMkRedirect = defaultMkRedirect
+        , ssGetMimeType = return . defaultMimeLookup . fromPiece . fileName
+        , ssMaxAge = NoMaxAge
+        , ssListing = Just defaultListing
+        , ssIndices = map unsafeToPiece ["index.html", "index.htm"]
+        , ssRedirectToIndex = False
+        , ssUseHash = False
+        , ssAddTrailingSlash = False
+        , ss404Handler = Nothing
+        }
 
 -- | Same as @defaultWebAppSettings@, but additionally uses a specialized
 -- @ETagLookup@ in place of the standard one. This can allow you to cache your
 -- hash values, or even precompute them.
-webAppSettingsWithLookup :: FilePath -- ^ root folder to serve from
-                         -> ETagLookup
-                         -> StaticSettings
+webAppSettingsWithLookup
+    :: FilePath
+    -- ^ root folder to serve from
+    -> ETagLookup
+    -> StaticSettings
 webAppSettingsWithLookup dir etagLookup =
-  (defaultWebAppSettings dir) { ssLookupFile = webAppLookup etagLookup dir}
+    (defaultWebAppSettings dir){ssLookupFile = webAppLookup etagLookup dir}
 
 -- | Convenience wrapper for @fileHelper@.
-fileHelperLR :: ETagLookup
-             -> FilePath -- ^ file location
-             -> Piece -- ^ file name
-             -> IO LookupResult
+fileHelperLR
+    :: ETagLookup
+    -> FilePath
+    -- ^ file location
+    -> Piece
+    -- ^ file name
+    -> IO LookupResult
 fileHelperLR a b c = fmap (maybe LRNotFound LRFile) $ fileHelper a b c
 
 -- | Attempt to load up a @File@ from the given path.
-fileHelper :: ETagLookup
-           -> FilePath -- ^ file location
-           -> Piece -- ^ file name
-           -> IO (Maybe File)
+fileHelper
+    :: ETagLookup
+    -> FilePath
+    -- ^ file location
+    -> Piece
+    -- ^ file name
+    -> IO (Maybe File)
 fileHelper hashFunc fp name = do
     efs <- try $ getFileStatus fp
     case efs of
         Left (_ :: SomeException) -> return Nothing
-        Right fs | isRegularFile fs -> return $ Just File
-            { fileGetSize = fromIntegral $ fileSize fs
-            , fileToResponse = \s h -> W.responseFile s h fp Nothing
-            , fileName = name
-            , fileGetHash = hashFunc fp
-            , fileGetModified = Just $ modificationTime fs
-            }
+        Right fs
+            | isRegularFile fs ->
+                return $
+                    Just
+                        File
+                            { fileGetSize = fromIntegral $ fileSize fs
+                            , fileToResponse = \s h -> W.responseFile s h fp Nothing
+                            , fileName = name
+                            , fileGetHash = hashFunc fp
+                            , fileGetModified = Just $ modificationTime fs
+                            }
         Right _ -> return Nothing
 
 -- | How to calculate etags. Can perform filesystem reads on each call, or use
@@ -144,14 +173,17 @@
         Right x -> Just x
 
 isVisible :: FilePath -> Bool
-isVisible ('.':_) = False
+isVisible ('.' : _) = False
 isVisible "" = False
 isVisible _ = True
 
 -- | Get a proper @LookupResult@, checking if the path is a file or folder.
 -- Compare with @webAppLookup@, which only deals with files.
-fileSystemLookup :: ETagLookup
-                 -> FilePath -> Pieces -> IO LookupResult
+fileSystemLookup
+    :: ETagLookup
+    -> FilePath
+    -> Pieces
+    -> IO LookupResult
 fileSystemLookup hashFunc prefix pieces = do
     let fp = pathFromPieces prefix pieces
     fe <- doesFileExist fp
diff --git a/WaiAppStatic/Types.hs b/WaiAppStatic/Types.hs
--- a/WaiAppStatic/Types.hs
+++ b/WaiAppStatic/Types.hs
@@ -1,31 +1,34 @@
-module WaiAppStatic.Types
-    ( -- * Pieces
-      Piece
-    , toPiece
-    , fromPiece
-    , unsafeToPiece
-    , Pieces
-    , toPieces
-      -- * Caching
-    , MaxAge (..)
-      -- * File\/folder serving
-    , FolderName
-    , Folder (..)
-    , File (..)
-    , LookupResult (..)
-    , Listing
-      -- * Settings
-    , StaticSettings (..)
-    ) where
+module WaiAppStatic.Types (
+    -- * Pieces
+    Piece,
+    toPiece,
+    fromPiece,
+    unsafeToPiece,
+    Pieces,
+    toPieces,
 
+    -- * Caching
+    MaxAge (..),
+
+    -- * File\/folder serving
+    FolderName,
+    Folder (..),
+    File (..),
+    LookupResult (..),
+    Listing,
+
+    -- * Settings
+    StaticSettings (..),
+) where
+
+import Data.ByteString (ByteString)
+import Data.ByteString.Builder (Builder)
 import Data.Text (Text)
+import qualified Data.Text as T
 import qualified Network.HTTP.Types as H
+import Network.Mime (MimeType)
 import qualified Network.Wai as W
-import Data.ByteString (ByteString)
 import System.Posix.Types (EpochTime)
-import qualified Data.Text as T
-import Data.ByteString.Builder (Builder)
-import Network.Mime (MimeType)
 
 -- | An individual component of a path, or of a filepath.
 --
@@ -35,7 +38,7 @@
 --
 -- Individual file lookup backends must know how to convert from a @Piece@ to
 -- their storage system.
-newtype Piece = Piece { fromPiece :: Text }
+newtype Piece = Piece {fromPiece :: Text}
     deriving (Show, Eq, Ord)
 
 -- | Smart constructor for a @Piece@. Won\'t allow unsafe components, such as
@@ -64,10 +67,17 @@
 type Pieces = [Piece]
 
 -- | Values for the max-age component of the cache-control response header.
-data MaxAge = NoMaxAge -- ^ no cache-control set
-            | MaxAgeSeconds Int -- ^ set to the given number of seconds
-            | MaxAgeForever -- ^ essentially infinite caching; in reality, probably one year
-            | NoStore -- ^ set cache-control to no-store @since 3.1.8
+data MaxAge
+    = -- | no cache-control set
+      NoMaxAge
+    | -- | set to the given number of seconds
+      MaxAgeSeconds Int
+    | -- | essentially infinite caching; in reality, probably one year
+      MaxAgeForever
+    | -- | set cache-control to no-store @since 3.1.8
+      NoStore
+    | -- | set cache-control to no-cache @since 3.1.9
+      NoCache
 
 -- | Just the name of a folder.
 type FolderName = Piece
@@ -80,26 +90,27 @@
 
 -- | Information on an individual file.
 data File = File
-    { -- | Size of file in bytes
-      fileGetSize :: Integer
-      -- | How to construct a WAI response for this file. Some files are stored
-      -- on the filesystem and can use @ResponseFile@, while others are stored
-      -- in memory and should use @ResponseBuilder@.
+    { fileGetSize :: Integer
+    -- ^ Size of file in bytes
     , fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response
-      -- | Last component of the filename.
+    -- ^ How to construct a WAI response for this file. Some files are stored
+    -- on the filesystem and can use @ResponseFile@, while others are stored
+    -- in memory and should use @ResponseBuilder@.
     , fileName :: Piece
-      -- | Calculate a hash of the contents of this file, such as for etag.
+    -- ^ Last component of the filename.
     , fileGetHash :: IO (Maybe ByteString)
-      -- | Last modified time, used for both display in listings and if-modified-since.
+    -- ^ Calculate a hash of the contents of this file, such as for etag.
     , fileGetModified :: Maybe EpochTime
+    -- ^ Last modified time, used for both display in listings and if-modified-since.
     }
 
 -- | Result of looking up a file in some storage backend.
 --
 -- The lookup is either a file or folder, or does not exist.
-data LookupResult = LRFile File
-                  | LRFolder Folder
-                  | LRNotFound
+data LookupResult
+    = LRFile File
+    | LRFolder Folder
+    | LRNotFound
 
 -- | How to construct a directory listing page for the given request path and
 -- the resulting folder.
@@ -110,45 +121,35 @@
 -- Note that you should use the settings type approach for modifying values.
 -- See <http://www.yesodweb.com/book/settings-types> for more information.
 data StaticSettings = StaticSettings
-    {
-      -- | Lookup a single file or folder. This is how you can control storage
-      -- backend (filesystem, embedded, etc) and where to lookup.
-      ssLookupFile :: Pieces -> IO LookupResult
-
-      -- | Determine the mime type of the given file. Note that this function
-      -- lives in @IO@ in case you want to perform more complicated mimetype
-      -- analysis, such as via the @file@ utility.
+    { ssLookupFile :: Pieces -> IO LookupResult
+    -- ^ Lookup a single file or folder. This is how you can control storage
+    -- backend (filesystem, embedded, etc) and where to lookup.
     , ssGetMimeType :: File -> IO MimeType
-
-      -- | Ordered list of filenames to be used for indices. If the user
-      -- requests a folder, and a file with the given name is found in that
-      -- folder, that file is served. This supercedes any directory listing.
+    -- ^ Determine the mime type of the given file. Note that this function
+    -- lives in @IO@ in case you want to perform more complicated mimetype
+    -- analysis, such as via the @file@ utility.
     , ssIndices :: [Piece]
-
-      -- | How to perform a directory listing. Optional. Will be used when the
-      -- user requested a folder.
+    -- ^ Ordered list of filenames to be used for indices. If the user
+    -- requests a folder, and a file with the given name is found in that
+    -- folder, that file is served. This supercedes any directory listing.
     , ssListing :: Maybe Listing
-
-      -- | Value to provide for max age in the cache-control.
+    -- ^ How to perform a directory listing. Optional. Will be used when the
+    -- user requested a folder.
     , ssMaxAge :: MaxAge
-
-      -- | Given a requested path and a new destination, construct a string
-      -- that will go there. Default implementation will use relative paths.
+    -- ^ Value to provide for max age in the cache-control.
     , ssMkRedirect :: Pieces -> ByteString -> ByteString
-
-      -- | If @True@, send a redirect to the user when a folder is requested
-      -- and an index page should be displayed. When @False@, display the
-      -- content immediately.
+    -- ^ Given a requested path and a new destination, construct a string
+    -- that will go there. Default implementation will use relative paths.
     , ssRedirectToIndex :: Bool
-
-      -- | Prefer usage of etag caching to last-modified caching.
+    -- ^ If @True@, send a redirect to the user when a folder is requested
+    -- and an index page should be displayed. When @False@, display the
+    -- content immediately.
     , ssUseHash :: Bool
-
-      -- | Force a trailing slash at the end of directories
+    -- ^ Prefer usage of etag caching to last-modified caching.
     , ssAddTrailingSlash :: Bool
-
-      -- | Optional `W.Application` to be used in case of 404 errors
-      --
-      -- Since 3.1.3
+    -- ^ Force a trailing slash at the end of directories
     , ss404Handler :: Maybe W.Application
+    -- ^ Optional `W.Application` to be used in case of 404 errors
+    --
+    -- Since 3.1.3
     }
diff --git a/test/EmbeddedTestEntries.hs b/test/EmbeddedTestEntries.hs
--- a/test/EmbeddedTestEntries.hs
+++ b/test/EmbeddedTestEntries.hs
@@ -1,55 +1,59 @@
-{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
 
 module EmbeddedTestEntries where
 
-import WaiAppStatic.Storage.Embedded
+import qualified Data.ByteString.Lazy as BL
 import qualified Data.Text as T
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.Encoding as TL
-import qualified Data.ByteString.Lazy as BL
+import WaiAppStatic.Storage.Embedded
 
 body :: Int -> Char -> BL.ByteString
 body i c = TL.encodeUtf8 $ TL.pack $ replicate i c
 
 mkEntries :: IO [EmbeddableEntry]
-mkEntries = return
-    -- An entry that should be compressed
-  [ EmbeddableEntry "e1.txt" 
-                    "text/plain" 
-                    (Left ("Etag 1", body 1000 'A'))
-
-    -- An entry so short that the compressed text is longer
-  , EmbeddableEntry "e2.txt"
-                    "text/plain"
-                    (Left ("Etag 2", "ABC"))
-
-    -- An entry that is not compressed because of the mime
-  , EmbeddableEntry "somedir/e3.txt"
-                    "xxx"
-                    (Left ("Etag 3", body 1000 'A'))
-
-    -- A reloadable entry
-  , EmbeddableEntry "e4.css"
-                    "text/css"
-                    (Right [| return ("Etag 4" :: T.Text, body 2000 'Q') |])
-
-    -- An entry without etag
-  , EmbeddableEntry "e5.txt"
-                    "text/plain"
-                    (Left ("", body 1000 'Z'))
-
-    -- A reloadable entry without etag
-  , EmbeddableEntry "e6.txt"
-                    "text/plain"
-                    (Right [| return ("" :: T.Text, body 1000 'W') |] )
-
-    -- An index file
-  , EmbeddableEntry "index.html"
-                    "text/html"
-                    (Right [| return ("" :: T.Text, "index file") |] )
-
-    -- An index file in a subdir
-  , EmbeddableEntry "foo/index.html"
-                    "text/html"
-                    (Right [| return ("" :: T.Text, "index file in subdir") |] )
-  ]
+mkEntries =
+    return
+        -- An entry that should be compressed
+        [ EmbeddableEntry
+            "e1.txt"
+            "text/plain"
+            (Left ("Etag 1", body 1000 'A'))
+        , -- An entry so short that the compressed text is longer
+          EmbeddableEntry
+            "e2.txt"
+            "text/plain"
+            (Left ("Etag 2", "ABC"))
+        , -- An entry that is not compressed because of the mime
+          EmbeddableEntry
+            "somedir/e3.txt"
+            "xxx"
+            (Left ("Etag 3", body 1000 'A'))
+        , -- A reloadable entry
+          EmbeddableEntry
+            "e4.css"
+            "text/css"
+            (Right [|return ("Etag 4" :: T.Text, body 2000 'Q')|])
+        , -- An entry without etag
+          EmbeddableEntry
+            "e5.txt"
+            "text/plain"
+            (Left ("", body 1000 'Z'))
+        , -- A reloadable entry without etag
+          EmbeddableEntry
+            "e6.txt"
+            "text/plain"
+            (Right [|return ("" :: T.Text, body 1000 'W')|])
+        , -- An index file
+          EmbeddableEntry
+            "index.html"
+            "text/html"
+            (Right [|return ("" :: T.Text, "index file")|])
+        , -- An index file in a subdir
+          EmbeddableEntry
+            "foo/index.html"
+            "text/html"
+            (Right [|return ("" :: T.Text, "index file in subdir")|])
+        ]
diff --git a/test/WaiAppEmbeddedTest.hs b/test/WaiAppEmbeddedTest.hs
--- a/test/WaiAppEmbeddedTest.hs
+++ b/test/WaiAppEmbeddedTest.hs
@@ -1,4 +1,6 @@
-{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
 module WaiAppEmbeddedTest (embSpec) where
 
 import Codec.Compression.GZip (compress)
@@ -28,27 +30,32 @@
             assertBody (compress $ body 1000 'A') req
 
         it "304 when valid if-none-match sent" $ embed $ do
-            req <- request (setRawPathInfo defRequest "e1.txt")
-                             { requestHeaders = [("If-None-Match", "Etag 1")] }
+            req <-
+                request
+                    (setRawPathInfo defRequest "e1.txt")
+                        { requestHeaders = [("If-None-Match", "Etag 1")]
+                        }
             assertStatus 304 req
 
         it "ssIndices works" $ do
-            let testSettings = $(mkSettings mkEntries){
-                    ssIndices = [unsafeToPiece "index.html"]
-                }
+            let testSettings =
+                    $(mkSettings mkEntries)
+                        { ssIndices = [unsafeToPiece "index.html"]
+                        }
             embedSettings testSettings $ do
-              req <- request defRequest
-              assertStatus 200 req
-              assertBody "index file" req
+                req <- request defRequest
+                assertStatus 200 req
+                assertBody "index file" req
 
         it "ssIndices works with trailing slashes" $ do
-            let testSettings = $(mkSettings mkEntries){
-                    ssIndices = [unsafeToPiece "index.html"]
-                }
+            let testSettings =
+                    $(mkSettings mkEntries)
+                        { ssIndices = [unsafeToPiece "index.html"]
+                        }
             embedSettings testSettings $ do
-              req <- request (setRawPathInfo defRequest "/foo/")
-              assertStatus 200 req
-              assertBody "index file in subdir" req
+                req <- request (setRawPathInfo defRequest "/foo/")
+                assertStatus 200 req
+                assertBody "index file in subdir" req
 
     describe "embedded, uncompressed entry" $ do
         it "too short" $ embed $ do
@@ -68,13 +75,14 @@
             assertBody (body 1000 'A') req
 
     describe "reloadable entry" $
-        it "served correctly" $ embed $ do
-            req <- request (setRawPathInfo defRequest "e4.css")
-            assertStatus 200 req
-            assertHeader "Content-Type" "text/css" req
-            assertNoHeader "Content-Encoding" req
-            assertHeader "ETag" "Etag 4" req
-            assertBody (body 2000 'Q') req
+        it "served correctly" $
+            embed $ do
+                req <- request (setRawPathInfo defRequest "e4.css")
+                assertStatus 200 req
+                assertHeader "Content-Type" "text/css" req
+                assertNoHeader "Content-Encoding" req
+                assertHeader "ETag" "Etag 4" req
+                assertBody (body 2000 'Q') req
 
     describe "entries without etags" $ do
         it "embedded entry" $ embed $ do
diff --git a/test/WaiAppStaticTest.hs b/test/WaiAppStaticTest.hs
--- a/test/WaiAppStaticTest.hs
+++ b/test/WaiAppStaticTest.hs
@@ -1,19 +1,24 @@
-{-# LANGUAGE OverloadedStrings, NoMonomorphismRestriction #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+
 module WaiAppStaticTest (spec) where
 
 import Network.Wai.Application.Static
 import WaiAppStatic.Types
 
+import qualified Data.ByteString.Char8 as S8
 import Test.Hspec
 import Test.Mockery.Directory
-import qualified Data.ByteString.Char8 as S8
+
 -- import qualified Data.ByteString.Lazy.Char8 as L8
-import System.PosixCompat.Files (getFileStatus, modificationTime)
+
 import System.FilePath
 import System.IO.Temp
+import System.PosixCompat.Files (getFileStatus, modificationTime)
 
 import Network.HTTP.Date
 import Network.HTTP.Types (status500)
+
 {-import System.Locale (defaultTimeLocale)-}
 {-import Data.Time.Format (formatTime)-}
 
@@ -29,157 +34,181 @@
 
 spec :: Spec
 spec = do
-  let webApp = flip runSession $ staticApp $ defaultWebAppSettings "test"
-  let fileServerAppWithSettings settings = flip runSession $ staticApp settings
-  let fileServerApp = fileServerAppWithSettings (defaultFileServerSettings "test")
-        { ssAddTrailingSlash = True
-        }
-
-  let etag = "1B2M2Y8AsgTpgAmY7PhCfg=="
-  let file = "a/b"
-  let statFile = setRawPathInfo defRequest file
+    let webApp = flip runSession $ staticApp $ defaultWebAppSettings "test"
+    let fileServerAppWithSettings settings = flip runSession $ staticApp settings
+    let fileServerApp =
+            fileServerAppWithSettings
+                (defaultFileServerSettings "test")
+                    { ssAddTrailingSlash = True
+                    }
 
-  describe "mime types" $ do
-    it "fileNameExtensions" $
-        fileNameExtensions "foo.tar.gz" `shouldBe` ["tar.gz", "gz"]
-    it "handles multi-extensions" $
-        defaultMimeLookup "foo.tar.gz" `shouldBe` "application/x-tgz"
-    it "defaults correctly" $
-        defaultMimeLookup "foo.unknown" `shouldBe` "application/octet-stream"
+    let etag = "1B2M2Y8AsgTpgAmY7PhCfg=="
+    let file = "a/b"
+    let statFile = setRawPathInfo defRequest file
 
-  describe "webApp" $ do
-    it "403 for unsafe paths" $ webApp $
-      forM_ ["..", "."] $ \path ->
-        assertStatus 403 =<<
-          request (setRawPathInfo defRequest path)
+    describe "mime types" $ do
+        it "fileNameExtensions" $
+            fileNameExtensions "foo.tar.gz" `shouldBe` ["tar.gz", "gz"]
+        it "handles multi-extensions" $
+            defaultMimeLookup "foo.tar.gz" `shouldBe` "application/x-tgz"
+        it "defaults correctly" $
+            defaultMimeLookup "foo.unknown" `shouldBe` "application/octet-stream"
 
-    it "200 for hidden paths" $ webApp $
-      forM_ [".hidden/folder.png", ".hidden/haskell.png"] $ \path ->
-        assertStatus 200 =<<
-          request (setRawPathInfo defRequest path)
+    describe "webApp" $ do
+        it "403 for unsafe paths" $
+            webApp $
+                forM_ ["..", "."] $ \path ->
+                    assertStatus 403
+                        =<< request (setRawPathInfo defRequest path)
 
-    it "404 for non-existent files" $ webApp $
-      assertStatus 404 =<<
-        request (setRawPathInfo defRequest "doesNotExist")
+        it "200 for hidden paths" $
+            webApp $
+                forM_ [".hidden/folder.png", ".hidden/haskell.png"] $ \path ->
+                    assertStatus 200
+                        =<< request (setRawPathInfo defRequest path)
 
-    it "302 redirect when multiple slashes" $ webApp $ do
-      req <- request (setRawPathInfo defRequest "a//b/c")
-      assertStatus 302 req
-      assertHeader "Location" "../../a/b/c" req
+        it "404 for non-existent files" $
+            webApp $
+                assertStatus 404
+                    =<< request (setRawPathInfo defRequest "doesNotExist")
 
-    let absoluteApp = flip runSession $ staticApp $ (defaultWebAppSettings "test") {
-          ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
-        }
-    it "302 redirect when multiple slashes" $ absoluteApp $
-      forM_ ["/a//b/c", "a//b/c"] $ \path -> do
-        req <- request (setRawPathInfo defRequest path)
-        assertStatus 302 req
-        assertHeader "Location" "http://www.example.com/a/b/c" req
+        it "302 redirect when multiple slashes" $ webApp $ do
+            req <- request (setRawPathInfo defRequest "a//b/c")
+            assertStatus 302 req
+            assertHeader "Location" "../../a/b/c" req
 
-  describe "webApp when requesting a static asset" $ do
-    it "200 and etag when no etag query parameters" $ webApp $ do
-      req <- request statFile
-      assertStatus 200 req
-      assertHeader "ETag" etag req
-      assertNoHeader "Last-Modified" req
+        let absoluteApp =
+                flip runSession $
+                    staticApp $
+                        (defaultWebAppSettings "test")
+                            { ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
+                            }
+        it "302 redirect when multiple slashes" $
+            absoluteApp $
+                forM_ ["/a//b/c", "a//b/c"] $ \path -> do
+                    req <- request (setRawPathInfo defRequest path)
+                    assertStatus 302 req
+                    assertHeader "Location" "http://www.example.com/a/b/c" req
 
-    it "Cache-Control set when etag parameter is correct" $ webApp $ do
-      req <- request statFile { queryString = [("etag", Just etag)] }
-      assertStatus 200 req
-      assertHeader "Cache-Control" "public, max-age=31536000" req
-      assertNoHeader "Last-Modified" req
+    describe "webApp when requesting a static asset" $ do
+        it "200 and etag when no etag query parameters" $ webApp $ do
+            req <- request statFile
+            assertStatus 200 req
+            assertHeader "ETag" etag req
+            assertNoHeader "Last-Modified" req
 
-    it "200 when invalid in-none-match sent" $ webApp $
-      forM_ ["cached", ""] $ \badETag -> do
-        req <- request statFile { requestHeaders  = [("If-None-Match", badETag)] }
-        assertStatus 200 req
-        assertHeader "ETag" etag req
-        assertNoHeader "Last-Modified" req
+        it "Cache-Control set when etag parameter is correct" $ webApp $ do
+            req <- request statFile{queryString = [("etag", Just etag)]}
+            assertStatus 200 req
+            assertHeader "Cache-Control" "public, max-age=31536000" req
+            assertNoHeader "Last-Modified" req
 
-    it "304 when valid if-none-match sent" $ webApp $ do
-      req <- request statFile { requestHeaders  = [("If-None-Match", etag)] }
-      assertStatus 304 req
-      assertNoHeader "Etag" req
-      assertNoHeader "Last-Modified" req
+        it "200 when invalid in-none-match sent" $
+            webApp $
+                forM_ ["cached", ""] $ \badETag -> do
+                    req <- request statFile{requestHeaders = [("If-None-Match", badETag)]}
+                    assertStatus 200 req
+                    assertHeader "ETag" etag req
+                    assertNoHeader "Last-Modified" req
 
-  describe "fileServerApp" $ do
-    let fileDate = do
-          stat <- liftIO $ getFileStatus $ "test/" ++ file
-          return $ formatHTTPDate . epochTimeToHTTPDate $ modificationTime stat
+        it "304 when valid if-none-match sent" $ webApp $ do
+            req <- request statFile{requestHeaders = [("If-None-Match", etag)]}
+            assertStatus 304 req
+            assertNoHeader "Etag" req
+            assertNoHeader "Last-Modified" req
 
-    it "directory listing for index" $ fileServerApp $ do
-      resp <- request (setRawPathInfo defRequest "a/")
-      assertStatus 200 resp
-      -- note the unclosed img tags so both /> and > will pass
-      assertBodyContains "<img src=\"../.hidden/haskell.png\"" resp
-      assertBodyContains "<img src=\"../.hidden/folder.png\" alt=\"Folder\"" resp
-      assertBodyContains "<a href=\"b\">b</a>" resp
+    describe "fileServerApp" $ do
+        let fileDate = do
+                stat <- liftIO $ getFileStatus $ "test/" ++ file
+                return $ formatHTTPDate . epochTimeToHTTPDate $ modificationTime stat
 
-    it "200 when invalid if-modified-since header" $ fileServerApp $ do
-      forM_ ["123", ""] $ \badDate -> do
-        req <- request statFile {
-          requestHeaders = [("If-Modified-Since", badDate)]
-        }
-        assertStatus 200 req
-        fdate <- fileDate
-        assertHeader "Last-Modified" fdate req
+        it "directory listing for index" $ fileServerApp $ do
+            resp <- request (setRawPathInfo defRequest "a/")
+            assertStatus 200 resp
+            -- note the unclosed img tags so both /> and > will pass
+            assertBodyContains "<img src=\"../.hidden/haskell.png\"" resp
+            assertBodyContains "<img src=\"../.hidden/folder.png\" alt=\"Folder\"" resp
+            assertBodyContains "<a href=\"b\">b</a>" resp
 
-    it "304 when if-modified-since matches" $ fileServerApp $ do
-      fdate <- fileDate
-      req <- request statFile {
-        requestHeaders = [("If-Modified-Since", fdate)]
-      }
-      assertStatus 304 req
-      assertNoHeader "Cache-Control" req
+        it "200 when invalid if-modified-since header" $ fileServerApp $ do
+            forM_ ["123", ""] $ \badDate -> do
+                req <-
+                    request
+                        statFile
+                            { requestHeaders = [("If-Modified-Since", badDate)]
+                            }
+                assertStatus 200 req
+                fdate <- fileDate
+                assertHeader "Last-Modified" fdate req
 
-    context "302 redirect to add a trailing slash on directories if missing" $ do
-      it "works at the root" $ fileServerApp $ do
-        req <- request (setRawPathInfo defRequest "/a")
-        assertStatus 302 req
-        assertHeader "Location" "/a/" req
+        it "304 when if-modified-since matches" $ fileServerApp $ do
+            fdate <- fileDate
+            req <-
+                request
+                    statFile
+                        { requestHeaders = [("If-Modified-Since", fdate)]
+                        }
+            assertStatus 304 req
+            assertNoHeader "Cache-Control" req
 
-      it "works when an index.html is delivered" $ do
-        let settings = (defaultFileServerSettings "."){
-              ssAddTrailingSlash = True
-            }
-        inTempDirectory $ fileServerAppWithSettings settings $ do
-          liftIO $ touch "foo/index.html"
-          req <- request (setRawPathInfo defRequest "/foo")
-          assertStatus 302 req
-          assertHeader "Location" "/foo/" req
+        context "302 redirect to add a trailing slash on directories if missing" $ do
+            it "works at the root" $ fileServerApp $ do
+                req <- request (setRawPathInfo defRequest "/a")
+                assertStatus 302 req
+                assertHeader "Location" "/a/" req
 
-      let urlMapApp = flip runSession $ \req send ->
-            case pathInfo req of
-                "subPath":rest ->
-                    let req' = req { pathInfo = rest }
-                     in (staticApp (defaultFileServerSettings "test")
+            it "works when an index.html is delivered" $ do
+                let settings =
+                        (defaultFileServerSettings ".")
                             { ssAddTrailingSlash = True
-                            }) req' send
-                _ -> send $ responseLBS status500 []
-                    "urlMapApp: only works at subPath"
-      it "works with subpath at the root of the file server" $ urlMapApp $ do
-        req <- request (setRawPathInfo defRequest "/subPath")
-        assertStatus 302 req
-        assertHeader "Location" "/subPath/" req
+                            }
+                inTempDirectory $ fileServerAppWithSettings settings $ do
+                    liftIO $ touch "foo/index.html"
+                    req <- request (setRawPathInfo defRequest "/foo")
+                    assertStatus 302 req
+                    assertHeader "Location" "/foo/" req
 
-    context "with defaultWebAppSettings" $ do
-      it "ssIndices works" $ do
-        withSystemTempDirectory "wai-app-static-test" $ \ dir -> do
-          writeFile (dir </> "index.html") "foo"
-          let testSettings = (defaultWebAppSettings dir) {
-                ssIndices = [unsafeToPiece "index.html"]
-              }
-          fileServerAppWithSettings testSettings $ do
-            resp <- request (setRawPathInfo defRequest "/")
-            assertStatus 200 resp
-            assertBody "foo" resp
+            let urlMapApp = flip runSession $ \req send ->
+                    case pathInfo req of
+                        "subPath" : rest ->
+                            let req' = req{pathInfo = rest}
+                             in ( staticApp
+                                    (defaultFileServerSettings "test")
+                                        { ssAddTrailingSlash = True
+                                        }
+                                )
+                                    req'
+                                    send
+                        _ ->
+                            send $
+                                responseLBS
+                                    status500
+                                    []
+                                    "urlMapApp: only works at subPath"
+            it "works with subpath at the root of the file server" $ urlMapApp $ do
+                req <- request (setRawPathInfo defRequest "/subPath")
+                assertStatus 302 req
+                assertHeader "Location" "/subPath/" req
 
-    context "with defaultFileServerSettings" $ do
-      it "prefers ssIndices over ssListing" $ do
-        withSystemTempDirectory "wai-app-static-test" $ \ dir -> do
-          writeFile (dir </> "index.html") "foo"
-          let testSettings = defaultFileServerSettings dir
-          fileServerAppWithSettings testSettings $ do
-            resp <- request (setRawPathInfo defRequest "/")
-            assertStatus 200 resp
-            assertBody "foo" resp
+        context "with defaultWebAppSettings" $ do
+            it "ssIndices works" $ do
+                withSystemTempDirectory "wai-app-static-test" $ \dir -> do
+                    writeFile (dir </> "index.html") "foo"
+                    let testSettings =
+                            (defaultWebAppSettings dir)
+                                { ssIndices = [unsafeToPiece "index.html"]
+                                }
+                    fileServerAppWithSettings testSettings $ do
+                        resp <- request (setRawPathInfo defRequest "/")
+                        assertStatus 200 resp
+                        assertBody "foo" resp
+
+        context "with defaultFileServerSettings" $ do
+            it "prefers ssIndices over ssListing" $ do
+                withSystemTempDirectory "wai-app-static-test" $ \dir -> do
+                    writeFile (dir </> "index.html") "foo"
+                    let testSettings = defaultFileServerSettings dir
+                    fileServerAppWithSettings testSettings $ do
+                        resp <- request (setRawPathInfo defRequest "/")
+                        assertStatus 200 resp
+                        assertBody "foo" resp
diff --git a/tests.hs b/tests.hs
--- a/tests.hs
+++ b/tests.hs
@@ -1,6 +1,6 @@
-import WaiAppStaticTest (spec)
-import WaiAppEmbeddedTest (embSpec)
 import Test.Hspec
+import WaiAppEmbeddedTest (embSpec)
+import WaiAppStaticTest (spec)
 
 main :: IO ()
 main = hspec $ spec >> embSpec
diff --git a/wai-app-static.cabal b/wai-app-static.cabal
--- a/wai-app-static.cabal
+++ b/wai-app-static.cabal
@@ -1,5 +1,5 @@
 name:            wai-app-static
-version:         3.1.8
+version:         3.1.9
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -52,7 +52,7 @@
                    , filepath
                    , wai-extra                 >= 3.0      && < 3.2
                    , optparse-applicative      >= 0.7
-                   , warp                      >= 3.0.11   && < 3.4
+                   , warp                      >= 3.0.11   && < 3.5
     if flag(crypton)
       build-depends: crypton                   >= 0.6
                    , memory                    >= 0.7
