diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -37,6 +37,6 @@
 
 ### Development & Support
 
-Open an issue on GitHub or join `#scotty` on Freenode.
+Open an issue on GitHub.
 
 Copyright (c) 2012-2019 Andrew Farmer
diff --git a/Web/Scotty.hs b/Web/Scotty.hs
--- a/Web/Scotty.hs
+++ b/Web/Scotty.hs
@@ -26,7 +26,7 @@
       -- definition, as they completely replace the current 'Response' body.
     , text, html, file, json, stream, raw
       -- ** Exceptions
-    , raise, rescue, next, finish, defaultHandler, liftAndCatchIO
+    , raise, raiseStatus, rescue, next, finish, defaultHandler, liftAndCatchIO
       -- * Parsing Parameters
     , Param, Trans.Parsable(..), Trans.readEither
       -- * Types
@@ -93,6 +93,10 @@
 -- turn into HTTP 500 responses.
 raise :: Text -> ActionM a
 raise = Trans.raise
+
+-- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions turn into HTTP responses corresponding to the given status.
+raiseStatus :: Status -> Text -> ActionM a
+raiseStatus = Trans.raiseStatus
 
 -- | Abort execution of this action and continue pattern matching routes.
 -- Like an exception, any code after 'next' is not executed.
diff --git a/Web/Scotty/Action.hs b/Web/Scotty/Action.hs
--- a/Web/Scotty/Action.hs
+++ b/Web/Scotty/Action.hs
@@ -18,6 +18,7 @@
     , param
     , params
     , raise
+    , raiseStatus
     , raw
     , readEither
     , redirect
@@ -37,8 +38,8 @@
 
 import qualified Control.Exception          as E
 import           Control.Monad.Error.Class
-import           Control.Monad.Reader
-import qualified Control.Monad.State        as MS
+import           Control.Monad.Reader       hiding (mapM)
+import qualified Control.Monad.State.Strict as MS
 import           Control.Monad.Trans.Except
 
 import qualified Data.Aeson                 as A
@@ -47,19 +48,24 @@
 import qualified Data.CaseInsensitive       as CI
 import           Data.Default.Class         (def)
 import           Data.Int
-#if !(MIN_VERSION_base(4,8,0))
-import           Data.Monoid                (mconcat)
-#endif
 import qualified Data.Text                  as ST
+import qualified Data.Text.Encoding         as STE
 import qualified Data.Text.Lazy             as T
 import           Data.Text.Lazy.Encoding    (encodeUtf8)
 import           Data.Word
 
 import           Network.HTTP.Types
+-- not re-exported until version 0.11
+#if !MIN_VERSION_http_types(0,11,0)
+import           Network.HTTP.Types.Status
+#endif
 import           Network.Wai
 
 import           Numeric.Natural
 
+import           Prelude ()
+import           Prelude.Compat
+
 import           Web.Scotty.Internal.Types
 import           Web.Scotty.Util
 
@@ -79,18 +85,24 @@
 defH _          (Redirect url)    = do
     status status302
     setHeader "Location" url
-defH Nothing    (ActionError e)   = do
-    status status500
-    html $ mconcat ["<h1>500 Internal Server Error</h1>", showError e]
-defH h@(Just f) (ActionError e)   = f e `catchError` (defH h) -- so handlers can throw exceptions themselves
+defH Nothing    (ActionError s e)   = do
+    status s
+    let code = T.pack $ show $ statusCode s
+    let msg = T.fromStrict $ STE.decodeUtf8 $ statusMessage s
+    html $ mconcat ["<h1>", code, " ", msg, "</h1>", showError e]
+defH h@(Just f) (ActionError _ e)   = f e `catchError` (defH h) -- so handlers can throw exceptions themselves
 defH _          Next              = next
 defH _          Finish            = return ()
 
 -- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions
 -- turn into HTTP 500 responses.
 raise :: (ScottyError e, Monad m) => e -> ActionT e m a
-raise = throwError . ActionError
+raise = raiseStatus status500
 
+-- | Throw an exception, which can be caught with 'rescue'. Uncaught exceptions turn into HTTP responses corresponding to the given status.
+raiseStatus :: (ScottyError e, Monad m) => Status -> e -> ActionT e m a
+raiseStatus s = throwError . ActionError s
+
 -- | Abort execution of this action and continue pattern matching routes.
 -- Like an exception, any code after 'next' is not executed.
 --
@@ -113,8 +125,8 @@
 -- > raise "just kidding" `rescue` (\msg -> text msg)
 rescue :: (ScottyError e, Monad m) => ActionT e m a -> (e -> ActionT e m a) -> ActionT e m a
 rescue action h = catchError action $ \e -> case e of
-    ActionError err -> h err            -- handle errors
-    other           -> throwError other -- rethrow internal error types
+    ActionError _ err -> h err            -- handle errors
+    other             -> throwError other -- rethrow internal error types
 
 -- | Like 'liftIO', but catch any IO exceptions and turn them into 'ScottyError's.
 liftAndCatchIO :: (ScottyError e, MonadIO m) => IO a -> ActionT e m a
@@ -172,11 +184,35 @@
 bodyReader :: Monad m => ActionT e m (IO B.ByteString)
 bodyReader = ActionT $ getBodyChunk `liftM` ask
 
--- | Parse the request body as a JSON object and return it. Raises an exception if parse is unsuccessful.
+-- | Parse the request body as a JSON object and return it.
+--
+--   If the JSON object is malformed, this sets the status to
+--   400 Bad Request, and throws an exception.
+--
+--   If the JSON fails to parse, this sets the status to
+--   422 Unprocessable Entity.
+--
+--   These status codes are as per https://www.restapitutorial.com/httpstatuscodes.html.
 jsonData :: (A.FromJSON a, ScottyError e, MonadIO m) => ActionT e m a
 jsonData = do
     b <- body
-    either (\e -> raise $ stringError $ "jsonData - no parse: " ++ e ++ ". Data was:" ++ BL.unpack b) return $ A.eitherDecode b
+    when (b == "") $ do
+      let htmlError = "jsonData - No data was provided."
+      raiseStatus status400 $ stringError htmlError
+    case A.eitherDecode b of
+      Left err -> do
+        let htmlError = "jsonData - malformed."
+              `mappend` " Data was: " `mappend` BL.unpack b
+              `mappend` " Error was: " `mappend` err
+        raiseStatus status400 $ stringError htmlError
+      Right value -> case A.fromJSON value of
+        A.Error err -> do
+          let htmlError = "jsonData - failed parse."
+                `mappend` " Data was: " `mappend` BL.unpack b `mappend` "."
+                `mappend` " Error was: " `mappend` err
+          raiseStatus status422 $ stringError htmlError
+        A.Success a -> do
+          return a
 
 -- | Get a parameter. First looks in captures, then form data, then query parameters.
 --
diff --git a/Web/Scotty/Internal/Types.hs b/Web/Scotty/Internal/Types.hs
--- a/Web/Scotty/Internal/Types.hs
+++ b/Web/Scotty/Internal/Types.hs
@@ -16,16 +16,13 @@
 import           Control.Monad.Error.Class
 import qualified Control.Monad.Fail as Fail
 import           Control.Monad.Reader
-import           Control.Monad.State
+import           Control.Monad.State.Strict
 import           Control.Monad.Trans.Control (MonadBaseControl, StM, liftBaseWith, restoreM, ComposeSt, defaultLiftBaseWith, defaultRestoreM, MonadTransControl, StT, liftWith, restoreT)
 import           Control.Monad.Trans.Except
 
 import qualified Data.ByteString as BS
 import           Data.ByteString.Lazy.Char8 (ByteString)
 import           Data.Default.Class (Default, def)
-#if !(MIN_VERSION_base(4,8,0))
-import           Data.Monoid (mempty)
-#endif
 import           Data.String (IsString(..))
 import           Data.Text.Lazy (Text, pack)
 import           Data.Typeable (Typeable)
@@ -37,6 +34,9 @@
 import           Network.Wai.Handler.Warp (Settings, defaultSettings)
 import           Network.Wai.Parse (FileInfo)
 
+import           Prelude ()
+import           Prelude.Compat
+
 --------------------- Options -----------------------
 data Options = Options { verbose :: Int -- ^ 0 = silent, 1(def) = startup banner
                        , settings :: Settings -- ^ Warp 'Settings'
@@ -79,10 +79,11 @@
 
 
 ------------------ Scotty Errors --------------------
-data ActionError e = Redirect Text
-                   | Next
-                   | Finish
-                   | ActionError e
+data ActionError e
+  = Redirect Text
+  | Next
+  | Finish
+  | ActionError Status e
 
 -- | In order to use a custom exception type (aside from 'Text'), you must
 -- define an instance of 'ScottyError' for that type.
@@ -95,11 +96,11 @@
     showError = id
 
 instance ScottyError e => ScottyError (ActionError e) where
-    stringError = ActionError . stringError
+    stringError = ActionError status500 . stringError
     showError (Redirect url)  = url
     showError Next            = pack "Next"
     showError Finish          = pack "Finish"
-    showError (ActionError e) = showError e
+    showError (ActionError _ e) = showError e
 
 type ErrorHandler e m = Maybe (e -> ActionT e m ())
 
@@ -196,6 +197,58 @@
     type StM (ActionT e m) a = ComposeSt (ActionT e) m a
     liftBaseWith = defaultLiftBaseWith
     restoreM     = defaultRestoreM
+
+instance (MonadReader r m, ScottyError e) => MonadReader r (ActionT e m) where
+    {-# INLINE ask #-}
+    ask = lift ask
+    {-# INLINE local #-}
+    local f = ActionT . mapExceptT (mapReaderT (mapStateT $ local f)) . runAM
+
+instance (MonadState s m, ScottyError e) => MonadState s (ActionT e m) where
+    {-# INLINE get #-}
+    get = lift get
+    {-# INLINE put #-}
+    put = lift . put
+
+instance (Semigroup a) => Semigroup (ScottyT e m a) where
+  x <> y = (<>) <$> x <*> y
+
+instance
+  ( Monoid a
+#if !(MIN_VERSION_base(4,11,0))
+  , Semigroup a
+#endif
+#if !(MIN_VERSION_base(4,8,0))
+  , Functor m
+#endif
+  ) => Monoid (ScottyT e m a) where
+  mempty = return mempty
+#if !(MIN_VERSION_base(4,11,0))
+  mappend = (<>)
+#endif
+
+instance
+  ( Monad m
+#if !(MIN_VERSION_base(4,8,0))
+  , Functor m
+#endif
+  , Semigroup a
+  ) => Semigroup (ActionT e m a) where
+  x <> y = (<>) <$> x <*> y
+
+instance
+  ( Monad m, ScottyError e, Monoid a
+#if !(MIN_VERSION_base(4,11,0))
+  , Semigroup a
+#endif
+#if !(MIN_VERSION_base(4,8,0))
+  , Functor m
+#endif
+  ) => Monoid (ActionT e m a) where
+  mempty = return mempty
+#if !(MIN_VERSION_base(4,11,0))
+  mappend = (<>)
+#endif
 
 ------------------ Scotty Routes --------------------
 data RoutePattern = Capture   Text
diff --git a/Web/Scotty/Route.hs b/Web/Scotty/Route.hs
--- a/Web/Scotty/Route.hs
+++ b/Web/Scotty/Route.hs
@@ -14,9 +14,6 @@
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString.Lazy.Char8 as BL
 import           Data.Maybe (fromMaybe, isJust)
-#if !(MIN_VERSION_base(4,8,0))
-import           Data.Monoid (mconcat)
-#endif
 import           Data.String (fromString)
 import qualified Data.Text.Lazy as T
 import qualified Data.Text as TS
@@ -28,6 +25,9 @@
 #endif
 import qualified Network.Wai.Parse as Parse hiding (parseRequestBody)
 
+import           Prelude ()
+import           Prelude.Compat
+
 import qualified Text.Regex as Regex
 
 import           Web.Scotty.Action
@@ -109,7 +109,7 @@
 matchRoute (Literal pat)  req | pat == path req = Just []
                               | otherwise       = Nothing
 matchRoute (Function fun) req = fun req
-matchRoute (Capture pat)  req = go (T.split (=='/') pat) (T.split (=='/') $ path req) []
+matchRoute (Capture pat)  req = go (T.split (=='/') pat) (compress $ T.split (=='/') $ path req) []
     where go [] [] prs = Just prs -- request string and pattern match!
           go [] r  prs | T.null (mconcat r)  = Just prs -- in case request has trailing slashes
                        | otherwise           = Nothing  -- request string is longer than pattern
@@ -119,6 +119,9 @@
                                | T.null p        = Nothing      -- p is null, but r is not, fail
                                | T.head p == ':' = go ps rs $ (T.tail p, r) : prs -- p is a capture, add to params
                                | otherwise       = Nothing      -- both literals, but unequal, fail
+          compress ("":rest@("":_)) = compress rest
+          compress (x:xs) = x : compress xs
+          compress [] = []
 
 -- Pretend we are at the top level.
 path :: Request -> T.Text
diff --git a/Web/Scotty/Trans.hs b/Web/Scotty/Trans.hs
--- a/Web/Scotty/Trans.hs
+++ b/Web/Scotty/Trans.hs
@@ -30,7 +30,7 @@
       -- definition, as they completely replace the current 'Response' body.
     , text, html, file, json, stream, raw
       -- ** Exceptions
-    , raise, rescue, next, finish, defaultHandler, ScottyError(..), liftAndCatchIO
+    , raise, raiseStatus, rescue, next, finish, defaultHandler, ScottyError(..), liftAndCatchIO
       -- * Parsing Parameters
     , Param, Parsable(..), readEither
       -- * Types
@@ -42,7 +42,7 @@
 import Blaze.ByteString.Builder (fromByteString)
 
 import Control.Monad (when)
-import Control.Monad.State (execState, modify)
+import Control.Monad.State.Strict (execState, modify)
 import Control.Monad.IO.Class
 
 import Data.Default.Class (def)
diff --git a/Web/Scotty/Util.hs b/Web/Scotty/Util.hs
--- a/Web/Scotty/Util.hs
+++ b/Web/Scotty/Util.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 module Web.Scotty.Util
     ( lazyTextToStrictByteString
     , strictByteStringToLazyText
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,54 @@
+{-# language
+    OverloadedStrings
+  , GeneralizedNewtypeDeriving
+  #-}
+
+module Main (main) where
+
+import Control.Monad
+import Data.Default.Class (def)
+import Data.Functor.Identity
+import Data.Text (Text)
+import Lucid.Base
+import Lucid.Html5
+import Web.Scotty
+import Web.Scotty.Internal.Types
+import qualified Control.Monad.State.Lazy as SL
+import qualified Control.Monad.State.Strict as SS
+import qualified Data.ByteString.Lazy as BL
+
+import Weigh
+
+main :: IO ()
+main = do
+  mainWith $ do
+    setColumns [Case,Allocated,GCs,Live,Check,Max,MaxOS]
+    setFormat Markdown
+    io "ScottyM Strict" BL.putStr
+      (SS.evalState (runS $ renderBST htmlScotty) def)
+    io "ScottyM Lazy" BL.putStr
+      (SL.evalState (runScottyLazy $ renderBST htmlScottyLazy) def)
+    io "Identity" BL.putStr
+      (runIdentity $ renderBST htmlIdentity)
+
+htmlTest :: Monad m => HtmlT m ()
+htmlTest = replicateM_ 2 $ div_ $ do
+  replicateM_ 1000 $ div_ $ do
+    replicateM_ 10000 $ div_ "test"
+
+htmlIdentity :: HtmlT Identity ()
+htmlIdentity = htmlTest
+{-# noinline htmlIdentity #-}
+
+htmlScotty :: HtmlT ScottyM ()
+htmlScotty = htmlTest
+{-# noinline htmlScotty #-}
+
+htmlScottyLazy :: HtmlT ScottyLazy ()
+htmlScottyLazy = htmlTest
+{-# noinline htmlScottyLazy #-}
+
+newtype ScottyLazy a = ScottyLazy
+  { runScottyLazy:: SL.State (ScottyState Text IO) a }
+  deriving (Functor,Applicative,Monad)
+
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,17 @@
+## 0.12 [2020.05.16]
+* Provide `MonadReader` and `MonadState` instances for `ActionT`.
+* Add HTTP Status code as a field to `ActionError`, and add
+  a sister function to `raise`, `raiseStatus`. This makes
+  throwing a specific error code and exiting much cleaner, and
+  avoids the strange defaulting to HTTP 500. This will make internal
+  functions easier to implement with the right status codes 'thrown',
+  such as `jsonData`.
+* Correct http statuses returned by `jsonData` (#228).
+* Better error message when no data is provided to `jsonData` (#226).
+* Add `Semigroup` and `Monoid` instances for `ActionT` and `ScottyT`
+* ScottyT: Use strict StateT instead of lazy
+* Handle adjacent slashes in the request path as one (thanks @SkyWriter)
+
 ## 0.11.5 [2019.09.07]
 * Allow building the test suite with `hspec-wai-0.10`.
 
diff --git a/examples/LICENSE b/examples/LICENSE
new file mode 100644
--- /dev/null
+++ b/examples/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2012-2017 Andrew Farmer
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Andrew Farmer nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/examples/basic.hs b/examples/basic.hs
--- a/examples/basic.hs
+++ b/examples/basic.hs
@@ -7,14 +7,14 @@
 
 import Control.Monad
 import Control.Monad.Trans
-import Data.Monoid
 import System.Random (newStdGen, randomRs)
 
 import Network.HTTP.Types (status302)
 
 import Data.Text.Lazy.Encoding (decodeUtf8)
 import Data.String (fromString)
-import Prelude
+import Prelude ()
+import Prelude.Compat
 
 main :: IO ()
 main = scotty 3000 $ do
diff --git a/examples/bodyecho.hs b/examples/bodyecho.hs
new file mode 100644
--- /dev/null
+++ b/examples/bodyecho.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main (main) where
+
+import Web.Scotty
+
+import Control.Monad.IO.Class (liftIO)
+import qualified Blaze.ByteString.Builder as B
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.Text.Lazy as T
+
+main :: IO ()
+main = scotty 3000 $ do
+    post "/echo" $ do
+        rd <- bodyReader
+        stream $ ioCopy rd $ return ()
+
+    post "/count" $ do
+        wb <- body -- this must happen before first 'rd'
+        rd <- bodyReader
+        let step acc = do 
+              chunk <- rd
+              putStrLn "got a chunk"
+              let len = BS.length chunk
+              if len > 0 
+                then step $ acc + len
+                else return acc
+        len <- liftIO $ step 0
+        text $ T.pack $ "uploaded " ++ show len ++ " bytes, wb len is " ++ show (BSL.length wb)
+
+
+ioCopy :: IO BS.ByteString -> IO () -> (B.Builder -> IO ()) -> IO () -> IO ()
+ioCopy reader close write flush = step >> flush where
+   step = do chunk <- reader
+             if (BS.length chunk > 0) 
+               then (write $ B.insertByteString chunk) >> step
+               else close
diff --git a/examples/exceptions.hs b/examples/exceptions.hs
--- a/examples/exceptions.hs
+++ b/examples/exceptions.hs
@@ -3,13 +3,13 @@
 
 import Control.Monad.IO.Class
 
-import Data.Monoid
 import Data.String (fromString)
 
 import Network.HTTP.Types
 import Network.Wai.Middleware.RequestLogger
 
-import Prelude
+import Prelude ()
+import Prelude.Compat
 
 import System.Random
 
diff --git a/examples/globalstate.hs b/examples/globalstate.hs
--- a/examples/globalstate.hs
+++ b/examples/globalstate.hs
@@ -9,7 +9,6 @@
 -- embedded into any MonadIO monad.
 module Main (main) where
 
-import Control.Applicative
 import Control.Concurrent.STM
 import Control.Monad.Reader
 
@@ -19,7 +18,8 @@
 
 import Network.Wai.Middleware.RequestLogger
 
-import Prelude
+import Prelude ()
+import Prelude.Compat
 
 import Web.Scotty.Trans
 
diff --git a/examples/reader.hs b/examples/reader.hs
new file mode 100644
--- /dev/null
+++ b/examples/reader.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+{-
+    An example of embedding a custom monad into Scotty's transformer
+    stack, using ReaderT to provide access to a global state.
+-}
+module Main where
+
+import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, asks, lift, runReaderT)
+import Data.Default.Class (def)
+import Data.Text.Lazy (Text, pack)
+import Prelude ()
+import Prelude.Compat
+import Web.Scotty.Trans (ScottyT, get, scottyOptsT, text)
+
+data Config = Config
+  { environment :: String
+  } deriving (Eq, Read, Show)
+
+newtype ConfigM a = ConfigM
+  { runConfigM :: ReaderT Config IO a
+  } deriving (Applicative, Functor, Monad, MonadIO, MonadReader Config)
+
+application :: ScottyT Text ConfigM ()
+application = do
+  get "/" $ do
+    e <- lift $ asks environment
+    text $ pack $ show e
+
+main :: IO ()
+main = scottyOptsT def runIO application where
+  runIO :: ConfigM a -> IO a
+  runIO m = runReaderT (runConfigM m) config
+
+  config :: Config
+  config = Config
+    { environment = "Development"
+    }
diff --git a/examples/upload.hs b/examples/upload.hs
--- a/examples/upload.hs
+++ b/examples/upload.hs
@@ -4,7 +4,6 @@
 import Web.Scotty
 
 import Control.Monad.IO.Class
-import Data.Monoid
 
 import Network.Wai.Middleware.RequestLogger
 import Network.Wai.Middleware.Static
@@ -17,7 +16,8 @@
 import qualified Data.ByteString.Lazy as B
 import qualified Data.ByteString.Char8 as BS
 import System.FilePath ((</>))
-import Prelude
+import Prelude ()
+import Prelude.Compat
 
 main :: IO ()
 main = scotty 3000 $ do
diff --git a/examples/uploads/.keep b/examples/uploads/.keep
new file mode 100644
--- /dev/null
+++ b/examples/uploads/.keep
diff --git a/examples/urlshortener.hs b/examples/urlshortener.hs
--- a/examples/urlshortener.hs
+++ b/examples/urlshortener.hs
@@ -6,13 +6,13 @@
 import Control.Concurrent.MVar
 import Control.Monad.IO.Class
 import qualified Data.Map as M
-import Data.Monoid
 import qualified Data.Text.Lazy as T
 
 import Network.Wai.Middleware.RequestLogger
 import Network.Wai.Middleware.Static
 
-import Prelude
+import Prelude ()
+import Prelude.Compat
 
 import qualified Text.Blaze.Html5 as H
 import Text.Blaze.Html5.Attributes
diff --git a/scotty.cabal b/scotty.cabal
--- a/scotty.cabal
+++ b/scotty.cabal
@@ -1,5 +1,5 @@
 Name:                scotty
-Version:             0.11.5
+Version:             0.11.6
 Synopsis:            Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp
 Homepage:            https://github.com/scotty-web/scotty
 Bug-reports:         https://github.com/scotty-web/scotty/issues
@@ -51,21 +51,17 @@
                    , GHC == 8.2.2
                    , GHC == 8.4.4
                    , GHC == 8.6.5
-                   , GHC == 8.8.1
+                   , GHC == 8.8.3
+                   , GHC == 8.10.1
 Extra-source-files:
     README.md
     changelog.md
     examples/404.html
-    examples/basic.hs
-    examples/cookies.hs
-    examples/exceptions.hs
-    examples/globalstate.hs
-    examples/gzip.hs
-    examples/options.hs
-    examples/upload.hs
-    examples/urlshortener.hs
+    examples/LICENSE
+    examples/*.hs
     examples/static/jquery.js
     examples/static/jquery-json.js
+    examples/uploads/.keep
 
 Library
   Exposed-modules:     Web.Scotty
@@ -75,27 +71,28 @@
                        Web.Scotty.Route
                        Web.Scotty.Util
   default-language:    Haskell2010
-  build-depends:       aeson               >= 0.6.2.1  && < 1.5,
-                       base                >= 4.6      && < 5,
-                       blaze-builder       >= 0.3.3.0  && < 0.5,
-                       bytestring          >= 0.10.0.2 && < 0.11,
-                       case-insensitive    >= 1.0.0.1  && < 1.3,
-                       data-default-class  >= 0.0.1    && < 0.2,
-                       exceptions          >= 0.7      && < 0.11,
+  build-depends:       aeson                 >= 0.6.2.1  && < 1.5,
+                       base                  >= 4.6      && < 5,
+                       base-compat-batteries >= 0.10     && < 0.12,
+                       blaze-builder         >= 0.3.3.0  && < 0.5,
+                       bytestring            >= 0.10.0.2 && < 0.11,
+                       case-insensitive      >= 1.0.0.1  && < 1.3,
+                       data-default-class    >= 0.0.1    && < 0.2,
+                       exceptions            >= 0.7      && < 0.11,
                        fail,
-                       http-types          >= 0.8.2    && < 0.13,
-                       monad-control       >= 1.0.0.3  && < 1.1,
-                       mtl                 >= 2.1.2    && < 2.3,
-                       nats                >= 0.1      && < 2,
-                       network             >= 2.6.0.2  && < 3.2,
-                       regex-compat        >= 0.95.1   && < 0.96,
-                       text                >= 0.11.3.1 && < 1.3,
-                       transformers        >= 0.3.0.0  && < 0.6,
-                       transformers-base   >= 0.4.1    && < 0.5,
-                       transformers-compat >= 0.4      && < 0.7,
-                       wai                 >= 3.0.0    && < 3.3,
-                       wai-extra           >= 3.0.0    && < 3.1,
-                       warp                >= 3.0.13   && < 3.4
+                       http-types            >= 0.9.1    && < 0.13,
+                       monad-control         >= 1.0.0.3  && < 1.1,
+                       mtl                   >= 2.1.2    && < 2.3,
+                       nats                  >= 0.1      && < 2,
+                       network               >= 2.6.0.2  && < 3.2,
+                       regex-compat          >= 0.95.1   && < 0.96,
+                       text                  >= 0.11.3.1 && < 1.3,
+                       transformers          >= 0.3.0.0  && < 0.6,
+                       transformers-base     >= 0.4.1    && < 0.5,
+                       transformers-compat   >= 0.4      && < 0.7,
+                       wai                   >= 3.0.0    && < 3.3,
+                       wai-extra             >= 3.0.0    && < 3.1,
+                       warp                  >= 3.0.13   && < 3.4
 
   GHC-options: -Wall -fno-warn-orphans
 
@@ -120,6 +117,22 @@
                        wai
   build-tool-depends:  hspec-discover:hspec-discover == 2.*
   GHC-options:         -Wall -threaded -fno-warn-orphans
+
+benchmark weigh
+  main-is:             Main.hs
+  type:                exitcode-stdio-1.0
+  default-language:    Haskell2010
+  hs-source-dirs:      bench
+  build-depends:       base,
+                       scotty,
+                       lucid,
+                       bytestring,
+                       mtl,
+                       text,
+                       transformers,
+                       data-default-class,
+                       weigh == 0.0.16
+  GHC-options:         -Wall -O2 -threaded
 
 source-repository head
   type:     git
diff --git a/test/Web/ScottySpec.hs b/test/Web/ScottySpec.hs
--- a/test/Web/ScottySpec.hs
+++ b/test/Web/ScottySpec.hs
@@ -48,6 +48,10 @@
           it ("adds route for " ++ method ++ " requests") $ do
             makeRequest "/scotty" `shouldRespondWith` 200
 
+        withApp (route "/scotty" $ html "") $ do
+          it ("properly handles extra slash routes for " ++ method ++ " requests") $ do
+            makeRequest "//scotty" `shouldRespondWith` 200
+
     describe "addroute" $ do
       forM_ availableMethods $ \method -> do
         withApp (addroute method "/scotty" $ html "") $ do
