diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,7 +1,13 @@
+## 0.6.1
+
+- multiple certificates and SNI support for HTTP/3
+- install signal handler with graceful shutdown on Linux and macOS
+- support ACME `http-01` challenge (RFC8555)
+
 ## 0.6.0
 
 - `--rev` now supports domain matching
-- fix `Content-Length` header in HTTP/2 responses.
+- fix `Content-Length` header in HTTP/2 responses
 - passwords are now Argon2 salt-hashed
 
 ## 0.5.4
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -12,60 +12,61 @@
 ### Features
 
 * Basic HTTP proxy functionality.
-* Simple password authentication.
-* TLS encryption (requires a valid certificate). Supports TLS 1.3 and HTTP/2, also known as SPDY Proxy.
-* TLS SNI validation (blocks all clients with invalid domain name).
-* Provide PAC file for easy client side configuration (supports Chrome and Firefox).
+* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) password authentication.
+* Enables TLS encryption, requiring a valid certificate. Supports TLS 1.3 and HTTP/2, also known as SPDY Proxy.
+* TLS SNI validation (blocks all clients with an invalid domain name).
+* Provides a PAC file for seamless client-side configuration, compatible with browsers like Chrome and Firefox.
 * Websocket redirection (compatible with [v2ray-plugin](https://github.com/shadowsocks/v2ray-plugin)).
-* Reverse proxy support (redirect requests to a fallback server).
+* Reverse proxy support (redirects requests to a fallback server).
 * DNS-over-HTTPS (DoH) support.
 * [naiveproxy](https://github.com/klzgrad/naiveproxy) compatible [padding](https://github.com/klzgrad/naiveproxy/#padding-protocol-an-informal-specification) (HTTP Connect proxy).
 * HTTP/3 (QUIC) support (`h3` protocol).
-* Implemented as a middleware, compatible with any Haskell Web Application built with `wai` interface.
-  See [library documents](https://hackage.haskell.org/package/hprox) for details.
+* ACME `http-01` challenge as specified by RFC8555, see [acme.sh stateless mode](https://github.com/acmesh-official/acme.sh/wiki/Stateless-Mode).
+* Designed as a middleware, ensuring compatibility with any Haskell Web Application built using the `wai` interface.
+  Refer to [library documents](https://hackage.haskell.org/package/hprox) for details.
 
 ### Installation
 
-`hprox` should build and work on all unix-like OS with `ghc` support, as well as Windows.
+`hprox` is designed to build and function seamlessly on all Unix-like operating systems with GHC support, as well as on Windows environments.
 
-[stack](https://docs.haskellstack.org/en/stable/README/#how-to-install) is recommended to build `hprox`.
+[stack](https://docs.haskellstack.org/en/stable/README/#how-to-install) is recommended for building `hprox`.
 
 ```sh
 stack setup
 stack install
 ```
 
-Alternatively, you also can use the statically linked binary for the [latest release](https://github.com/bjin/hprox/releases).
+Alternatively, you have the option to utilize the statically linked binary available in the [latest release](https://github.com/bjin/hprox/releases).
 
 ### Usage
 
-Use `hprox --help` to list options with detailed explanation.
+Utilize `hprox --help` to view a comprehensive list of options along with detailed explanations.
 
-* To run `hprox` on port 8080, with simple password authentication:
+* To run `hprox` on port 8080 with simple password authentication (passwords will be [Argon2-hashed](https://en.wikipedia.org/wiki/Argon2) after first run):
 
 ```sh
 echo "user:pass" > userpass.txt
 hprox -p 8080 -a userpass.txt
 ```
 
-* To run `hprox` with TLS encryption on port 443, with certificate of `example.com` obtained with [acme.sh](https://acme.sh/):
+* To run `hprox` with TLS encryption on port 443, with a certificate for `example.com` obtained with [acme.sh](https://acme.sh/):
 
 ```sh
 hprox -p 443 -s example.com:$HOME/.acme.sh/example.com/fullchain.cer:$HOME/.acme.sh/example.com/example.com.key
 ```
 
-Browsers can be configured with PAC file URL `https://example.com/.hprox/config.pac`.
+Browsers can be configured with the PAC file URL `https://example.com/.hprox/config.pac`.
 
-* To work with `v2ray-plugin`, with fallback page to [ubuntu archive](http://archive.ubuntu.com/):
+* For integration with `v2ray-plugin` and a fallback page to the [Ubuntu archive](http://archive.ubuntu.com/):
 
 ```sh
 v2ray-plugin -server -localPort 8080 -mode websocket -host example.com -remotePort xxxx
 hprox -p 443 -s example.com:fullchain.pem:privkey.pem --ws 127.0.0.1:8080 --rev archive.ubuntu.com:80
 ```
 
-Clients will be able to connect with plugin option `tls;host=example.com`.
+Clients can establish connections using the plugin option `tls;host=example.com`.
 
-* Enable HTTP/3 (QUIC) on UDP port 8443, enable DoH support (redirect to 8.8.8.8), and add `naiveproxy` compatible padding:
+* Enable HTTP/3 (QUIC) on UDP port 8443, enable DoH support (redirect to 8.8.8.8), and add naiveproxy compatible padding:
 
 ```sh
 hprox -p 443 -q 8443 -s example.com:fullchain.pem:privkey.pem -a userpass.txt --naive --doh 8.8.8.8
@@ -73,11 +74,6 @@
 
 Then DoH can be accessed at `https://example.com/dns-query`.
 
-### Known Issue
-
-* HTTP/3 currently only works on the first domain as specified by `-s/--tls`, also SNI
-  validation is unavailable as well.
-
 ### License
 
-`hprox` is licensed under the Apache license. See LICENSE file for details.
+`hprox` is licensed under the Apache license. Refer to the `LICENSE` file for comprehensive details.
diff --git a/hprox.cabal b/hprox.cabal
--- a/hprox.cabal
+++ b/hprox.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hprox
-version:        0.6.0
+version:        0.6.1
 synopsis:       a lightweight HTTP proxy server, and more
 description:    Please see the README on GitHub at <https://github.com/bjin/hprox#readme>
 category:       Web
@@ -62,15 +62,17 @@
     , conduit >=1.3
     , conduit-extra >=1.3
     , crypton
+    , data-default-class
     , dns >=4.0
     , fast-logger >=3.0
     , http-client >=0.5
     , http-client-tls >=0.3.4
-    , http-reverse-proxy >=0.4.0
+    , http-reverse-proxy >=0.6.0.2
     , http-types >=0.12
     , http2 >=4.0
     , optparse-applicative >=0.14
     , random >=1.2.1
+    , text
     , tls >=1.5
     , tls-session-manager >=0.0.4
     , unordered-containers
@@ -83,8 +85,12 @@
     cpp-options: -DQUIC_ENABLED
     build-depends:
         http3 >=0.0.3
-      , quic >=0.1.1
+      , quic >=0.1.15
       , warp-quic
+  if !os(windows)
+    cpp-options: -DOS_UNIX
+    build-depends:
+        unix
 
 executable hprox
   main-is: Main.hs
@@ -107,16 +113,18 @@
     , conduit >=1.3
     , conduit-extra >=1.3
     , crypton
+    , data-default-class
     , dns >=4.0
     , fast-logger >=3.0
     , hprox
     , http-client >=0.5
     , http-client-tls >=0.3.4
-    , http-reverse-proxy >=0.4.0
+    , http-reverse-proxy >=0.6.0.2
     , http-types >=0.12
     , http2 >=4.0
     , optparse-applicative >=0.14
     , random >=1.2.1
+    , text
     , tls >=1.5
     , tls-session-manager >=0.0.4
     , unordered-containers
@@ -129,7 +137,11 @@
     cpp-options: -DQUIC_ENABLED
     build-depends:
         http3 >=0.0.3
-      , quic >=0.1.1
+      , quic >=0.1.15
       , warp-quic
+  if !os(windows)
+    cpp-options: -DOS_UNIX
+    build-depends:
+        unix
   if flag(static)
     ghc-options: -optl-static
diff --git a/src/Network/HProx.hs b/src/Network/HProx.hs
--- a/src/Network/HProx.hs
+++ b/src/Network/HProx.hs
@@ -19,7 +19,10 @@
   ) where
 
 import Data.ByteString.Char8       qualified as BS8
+import Data.Default.Class          (def)
 import Data.HashMap.Strict         qualified as HM
+import Data.List
+    (elemIndex, elemIndices, find, isSuffixOf, sortOn, (\\))
 import Data.Ord                    (Down (..))
 import Data.String                 (fromString)
 import Data.Version                (showVersion)
@@ -34,8 +37,9 @@
     runSettings, setHost, setLogger, setNoParsePath, setOnException, setPort,
     setServerName)
 import Network.Wai.Handler.WarpTLS
-    (OnInsecure (..), WarpTLSException, onInsecure, runTLS, tlsAllowedVersions,
-    tlsCiphers, tlsServerHooks, tlsSessionManager, tlsSettings)
+    (OnInsecure (..), WarpTLSException, defaultTlsSettings, onInsecure, runTLS,
+    tlsAllowedVersions, tlsCiphers, tlsCredentials, tlsServerHooks,
+    tlsSessionManager)
 
 import Control.Exception    (Exception (..))
 import GHC.IO.Exception     (IOErrorType (..))
@@ -50,8 +54,13 @@
 import Network.Wai.Handler.WarpQUIC (runQUIC)
 #endif
 
+#ifdef OS_UNIX
+import Network.Wai.Handler.Warp
+    (setGracefulShutdownTimeout, setInstallShutdownHandler)
+import System.Posix.Signals
+#endif
+
 import Control.Monad
-import Data.List
 import Data.Maybe
 import Options.Applicative
 
@@ -67,12 +76,13 @@
   , _port     :: Int
   , _ssl      :: [(String, CertFile)]
   , _auth     :: Maybe FilePath
-  , _ws       :: Maybe String
+  , _ws       :: Maybe BS8.ByteString
   , _rev      :: [(Maybe BS8.ByteString, BS8.ByteString, BS8.ByteString)]
   , _doh      :: Maybe String
   , _hide     :: Bool
   , _naive    :: Bool
   , _name     :: BS8.ByteString
+  , _acme     :: Maybe BS8.ByteString
   , _log      :: String
   , _loglevel :: LogLevel
 #ifdef QUIC_ENABLED
@@ -82,7 +92,7 @@
 
 -- | Default value of 'Config', same as running @hprox@ without arguments
 defaultConfig :: Config
-defaultConfig = Config Nothing 3000 [] Nothing Nothing [] Nothing False False "hprox" "stdout" INFO
+defaultConfig = Config Nothing 3000 [] Nothing Nothing [] Nothing False False "hprox" Nothing "stdout" INFO
 #ifdef QUIC_ENABLED
     Nothing
 #endif
@@ -130,6 +140,7 @@
                     <*> hide
                     <*> naive
                     <*> name
+                    <*> acme
                     <*> logging
                     <*> loglevel
 #ifdef QUIC_ENABLED
@@ -192,6 +203,11 @@
        <> showDefault
        <> help "Specify the server name for the 'Server' header")
 
+    acme = optional $ strOption
+        ( long "acme"
+       <> metavar "ACCOUNT_THUMBPRINT"
+       <> help "Set the thumbprint for stateless http-01 ACME challenge as specified by RFC8555")
+
     logging = strOption
         ( long "log"
        <> metavar "<none|stdout|stderr|file>"
@@ -237,21 +253,29 @@
     smgr <- SM.newSessionManager SM.defaultConfig
 
     let isSSL = not (null certfiles)
-        (primaryHost, primaryCert) = head certfiles
-        otherCerts = tail $ zip (map fst certfiles) certs
+        allCerts = zip (map fst certfiles) certs
 
     when isSSL $ do
         logger INFO $ "read " <> toLogStr (show $ length certs) <> " certificates"
-        logger INFO $ "primary domain: " <> toLogStr primaryHost
-        logger INFO $ "other domains: " <> toLogStr (unwords $ map fst otherCerts)
+        logger INFO $ "domains: " <> toLogStr (unwords $ map fst allCerts)
 
     let settings = setHost (fromString (fromMaybe "*6" _bind)) $
                    setPort _port $
                    setLogger warpLogger $
                    setOnException exceptionHandler $
+#ifdef OS_UNIX
+                   setGracefulShutdownTimeout (Just 3) $
+                   setInstallShutdownHandler shutdownHandler $
+#endif
                    setNoParsePath True $
                    setServerName _name defaultSettings
 
+#ifdef OS_UNIX
+        shutdownHandler closeSocket = do
+            void $ installHandler sigTERM (CatchOnce $ logger INFO "Received SIGTERM signal, shutting down gracefully" >> closeSocket) Nothing
+            void $ installHandler sigINT (CatchOnce $ logger INFO "Received SIGINT signal, shutting down gracefully" >> closeSocket) Nothing
+#endif
+
         exceptionHandler req ex
             | _loglevel > DEBUG                                 = return ()
             | not (defaultShouldDisplayException ex)            = return ()
@@ -273,9 +297,6 @@
             | otherwise                           =
                 logger TRACE $ "(" <> toLogStr (HT.statusCode status) <> ") " <> logRequest req
 
-        tlsset' = tlsSettings (certfile primaryCert) (keyfile primaryCert)
-        hooks = (tlsServerHooks tlsset') { TLS.onServerNameIndication = onSNI }
-
         -- https://www.ssllabs.com/ssltest
         weak_ciphers = [ TLS.cipher_ECDHE_RSA_AES256CBC_SHA384
                        , TLS.cipher_ECDHE_RSA_AES256CBC_SHA
@@ -285,18 +306,17 @@
                        , TLS.cipher_AES256_SHA1
                        ]
 
-        tlsset = tlsset'
-            { tlsServerHooks     = hooks
+        tlsset = defaultTlsSettings
+            { tlsServerHooks     = def { TLS.onServerNameIndication = onSNI }
+            , tlsCredentials     = Just (TLS.Credentials certs)
             , onInsecure         = AllowInsecure
             , tlsAllowedVersions = [TLS.TLS13, TLS.TLS12]
             , tlsCiphers         = TLS.ciphersuite_strong \\ weak_ciphers
             , tlsSessionManager  = Just smgr
             }
 
-        onSNI Nothing = fail "SNI: unspecified"
-        onSNI (Just host)
-          | checkSNI host primaryHost = return mempty
-          | otherwise                 = lookupSNI host otherCerts
+        onSNI Nothing     = fail "SNI: unspecified"
+        onSNI (Just host) = lookupSNI host allCerts
 
         lookupSNI host [] = fail ("SNI: unknown hostname (" ++ show host ++ ")")
         lookupSNI host ((p, cert) : cs)
@@ -314,9 +334,10 @@
         quicset qport = Q.defaultServerConfig
             { Q.scAddresses      = [(fromString (fromMaybe "0.0.0.0" _bind), fromIntegral qport)]
             , Q.scVersions       = [Q.Version1, Q.Version2]
-            , Q.scCredentials    = TLS.Credentials [head certs]
+            , Q.scCredentials    = TLS.Credentials certs
             , Q.scCiphers        = Q.scCiphers Q.defaultServerConfig \\ weak_ciphers
             , Q.scALPN           = Just alpn
+            , Q.scTlsHooks       = def { TLS.onServerNameIndication = onSNI }
             , Q.scUse0RTT        = True
             , Q.scSessionManager = smgr
             }
@@ -363,8 +384,9 @@
     manager <- newTlsManager
 
     let revSorted = sortOn (\(a,b,_) -> Down (isJust a, BS8.length b)) _rev
-        pset = ProxySettings pauth (Just _name) (BS8.pack <$> _ws) revSorted _hide (_naive && isSSL) logger
+        pset = ProxySettings pauth (Just _name) _ws revSorted _hide (_naive && isSSL) _acme logger
         proxy = healthCheckProvider $
+                acmeProvider pset $
                 (if isSSL then forceSSL pset else id) $
                 httpProxy pset manager $
                 reverseProxy pset manager fallback
diff --git a/src/Network/HProx/Impl.hs b/src/Network/HProx/Impl.hs
--- a/src/Network/HProx/Impl.hs
+++ b/src/Network/HProx/Impl.hs
@@ -4,6 +4,7 @@
 
 module Network.HProx.Impl
   ( ProxySettings (..)
+  , acmeProvider
   , forceSSL
   , healthCheckProvider
   , httpConnectProxy
@@ -27,6 +28,7 @@
 import Data.ByteString.Lazy.Char8 qualified as LBS8
 import Data.CaseInsensitive       qualified as CI
 import Data.Conduit.Network       qualified as CN
+import Data.Text.Encoding         qualified as TE
 import Network.HTTP.Client        qualified as HC
 import Network.HTTP.ReverseProxy
     (ProxyDest (..), SetIpHeader (..), WaiProxyResponse (..),
@@ -46,13 +48,14 @@
 import Network.HProx.Util
 
 data ProxySettings = ProxySettings
-  { proxyAuth     :: Maybe (BS.ByteString -> Bool)
-  , passPrompt    :: Maybe BS.ByteString
-  , wsRemote      :: Maybe BS.ByteString
-  , revRemoteMap  :: [(Maybe BS.ByteString, BS.ByteString, BS.ByteString)]
-  , hideProxyAuth :: Bool
-  , naivePadding  :: Bool
-  , logger        :: Logger
+  { proxyAuth      :: Maybe (BS.ByteString -> Bool)
+  , passPrompt     :: Maybe BS.ByteString
+  , wsRemote       :: Maybe BS.ByteString
+  , revRemoteMap   :: [(Maybe BS.ByteString, BS.ByteString, BS.ByteString)]
+  , hideProxyAuth  :: Bool
+  , naivePadding   :: Bool
+  , acmeThumbprint :: Maybe BS.ByteString
+  , logger         :: Logger
   }
 
 logRequest :: Request -> LogStr
@@ -122,6 +125,18 @@
     ""
   where
     prompt = fromMaybe "hprox" passPrompt
+
+acmeProvider :: ProxySettings -> Middleware
+acmeProvider ProxySettings{..} app req respond
+    | not (isSecure req)
+    , Just thumbprint <- acmeThumbprint
+    , [".well-known", "acme-challenge", token] <- pathInfo req
+        = respond $ responseKnownLength
+              HT.status200
+              [("Content-Type", "text/plain")] $
+              LBS.fromChunks [TE.encodeUtf8 token, ".", thumbprint]
+    | otherwise
+        = app req respond
 
 pacProvider :: Middleware
 pacProvider fallback req respond
