diff --git a/mighttpd2.cabal b/mighttpd2.cabal
--- a/mighttpd2.cabal
+++ b/mighttpd2.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               mighttpd2
-version:            4.0.11
+version:            4.0.12
 license:            BSD3
 license-file:       LICENSE
 maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>
@@ -84,7 +84,7 @@
         unordered-containers,
         wai >=3.2 && <3.3,
         wai-app-file-cgi >=3.3 && <3.4,
-        warp >=3.4 && <3.5
+        warp >=3.4.21 && <3.5
 
     if impl(ghc >=8)
         default-extensions: Strict StrictData
@@ -131,7 +131,7 @@
         wai >=3.2 && <3.3,
         wai-app-file-cgi >=3.3 && <3.4,
         wai-logger >=2.3.0,
-        warp >=3.4 && <3.5
+        warp >=3.4.21 && <3.5
 
     if flag(tls)
         cpp-options:   -DHTTP_OVER_TLS
@@ -152,12 +152,12 @@
     if flag(tls)
         build-depends:
             tls,
-            warp-tls >=3.2.12 && <3.5
+            warp-tls >=3.4.17 && <3.5
 
     if flag(quic)
         build-depends:
-            quic >= 0.3 && < 0.4,
-            warp-quic >= 0.0.2
+            quic >= 0.3.20 && < 0.4,
+            warp-quic >= 0.0.5
 
     if impl(ghc >=8)
         default-extensions: Strict StrictData
@@ -213,12 +213,12 @@
         unix,
         wai,
         wai-app-file-cgi >=3.3 && <3.4,
-        warp >=3.4 && <3.5
+        warp >=3.4.21 && <3.5
 
     if flag(tls)
         build-depends:
             tls,
-            warp-tls >=3.2.12 && <3.5
+            warp-tls >=3.4.17 && <3.5
 
     if impl(ghc >=8)
         default-extensions: Strict StrictData
diff --git a/src/Server.hs b/src/Server.hs
--- a/src/Server.hs
+++ b/src/Server.hs
@@ -7,6 +7,7 @@
 import Control.Concurrent (runInUnboundThread)
 import Control.Monad (unless, when)
 import qualified Data.ByteString.Char8 as BS
+import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef)
 import Data.Streaming.Network (bindPortTCP, bindPortUDP)
 #if __GLASGOW_HASKELL__ >= 906
 import GHC.Conc.Sync
@@ -102,12 +103,13 @@
                 pushlgr = serverpushLogger ap
             -- HTTP Client Manager
             mgr <- getManager opt
-            setHandlers opt rpt svc remover rdr
+            stoppers <- newStoppers
+            setHandlers opt rpt svc stoppers remover rdr
 
             report rpt "Mighty started"
             runInUnboundThread $ do
                 labelMe "Mighty main (bound thread)"
-                mighty opt rpt svc lgr pushlgr mgr rdr mcred smgr tmgr
+                mighty opt rpt svc stoppers lgr pushlgr mgr rdr mcred smgr tmgr
             report rpt "Mighty retired"
             remover
             exitSuccess
@@ -131,8 +133,32 @@
       | debug                 = LogStdout $ naturalToInt logBufferSize
       | otherwise             = LogFile logspec $ naturalToInt logBufferSize
 
-setHandlers :: Option -> Reporter -> Service -> LogRemover -> RouteDBRef -> IO ()
-setHandlers opt rpt svc remover rdr = do
+-- | The actions that stop the servers, as warp, warp-tls and warp-quic
+--   hand them over.
+--
+-- Each of them stops its own server where it waits for work -- no socket is
+-- closed under it and nothing is raised -- and leaves whatever it was
+-- serving to finish.  A QUIC server also tells the peer of every live
+-- connection that it is going, which closing its socket could not.
+--
+-- There is one shutdown handler in warp's settings and up to three servers
+-- sharing it, so what the handler does is keep what it is given, and
+-- retiring runs all of it.
+newtype Stoppers = Stoppers (IORef [IO ()])
+
+newStoppers :: IO Stoppers
+newStoppers = Stoppers <$> newIORef []
+
+keepStopper :: Stoppers -> IO () -> IO ()
+keepStopper (Stoppers ref) stop = atomicModifyIORef' ref $ \stops -> (stop : stops, ())
+
+stopServers :: Stoppers -> IO ()
+stopServers (Stoppers ref) = readIORef ref >>= sequence_
+
+----------------------------------------------------------------
+
+setHandlers :: Option -> Reporter -> Service -> Stoppers -> LogRemover -> RouteDBRef -> IO ()
+setHandlers opt rpt svc stoppers remover rdr = do
     setHandler sigStop   stopHandler
     setHandler sigRetire retireHandler
     setHandler sigInfo   infoHandler
@@ -145,7 +171,9 @@
         exitImmediately ExitSuccess
     retireHandler = Catch $ do
         report rpt "Mighty retiring"
-        closeService svc -- this lets warp break
+        -- Each server stops accepting where it waits, closes its own
+        -- listening socket, and finishes what it is serving.
+        stopServers stoppers
     infoHandler = Catch $ do
         labelMe "Info signale handler"
         threadSummary >>= mapM_ (report rpt . showT)
@@ -200,12 +228,12 @@
 
 ----------------------------------------------------------------
 
-mighty :: Option -> Reporter -> Service
+mighty :: Option -> Reporter -> Service -> Stoppers
        -> ApacheLogger -> ServerPushLogger
        -> ConnPool -> RouteDBRef
        -> Maybe Credentials -> Maybe SessionManager -> T.Manager
        -> IO ()
-mighty opt rpt svc lgr pushlgr mgr rdr _mcreds _msmgr tmgr
+mighty opt rpt svc stoppers lgr pushlgr mgr rdr _mcreds _msmgr tmgr
   = reportDo rpt $ case svc of
     HttpOnly s  -> runHTTP setting s app
 #ifdef HTTP_OVER_TLS
@@ -236,9 +264,8 @@
 #endif
   where
     app = fileCgiApp cspec filespec cgispec revproxyspec rdr
-    -- We don't use setInstallShutdownHandler because we may use
-    -- two sockets for HTTP and HTTPS.
-    setting = setPort            (naturalToInt $ opt_port opt) -- just in case
+    setting = setInstallShutdownHandler (keepStopper stoppers)
+            $ setPort            (naturalToInt $ opt_port opt) -- just in case
             $ setHost            (fromString (opt_host opt))  -- just in case
             $ setManager         tmgr
             $ setFdCacheDuration (naturalToInt $ opt_fd_cache_duration opt)
