diff --git a/doc/programmers-guide.rst b/doc/programmers-guide.rst
index 3d504064a4f840294c490f1a6460e4e52cee468f..8a4bed47b485ce51b674d656d7745a9eb4533a91 100644
--- a/doc/programmers-guide.rst
+++ b/doc/programmers-guide.rst
@@ -271,6 +271,7 @@ timestamp is equal to or larger than the value returned from
 (or `ngtcp2_conn_write_handshake()` if handshake has not completed
 yet).  If the current timestamp is equal to or larger than the value
 returned from `ngtcp2_conn_ack_delay_expiry()`, it has to call
+`ngtcp2_conn_cancel_expired_ack_delay_timer()` and
 `ngtcp2_conn_write_pkt()` (or `ngtcp2_conn_write_handshake()` if
 handshake has not completed yet).  After calling these functions, new
 expiry will be set.  The application should call
diff --git a/examples/client.cc b/examples/client.cc
index 6041c1a2ff5154f7d8d8c7cb7d9ca82ccf563465..7cf29c3c6db1008c108048810ee7e5a9e17c29aa 100644
--- a/examples/client.cc
+++ b/examples/client.cc
@@ -361,6 +361,7 @@ void retransmitcb(struct ev_loop *loop, ev_timer *w, int revents) {
   }
 
   if (ngtcp2_conn_ack_delay_expiry(conn) <= now) {
+    ngtcp2_conn_cancel_expired_ack_delay_timer(conn, now);
     rv = c->on_write();
     if (rv != 0) {
       goto fail;
diff --git a/examples/server.cc b/examples/server.cc
index 4556adb101549bb34f6e7b69d3c157939ffa88f2..660999279a28f27bdf1850b07d03afe6126deaa8 100644
--- a/examples/server.cc
+++ b/examples/server.cc
@@ -835,6 +835,7 @@ void retransmitcb(struct ev_loop *loop, ev_timer *w, int revents) {
     if (!config.quiet) {
       std::cerr << "Delayed ACK timer expired" << std::endl;
     }
+    ngtcp2_conn_cancel_expired_ack_delay_timer(conn, now);
     rv = h->on_write();
     switch (rv) {
     case 0:
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 9428fd5f1a7e4bbf2e01184467b952dce22d6589..8b6bb4c9995ea2151a9d71cbc1a0f8b281746077 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -2030,6 +2030,7 @@ ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
  *
  * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of
  * delayed protected ACK.  Application should call
+ * ngtcp2_conn_cancel_expired_ack_delay_timer() and
  * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_write_handshake` if
  * handshake has not finished yet) when it expires.  It returns
  * UINT64_MAX if there is no expiry.
@@ -2046,6 +2047,16 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn);
  */
 NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK
+ * delay timer.  |ts| is the current time.  This function must be
+ * called when ngtcp2_conn_ack_delay_expiry() <= ts.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+                                                              ngtcp2_tstamp ts);
+
 /**
  * @function
  *
diff --git a/lib/ngtcp2_acktr.c b/lib/ngtcp2_acktr.c
index e7403db618c9387abe0483139b9cf5065dc4c3b1..494e4cd616fd5309aa8cfced3e0126d0c3281d83 100644
--- a/lib/ngtcp2_acktr.c
+++ b/lib/ngtcp2_acktr.c
@@ -316,15 +316,15 @@ void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
 
 void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
   acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK |
-                               NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK);
+                               NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK |
+                               NGTCP2_ACKTR_FLAG_CANCEL_TIMER);
   acktr->first_unacked_ts = UINT64_MAX;
   acktr->rx_npkt = 0;
 }
 
 int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, uint64_t max_ack_delay,
                                     ngtcp2_tstamp ts) {
-  return (acktr->flags & NGTCP2_ACKTR_FLAG_ACTIVE_ACK) &&
-         acktr->first_unacked_ts <= ts - max_ack_delay;
+  return acktr->first_unacked_ts <= ts - max_ack_delay;
 }
 
 void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
diff --git a/lib/ngtcp2_acktr.h b/lib/ngtcp2_acktr.h
index 35e00d76bd4904e1f6f46c71adef8154200146b1..1986420391f2a5a2ba578cac1f5bfd9c38e54b91 100644
--- a/lib/ngtcp2_acktr.h
+++ b/lib/ngtcp2_acktr.h
@@ -108,6 +108,9 @@ typedef enum {
      acknowledgement for ACK which acknowledges the last handshake
      packet from client (which contains TLSv1.3 Finished message). */
   NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK = 0x80,
+  /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
+     expired and canceled. */
+  NGTCP2_ACKTR_FLAG_CANCEL_TIMER = 0x0100,
 } ngtcp2_acktr_flag;
 
 /*
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index cafd051d77f3784d3aff71a04f1513600f020e77..ba8864620df93f3e020890edd00b44ae8783a614 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -1754,8 +1754,7 @@ static ssize_t conn_write_handshake_ack_pkt(ngtcp2_conn *conn, uint8_t *dest,
  * Initial and Handshake packet.
  */
 static ssize_t conn_write_handshake_ack_pkts(ngtcp2_conn *conn, uint8_t *dest,
-                                             size_t destlen,
-                                             ngtcp2_tstamp ts) {
+                                             size_t destlen, ngtcp2_tstamp ts) {
   ssize_t res = 0, nwrite = 0;
   int require_padding;
 
@@ -7511,15 +7510,18 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) {
   ngtcp2_acktr *acktr = &conn->pktns.acktr;
   ngtcp2_tstamp ts = UINT64_MAX, t;
 
-  if (in_acktr->first_unacked_ts != UINT64_MAX) {
+  if (!(in_acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+      in_acktr->first_unacked_ts != UINT64_MAX) {
     t = in_acktr->first_unacked_ts + NGTCP2_HS_ACK_DELAY;
     ts = ngtcp2_min(ts, t);
   }
-  if (hs_acktr->first_unacked_ts != UINT64_MAX) {
+  if (!(hs_acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+      hs_acktr->first_unacked_ts != UINT64_MAX) {
     t = hs_acktr->first_unacked_ts + NGTCP2_HS_ACK_DELAY;
     ts = ngtcp2_min(ts, t);
   }
-  if (acktr->first_unacked_ts != UINT64_MAX) {
+  if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+      acktr->first_unacked_ts != UINT64_MAX) {
     t = acktr->first_unacked_ts + conn_compute_ack_delay(conn);
     ts = ngtcp2_min(ts, t);
   }
@@ -7532,6 +7534,21 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) {
   return ngtcp2_min(t1, t2);
 }
 
+static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr,
+                                                 ngtcp2_tstamp ts) {
+  if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+      acktr->first_unacked_ts <= ts) {
+    acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER;
+  }
+}
+
+void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+                                                ngtcp2_tstamp ts) {
+  acktr_cancel_expired_ack_delay_timer(&conn->in_pktns.acktr, ts);
+  acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns.acktr, ts);
+  acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts);
+}
+
 /*
  * settings_copy_from_transport_params translates
  * ngtcp2_transport_params to ngtcp2_settings.