diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Revision history for http3
 
+## 0.0.20
+
+* Unregistering handle to remove ThreadId to prevent temporary
+  thread leak.
+
 ## 0.0.19
 
 * Labeling threads.
diff --git a/Network/HTTP3/Client.hs b/Network/HTTP3/Client.hs
--- a/Network/HTTP3/Client.hs
+++ b/Network/HTTP3/Client.hs
@@ -26,6 +26,7 @@
 import Network.QUIC (Connection)
 import qualified Network.QUIC as QUIC
 import Network.QUIC.Internal (possibleMyStreams)
+import qualified System.TimeManager as T
 
 import Imports
 import Network.HTTP3.Config
@@ -79,30 +80,30 @@
 
 sendRequest
     :: Context -> Scheme -> Authority -> Request -> (Response -> IO a) -> IO a
-sendRequest ctx scm auth (Request outobj) processResponse = do
-    th <- registerThread ctx
-    let hdr = outObjHeaders outobj
-        hdr' =
-            (":scheme", scm)
-                : (":authority", C8.pack auth)
-                : hdr
-    E.bracket (newStream ctx) closeStream $ \strm -> do
-        sendHeader ctx strm th hdr'
-        tid <- forkIO $ do
-            sendBody ctx strm th outobj
-            QUIC.shutdownStream strm
-        addThreadId ctx tid
-        src <- newSource strm
-        mvt <- recvHeader ctx src
-        case mvt of
-            Nothing -> do
-                QUIC.resetStream strm H3MessageError
-                threadDelay 100000
-                -- just for type inference
-                E.throwIO $ QUIC.ApplicationProtocolErrorIsSent H3MessageError ""
-            Just vt -> do
-                refI <- newIORef IInit
-                refH <- newIORef Nothing
-                let readB = recvBody ctx src refI refH
-                    rsp = Response $ InpObj vt Nothing readB refH
-                processResponse rsp
+sendRequest ctx scm auth (Request outobj) processResponse =
+    E.bracket (registerThread ctx) T.cancel $ \th -> do
+        let hdr = outObjHeaders outobj
+            hdr' =
+                (":scheme", scm)
+                    : (":authority", C8.pack auth)
+                    : hdr
+        E.bracket (newStream ctx) closeStream $ \strm -> do
+            sendHeader ctx strm th hdr'
+            tid <- forkIO $ do
+                sendBody ctx strm th outobj
+                QUIC.shutdownStream strm
+            addThreadId ctx tid
+            src <- newSource strm
+            mvt <- recvHeader ctx src
+            case mvt of
+                Nothing -> do
+                    QUIC.resetStream strm H3MessageError
+                    threadDelay 100000
+                    -- just for type inference
+                    E.throwIO $ QUIC.ApplicationProtocolErrorIsSent H3MessageError ""
+                Just vt -> do
+                    refI <- newIORef IInit
+                    refH <- newIORef Nothing
+                    let readB = recvBody ctx src refI refH
+                        rsp = Response $ InpObj vt Nothing readB refH
+                    processResponse rsp
diff --git a/Network/HTTP3/Context.hs b/Network/HTTP3/Context.hs
--- a/Network/HTTP3/Context.hs
+++ b/Network/HTTP3/Context.hs
@@ -12,7 +12,6 @@
     qpackEncode,
     qpackDecode,
     registerThread,
-    timeoutClose,
     newStream,
     closeStream,
     pReadMaker,
@@ -111,11 +110,6 @@
 
 registerThread :: Context -> IO T.Handle
 registerThread Context{..} = T.registerKillThread ctxManager (return ())
-
-timeoutClose :: Context -> IO () -> IO (IO ())
-timeoutClose Context{..} closer = do
-    th <- T.register ctxManager closer
-    return $ T.tickle th
 
 newStream :: Context -> IO Stream
 newStream Context{..} = stream ctxConnection
diff --git a/Network/HTTP3/Server.hs b/Network/HTTP3/Server.hs
--- a/Network/HTTP3/Server.hs
+++ b/Network/HTTP3/Server.hs
@@ -107,15 +107,15 @@
 processRequest ctx server strm = E.handle reset $ do
     tid <- myThreadId
     labelThread tid "H3 processRequest"
-    th <- registerThread ctx
-    src <- newSource strm
-    mvt <- recvHeader ctx src
-    case mvt of
-        Nothing -> QUIC.resetStream strm H3MessageError
-        Just ht -> do
-            req <- mkRequest ctx strm src ht
-            let aux = Aux th (getMySockAddr ctx) (getPeerSockAddr ctx)
-            server req aux $ sendResponse ctx strm th
+    E.bracket (registerThread ctx) T.cancel $ \th -> do
+        src <- newSource strm
+        mvt <- recvHeader ctx src
+        case mvt of
+            Nothing -> QUIC.resetStream strm H3MessageError
+            Just ht -> do
+                req <- mkRequest ctx strm src ht
+                let aux = Aux th (getMySockAddr ctx) (getPeerSockAddr ctx)
+                server req aux $ sendResponse ctx strm th
   where
     reset se
         | Just E.ThreadKilled <- E.fromException se = return ()
@@ -170,8 +170,8 @@
 
 sendResponseIO
     :: Context -> Stream -> Response -> IO ()
-sendResponseIO ctx strm (Response outobj) = do
-    th <- registerThread ctx -- fixme
-    sendHeader ctx strm th $ outObjHeaders outobj
-    sendBody ctx strm th outobj
-    QUIC.closeStream strm
+sendResponseIO ctx strm (Response outobj) =
+    E.bracket (registerThread ctx) T.cancel $ \th -> do
+        sendHeader ctx strm th $ outObjHeaders outobj
+        sendBody ctx strm th outobj
+        QUIC.closeStream strm
diff --git a/http3.cabal b/http3.cabal
--- a/http3.cabal
+++ b/http3.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               http3
-version:            0.0.19
+version:            0.0.20
 license:            BSD-3-Clause
 license-file:       LICENSE
 maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>
