diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Change log for "tls"
 
+## Version 2.4.1
+
+* Ensure same `supported_groups` before/after HRR.
+* New `clientWantTicket` parameter makes it possible to opt-out of soliciting
+  session tickets from servers.
+
 ## Version 2.4.0
 
 * Idential to v2.3.1 but major version up as v2.3.1 breaks "quic".
diff --git a/Network/TLS.hs b/Network/TLS.hs
--- a/Network/TLS.hs
+++ b/Network/TLS.hs
@@ -46,6 +46,7 @@
     clientUseServerNameIndication,
     clientWantSessionResume,
     clientWantSessionResumeList,
+    clientWantTicket,
     clientShared,
     clientHooks,
     clientSupported,
diff --git a/Network/TLS/Handshake/Client.hs b/Network/TLS/Handshake/Client.hs
--- a/Network/TLS/Handshake/Client.hs
+++ b/Network/TLS/Handshake/Client.hs
@@ -102,7 +102,7 @@
         case ver of
             TLS13
                 | hrr ->
-                    helloRetry cparams ctx mparams ver crand (grpsSupported \\ grpsSelected)
+                    helloRetry cparams ctx mparams ver crand grpsSupported grpsSelected
                 | otherwise -> do
                     recvServerSecondFlight13 cparams ctx grpsSelected
                     sendClientSecondFlight13 cparams ctx
@@ -126,8 +126,9 @@
     -> Version
     -> ClientRandom
     -> [Group]
+    -> [Group]
     -> IO ()
-helloRetry cparams ctx mparams ver crand groupsSupported = do
+helloRetry cparams ctx mparams ver crand groupsSupported groupsSelected = do
     when (null groupsSupported) $
         throwCore $
             Error_Protocol "no supported groups on the client side" IllegalParameter
@@ -137,18 +138,22 @@
     mks <- usingState_ ctx getTLS13KeyShare
     case mks of
         Just (KeyShareHRR selectedGroup)
-            | selectedGroup `elem` groupsSupported -> do
+            -- RFC 8446 Sec 4.1.4: the selected_group MUST be in supported_groups
+            -- and MUST NOT already have been offered in the initial key_share.
+            | selectedGroup `elem` groupsSupported
+                && selectedGroup `notElem` groupsSelected -> do
                 usingHState ctx $ setTLS13HandshakeMode HelloRetryRequest
                 clearTxRecordState ctx
                 let cparams' = cparams{clientUseEarlyData = False}
                 runPacketFlight ctx $ sendChangeCipherSpec13 ctx
                 clientSession <- tls13stSession <$> getTLS13State ctx
-                let groupsSupported' = selectedGroup : filter (/= selectedGroup) groupsSupported
-                    groupsSelected' = [selectedGroup]
-                    grps =
+                -- RFC 8446 Sec 4.1.2: the second ClientHello MUST be identical
+                -- to the first except for the specific listed changes.
+                -- supported_groups is NOT on that list, so it must be unchanged.
+                let grps =
                         Groups
-                            { grpsSupported = groupsSupported'
-                            , grpsSelected = groupsSelected'
+                            { grpsSupported = groupsSupported
+                            , grpsSelected = [selectedGroup]
                             }
 
                 handshake
@@ -156,6 +161,11 @@
                     ctx
                     grps
                     (Just (crand, clientSession, ver))
+            | selectedGroup `elem` groupsSelected ->
+                throwCore $
+                    Error_Protocol
+                        "server selected a group already offered in key_share"
+                        IllegalParameter
             | otherwise ->
                 throwCore $
                     Error_Protocol "server-selected group is not supported" IllegalParameter
diff --git a/Network/TLS/Handshake/Client/ClientHello.hs b/Network/TLS/Handshake/Client/ClientHello.hs
--- a/Network/TLS/Handshake/Client/ClientHello.hs
+++ b/Network/TLS/Handshake/Client/ClientHello.hs
@@ -262,11 +262,12 @@
         Nothing -> return Nothing
         Just siz -> return $ Just $ toExtensionRaw $ RecordSizeLimit $ fromIntegral siz
 
-    sessionTicketExt = do
+    sessionTicketExt =
         case clientSessions cparams of
             (sidOrTkt, _) : _
                 | isTicket sidOrTkt -> return $ Just $ toExtensionRaw $ SessionTicket sidOrTkt
-            _ -> return $ Just $ toExtensionRaw $ SessionTicket ""
+            _   | clientWantTicket cparams -> return $ Just $ toExtensionRaw $ SessionTicket ""
+                | otherwise -> return $ Nothing
 
     earlyDataExt
         | rtt0 = return $ Just $ toExtensionRaw (EarlyDataIndication Nothing)
diff --git a/Network/TLS/Parameters.hs b/Network/TLS/Parameters.hs
--- a/Network/TLS/Parameters.hs
+++ b/Network/TLS/Parameters.hs
@@ -153,6 +153,16 @@
     -- specified for TLS 1.2 but only the first entry is used.
     --
     -- Default: '[]'
+    , clientWantTicket :: Bool
+    -- ^ Whether to solicit TLS 1.2 session tickets (or TLS 1.3
+    -- resumption PSKs) from servers.  With a 'False' setting,
+    -- stateless clients that never do resumption can avoid
+    -- wasting server and client resources used to generate,
+    -- transmit and process tickets that will never be used.
+    --
+    -- Default: 'True'
+    --
+    -- @since 2.4.1
     , clientShared :: Shared
     -- ^ See the default value of 'Shared'.
     , clientHooks :: ClientHooks
@@ -192,6 +202,7 @@
         , clientUseServerNameIndication = True
         , clientWantSessionResume = Nothing
         , clientWantSessionResumeList = []
+        , clientWantTicket = True
         , clientShared = def
         , clientHooks = def
         , clientSupported = def
@@ -401,8 +412,9 @@
     --
     --   Default: @[X25519,P256,P384,X448,P521,FFDHE3072,FFDHE4096,FFDHE6144,FFDHE8192,X25519MLKEM768,P256MLKEM768,P384MLKEM1024,MLKEM768,MLKEM1024]@
     , supportedGroupsTLS13 :: [[Group]]
-    -- ^ @[Group]@ contains @Group@s of the same level in preferred order.
-    -- @[Group]@ is also listed in preferred order.
+    -- ^ The inside @[Group]@ is the list of @Group@ at the same level
+    -- in preferred order.  The inside @[Group]@s are also listed in
+    -- preferred order.
     --
     -- TLS 1.3 server: this is used as the 1st argument to
     -- 'onSelectKeyShare'.
diff --git a/tls.cabal b/tls.cabal
--- a/tls.cabal
+++ b/tls.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               tls
-version:            2.4.0
+version:            2.4.1
 license:            BSD3
 license-file:       LICENSE
 copyright:          Vincent Hanquez <vincent@snarc.org>
