diff --git a/crypto/shared.c b/crypto/shared.c
index 08aa5e2b520850b09ddbff00beda27fa95006230..7e5219c52f2957a5463846370e0b1a1ceaf8bd8d 100644
--- a/crypto/shared.c
+++ b/crypto/shared.c
@@ -245,6 +245,35 @@ fail:
   return -1;
 }
 
+/*
+ * crypto_set_local_transport_params gets local QUIC transport
+ * parameters from |conn| and sets it to |tls|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
+  ngtcp2_transport_params_type exttype =
+      ngtcp2_conn_is_server(conn)
+          ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
+          : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO;
+  ngtcp2_transport_params params;
+  ngtcp2_ssize nwrite;
+  uint8_t buf[256];
+
+  ngtcp2_conn_get_local_transport_params(conn, &params);
+
+  nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, &params);
+  if (nwrite < 0) {
+    return -1;
+  }
+
+  if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) {
+    return -1;
+  }
+
+  return 0;
+}
+
 int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
                                             uint8_t *iv, uint8_t *hp_key,
                                             ngtcp2_crypto_level level,
@@ -311,18 +340,23 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
     }
     break;
   case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+    rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen,
+                                              &hp_ctx);
+    if (rv != 0) {
+      goto fail;
+    }
+
     if (ngtcp2_conn_is_server(conn)) {
       rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
       if (rv != 0) {
         goto fail;
       }
-    }
 
-    rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen,
-                                              &hp_ctx);
-    if (rv != 0) {
-      goto fail;
+      if (crypto_set_local_transport_params(conn, tls) != 0) {
+        goto fail;
+      }
     }
+
     break;
   case NGTCP2_CRYPTO_LEVEL_APP:
     rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv,
@@ -678,60 +712,29 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
   return spktlen;
 }
 
-/*
- * crypto_set_local_transport_params gets local QUIC transport
- * parameters from |conn| and sets it to |tls|.
- *
- * This function returns 0 if it succeeds, or -1.
- */
-static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
-  ngtcp2_transport_params_type exttype =
-      ngtcp2_conn_is_server(conn)
-          ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
-          : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO;
-  ngtcp2_transport_params params;
-  ngtcp2_ssize nwrite;
-  uint8_t buf[256];
-
-  ngtcp2_conn_get_local_transport_params(conn, &params);
-
-  nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, &params);
-  if (nwrite < 0) {
-    return -1;
-  }
-
-  if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) {
-    return -1;
-  }
-
-  return 0;
-}
-
 /*
  * crypto_setup_initial_crypto establishes the initial secrets and
  * encryption keys, and prepares local QUIC transport parameters.
  */
 static int crypto_setup_initial_crypto(ngtcp2_conn *conn,
                                        const ngtcp2_cid *dcid) {
-  void *tls = ngtcp2_conn_get_tls_native_handle(conn);
-
-  if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL,
-                                                   NULL, NULL, NULL, NULL, NULL,
-                                                   dcid) != 0) {
-    return -1;
-  }
-
-  return crypto_set_local_transport_params(conn, tls);
+  return ngtcp2_crypto_derive_and_install_initial_key(
+      conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid);
 }
 
 int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
   const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
+  void *tls = ngtcp2_conn_get_tls_native_handle(conn);
   (void)user_data;
 
   if (crypto_setup_initial_crypto(conn, dcid) != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
+  if (crypto_set_local_transport_params(conn, tls) != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
   if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL,
                                            NULL, 0) != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 2e420efe4caef7291f478bb6b77580d1beb06d97..6d161677ae58c7157adb74f9156a82b4ac04aa67 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -2427,6 +2427,27 @@ ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn,
 NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params(
     ngtcp2_conn *conn, const ngtcp2_transport_params *params);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_local_transport_params` sets the local transport
+ * parameters |params|.  This function can only be called by server.
+ * Although the local transport parameters are passed to
+ * `ngtcp2_conn_server_new`, server might want to update them after
+ * ALPN is chosen.  In that case, server can update the transport
+ * parameter with this function.  Server must call this function
+ * before calling `ngtcp2_conn_install_tx_handshake_key`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :enum:`NGTCP2_ERR_INVALID_STATE`
+ *     `ngtcp2_conn_install_tx_handshake_key` has been called.
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn,
+                                       const ngtcp2_transport_params *params);
+
 /**
  * @function
  *
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index a9a031e9c9a194a9bae8b67fc937e05d5a9e9ca3..90b64087ed01cc57e3ef8ae2db6877404019bc51 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -702,17 +702,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
     (*pconn)->local.settings.token.len = 0;
   }
 
-  if (params->active_connection_id_limit == 0) {
-    (*pconn)->local.settings.transport_params.active_connection_id_limit =
-        NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
-  }
-
-  (*pconn)->local.settings.transport_params.initial_scid = *scid;
-
-  if (scid->datalen == 0) {
-    (*pconn)->local.settings.transport_params.preferred_address_present = 0;
-  }
-
   if (settings->max_udp_payload_size == 0) {
     (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN;
   }
@@ -772,10 +761,9 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
     goto fail_scident;
   }
 
-  ngtcp2_scid_init(scident, 0, scid,
-                   params->stateless_reset_token_present
-                       ? params->stateless_reset_token
-                       : NULL);
+  /* Set stateless reset token later if it is available in the local
+     transport parameters */
+  ngtcp2_scid_init(scident, 0, scid, NULL);
 
   rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident);
   if (rv != 0) {
@@ -784,26 +772,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
 
   scident = NULL;
 
-  if (server && params->preferred_address_present) {
-    scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
-    if (scident == NULL) {
-      rv = NGTCP2_ERR_NOMEM;
-      goto fail_scident;
-    }
-
-    ngtcp2_scid_init(scident, 1, &params->preferred_address.cid,
-                     params->preferred_address.stateless_reset_token);
-
-    rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident);
-    if (rv != 0) {
-      goto fail_scid_set_insert;
-    }
-
-    scident = NULL;
-
-    (*pconn)->scid.last_seq = 1;
-  }
-
   ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL);
   ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path);
 
@@ -818,22 +786,12 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   (*pconn)->version = version;
   (*pconn)->mem = mem;
   (*pconn)->user_data = user_data;
-  (*pconn)->rx.unsent_max_offset = (*pconn)->rx.max_offset =
-      params->initial_max_data;
-  (*pconn)->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi;
-  (*pconn)->remote.bidi.max_streams = params->initial_max_streams_bidi;
-  (*pconn)->remote.uni.unsent_max_streams = params->initial_max_streams_uni;
-  (*pconn)->remote.uni.max_streams = params->initial_max_streams_uni;
   (*pconn)->idle_ts = settings->initial_ts;
   (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX;
 
   ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid,
                     server);
 
-  ngtcp2_qlog_parameters_set_transport_params(
-      &(*pconn)->qlog, &(*pconn)->local.settings.transport_params, server,
-      NGTCP2_QLOG_SIDE_LOCAL);
-
   return 0;
 
 fail_seqgap_push:
@@ -891,6 +849,16 @@ int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   (*pconn)->local.bidi.next_stream_id = 0;
   (*pconn)->local.uni.next_stream_id = 2;
 
+  rv = ngtcp2_conn_commit_local_transport_params(*pconn);
+  if (rv != 0) {
+    ngtcp2_conn_del(*pconn);
+    return rv;
+  }
+
+  ngtcp2_qlog_parameters_set_transport_params(
+      &(*pconn)->qlog, &(*pconn)->local.settings.transport_params,
+      (*pconn)->server, NGTCP2_QLOG_SIDE_LOCAL);
+
   return 0;
 }
 
@@ -8192,6 +8160,10 @@ int ngtcp2_conn_install_tx_handshake_key(
 
   pktns->crypto.tx.hp_ctx = *hp_ctx;
 
+  if (conn->server) {
+    return ngtcp2_conn_commit_local_transport_params(conn);
+  }
+
   return 0;
 }
 
@@ -8489,6 +8461,80 @@ void ngtcp2_conn_set_early_remote_transport_params(
                                               NGTCP2_QLOG_SIDE_REMOTE);
 }
 
+int ngtcp2_conn_set_local_transport_params(
+    ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
+  assert(conn->server);
+  assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
+
+  if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) {
+    return NGTCP2_ERR_INVALID_STATE;
+  }
+
+  conn->local.settings.transport_params = *params;
+
+  return 0;
+}
+
+int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) {
+  const ngtcp2_mem *mem = conn->mem;
+  ngtcp2_transport_params *params = &conn->local.settings.transport_params;
+  ngtcp2_scid *scident;
+  ngtcp2_ksl_it it;
+  int rv;
+
+  assert(1 == ngtcp2_ksl_len(&conn->scid.set));
+
+  if (params->active_connection_id_limit == 0) {
+    params->active_connection_id_limit =
+        NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+  }
+
+  params->initial_scid = conn->oscid;
+
+  if (conn->oscid.datalen == 0) {
+    params->preferred_address_present = 0;
+  }
+
+  if (conn->server) {
+    if (params->stateless_reset_token_present) {
+      it = ngtcp2_ksl_begin(&conn->scid.set);
+      scident = ngtcp2_ksl_it_get(&it);
+
+      memcpy(scident->token, params->stateless_reset_token,
+             NGTCP2_STATELESS_RESET_TOKENLEN);
+    }
+
+    if (params->preferred_address_present) {
+      scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
+      if (scident == NULL) {
+        return NGTCP2_ERR_NOMEM;
+      }
+
+      ngtcp2_scid_init(scident, 1, &params->preferred_address.cid,
+                       params->preferred_address.stateless_reset_token);
+
+      rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident);
+      if (rv != 0) {
+        ngtcp2_mem_free(mem, scident);
+        return rv;
+      }
+
+      conn->scid.last_seq = 1;
+    }
+  }
+
+  conn->rx.unsent_max_offset = conn->rx.max_offset = params->initial_max_data;
+  conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi;
+  conn->remote.bidi.max_streams = params->initial_max_streams_bidi;
+  conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni;
+  conn->remote.uni.max_streams = params->initial_max_streams_uni;
+
+  ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server,
+                                              NGTCP2_QLOG_SIDE_LOCAL);
+
+  return 0;
+}
+
 void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn,
                                             ngtcp2_transport_params *params) {
   *params = conn->local.settings.transport_params;
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index dbcb5d8847a871cacab5710049272cb3a66cd73b..1c0dc3ac448ecec9edac56dafa26bdb1c88b8456 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -622,4 +622,20 @@ ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest,
                                    const ngtcp2_cid *dcid, ngtcp2_frame *fr,
                                    uint8_t rtb_flags, ngtcp2_tstamp ts);
 
+/*
+ * ngtcp2_conn_commit_local_transport_params commits the local
+ * transport parameters, which is currently set to
+ * conn->local.settings.transport_params.  This function will do some
+ * amends on transport parameters for adjusting default values.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ *     Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ *     CID in preferred address equals to the original SCID.
+ */
+int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn);
+
 #endif /* NGTCP2_CONN_H */