diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index db9031cdde2a6a0de0b2968c198e1f6082508d6b..741995016f867a924c441721d60d93305930ae10 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -1280,11 +1280,10 @@ static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb,
 
   if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) {
     conn->cstat.last_tx_pkt_ts[rtb->pktns_id] = ent->ts;
-    /* I think pseudo code is wrong; timer should be set when
-       ack-eliciting packet is sent. */
-    ngtcp2_conn_set_loss_detection_timer(conn, ent->ts);
   }
 
+  ngtcp2_conn_set_loss_detection_timer(conn, ent->ts);
+
   return 0;
 }
 
@@ -1934,11 +1933,12 @@ build_pkt:
     pfrc = &(*pfrc)->next;
 
     pkt_empty = 0;
-    rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+    rtb_entry_flags |=
+        NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
   }
 
   if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) &&
-      pktns->rtb.probe_pkt_left) {
+      pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) {
     num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
                                               pktns->rtb.probe_pkt_left + 1);
     if (num_reclaimed < 0) {
@@ -1948,6 +1948,19 @@ build_pkt:
     if (num_reclaimed) {
       goto build_pkt;
     }
+    /* We had pktns->rtb.num_retransmittable > 0 but the contents of
+       those packets have been acknowledged (i.e., retransmission in
+       another packet).  For server, in this case, we don't have to
+       send any probe packet.  Client needs to send probe packets
+       until it knows that server has completed address validation or
+       handshake has been confirmed. */
+    if (pktns->rtb.num_retransmittable == 0 &&
+        (conn->server ||
+         (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
+                         NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+      pktns->rtb.probe_pkt_left = 0;
+      ngtcp2_conn_set_loss_detection_timer(conn, ts);
+    }
   }
 
   /* Don't send any PING frame if client Initial has not been
@@ -2807,7 +2820,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
       }
 
       pkt_empty = 0;
-      rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+      rtb_entry_flags |=
+          NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
       pfrc = &(*pfrc)->next;
     }
 
@@ -2846,7 +2860,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
         pfrc = &(*pfrc)->next;
 
         pkt_empty = 0;
-        rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+        rtb_entry_flags |=
+            NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
       }
     }
 
@@ -2877,7 +2892,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
         assert(NGTCP2_ERR_NOBUF == rv);
       } else {
         pkt_empty = 0;
-        rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+        rtb_entry_flags |=
+            NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
         pfrc = &(*pfrc)->next;
       }
     }
@@ -2908,7 +2924,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
           assert(NGTCP2_ERR_NOBUF == rv);
         } else {
           pkt_empty = 0;
-          rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+          rtb_entry_flags |=
+              NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
           pfrc = &(*pfrc)->next;
         }
       }
@@ -2939,7 +2956,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
 
           pkt_empty = 0;
           credit_expanded = 1;
-          rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+          rtb_entry_flags |=
+              NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
           pfrc = &(*pfrc)->next;
           strm->rx.max_offset = strm->rx.unsent_max_offset;
         }
@@ -2985,7 +3003,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
         pfrc = &(*pfrc)->next;
 
         pkt_empty = 0;
-        rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+        rtb_entry_flags |=
+            NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
 
         if (ngtcp2_strm_streamfrq_empty(strm)) {
           ngtcp2_conn_tx_strmq_pop(conn);
@@ -3004,15 +3023,25 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
 
     if (rv != NGTCP2_ERR_NOBUF && !send_stream &&
         !(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) &&
-        pktns->tx.frq == NULL && pktns->rtb.probe_pkt_left) {
+        pktns->rtb.num_retransmittable && pktns->tx.frq == NULL &&
+        pktns->rtb.probe_pkt_left) {
       num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
-                                                pktns->rtb.probe_pkt_left);
+                                                pktns->rtb.probe_pkt_left + 1);
       if (num_reclaimed < 0) {
         return rv;
       }
       if (num_reclaimed) {
         goto build_pkt;
       }
+
+      /* We had pktns->rtb.num_retransmittable > 0 but the contents of
+         those packets have been acknowledged (i.e., retransmission in
+         another packet).  In this case, we don't have to send any
+         probe packet. */
+      if (pktns->rtb.num_retransmittable == 0) {
+        pktns->rtb.probe_pkt_left = 0;
+        ngtcp2_conn_set_loss_detection_timer(conn, ts);
+      }
     }
 
     /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to
@@ -3079,7 +3108,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
     pfrc = &(*pfrc)->next;
 
     pkt_empty = 0;
-    rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
+    rtb_entry_flags |=
+        NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_RETRANSMITTABLE;
 
     data_strm->tx.offset += ndatalen;
     conn->tx.offset += ndatalen;
@@ -3121,8 +3151,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
   }
 
   if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) {
-    if (pktns->rtb.probe_pkt_left ||
-        pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) {
+    if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) {
       lfr.type = NGTCP2_FRAME_PING;
 
       rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
@@ -3132,9 +3161,6 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
            packet is still empty. */
       } else {
         rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING;
-        if (pktns->rtb.probe_pkt_left) {
-          rtb_entry_flags |= NGTCP2_RTB_FLAG_PROBE;
-        }
         pktns->tx.num_non_ack_pkt = 0;
       }
     } else {
@@ -3173,6 +3199,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
 
   ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite);
 
+  /* TODO ack-eliciting vs needs-tracking */
+  /* probe packet needs tracking but it does not need ACK, could be lost. */
   if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) {
     rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite,
                               rtb_entry_flags, conn->mem);
@@ -3390,9 +3418,9 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
   ngtcp2_pktns *hs_pktns = conn->hs_pktns;
 
   return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
-         (in_pktns && (in_pktns->rtb.num_ack_eliciting ||
+         (in_pktns && (in_pktns->rtb.num_retransmittable ||
                        ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) ||
-         (hs_pktns && (hs_pktns->rtb.num_ack_eliciting ||
+         (hs_pktns && (hs_pktns->rtb.num_retransmittable ||
                        ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq)));
 }
 
@@ -9367,7 +9395,7 @@ static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn,
   ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL];
 
   for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) {
-    if (ns[i] == NULL || ns[i]->rtb.num_ack_eliciting == 0 ||
+    if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 ||
         (times[i] == UINT64_MAX ||
          (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) ||
          (i == NGTCP2_PKTNS_ID_APP &&
@@ -9416,9 +9444,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return;
   }
 
-  if ((!in_pktns || in_pktns->rtb.num_ack_eliciting == 0) &&
-      (!hs_pktns || hs_pktns->rtb.num_ack_eliciting == 0) &&
-      (pktns->rtb.num_ack_eliciting == 0 ||
+  if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) &&
+      (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) &&
+      (pktns->rtb.num_retransmittable == 0 ||
        !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) &&
       (conn->server ||
        (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index 498604969a472262a57ef36fd1c7f8d986691745..53f9c4d80ce86b7ac795081748ec1d4f210247df 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -106,7 +106,7 @@ typedef enum {
 
 /* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non
    ACK-eliciting packets. */
-#define NGTCP2_MAX_NON_ACK_TX_PKT 10
+#define NGTCP2_MAX_NON_ACK_TX_PKT 3
 
 /*
  * ngtcp2_max_frame is defined so that it covers the largest ACK
diff --git a/lib/ngtcp2_rtb.c b/lib/ngtcp2_rtb.c
index d22570974514a4b8f962debf6694a4c1f9b211db..83308e8e88c4dbf9b19972e271f00cb00ab73b55 100644
--- a/lib/ngtcp2_rtb.c
+++ b/lib/ngtcp2_rtb.c
@@ -236,6 +236,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
   rtb->mem = mem;
   rtb->largest_acked_tx_pkt_num = -1;
   rtb->num_ack_eliciting = 0;
+  rtb->num_retransmittable = 0;
   rtb->probe_pkt_left = 0;
   rtb->pktns_id = pktns_id;
   rtb->cc_pkt_num = 0;
@@ -274,6 +275,9 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
   if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) {
     ++rtb->num_ack_eliciting;
   }
+  if (ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE) {
+    ++rtb->num_retransmittable;
+  }
 }
 
 static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
@@ -282,12 +286,17 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
     return;
   }
 
-  if ((ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) &&
-      !(ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED)) {
+  if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) {
     assert(rtb->num_ack_eliciting);
     --rtb->num_ack_eliciting;
   }
 
+  if ((ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE) &&
+      !(ent->flags & NGTCP2_RTB_FLAG_PTO_RECLAIMED)) {
+    assert(rtb->num_retransmittable);
+    --rtb->num_retransmittable;
+  }
+
   if (rtb->cc_pkt_num <= ent->hd.pkt_num) {
     assert(cstat->bytes_in_flight >= ent->pktlen);
     cstat->bytes_in_flight -= ent->pktlen;
@@ -1118,10 +1127,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
 
     if ((ent->flags & (NGTCP2_RTB_FLAG_LOST_RETRANSMITTED |
                        NGTCP2_RTB_FLAG_PTO_RECLAIMED)) ||
-        ent->frc == NULL) {
+        !(ent->flags & NGTCP2_RTB_FLAG_RETRANSMITTABLE)) {
       continue;
     }
 
+    assert(ent->frc);
+
     rv = rtb_reclaim_frame(rtb, &reclaimed, conn, pktns, ent);
     if (rv != 0) {
       return rv;
@@ -1131,8 +1142,8 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
        the next run. */
     ent->flags |= NGTCP2_RTB_FLAG_PTO_RECLAIMED;
 
-    assert(rtb->num_ack_eliciting);
-    --rtb->num_ack_eliciting;
+    assert(rtb->num_retransmittable);
+    --rtb->num_retransmittable;
 
     if (reclaimed) {
       --num_pkts;
diff --git a/lib/ngtcp2_rtb.h b/lib/ngtcp2_rtb.h
index bfd45fe6a1e6473aa2a5e7015fdc5c6e6aade24e..0d0b738f396a2393511bc21b56813058002829df 100644
--- a/lib/ngtcp2_rtb.h
+++ b/lib/ngtcp2_rtb.h
@@ -176,6 +176,13 @@ typedef enum {
   /* NGTCP2_RTB_FLAG_PROBE indicates that the entry includes a probe
      packet. */
   NGTCP2_RTB_FLAG_PROBE = 0x01,
+  /* NGTCP2_RTB_FLAG_RETRANSMITTABLE indicates that the entry includes
+     a frame which must be retransmitted until it is acknowledged.  In
+     most cases, this flag is used along with
+     NGTCP2_RTB_FLAG_ACK_ELICITING.  We have these 2 flags because
+     NGTCP2_RTB_FLAG_RETRANSMITTABLE triggers PTO, but just
+     NGTCP2_RTB_FLAG_ACK_ELICITING does not. */
+  NGTCP2_RTB_FLAG_RETRANSMITTABLE = 0x02,
   /* NGTCP2_RTB_FLAG_ACK_ELICITING indicates that the entry elicits
      acknowledgement. */
   NGTCP2_RTB_FLAG_ACK_ELICITING = 0x04,
@@ -262,6 +269,9 @@ typedef struct {
   int64_t largest_acked_tx_pkt_num;
   /* num_ack_eliciting is the number of ACK eliciting entries. */
   size_t num_ack_eliciting;
+  /* num_retransmittable is the number of packets which contain frames
+     that must be retransmitted on loss. */
+  size_t num_retransmittable;
   /* probe_pkt_left is the number of probe packet to send */
   size_t probe_pkt_left;
   /* pktns_id is the identifier of packet number space. */
diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c
index 2d6ee372f1e202ac273a28ce101023c515ef5ae0..bf347f9d24f91e784caa01bfe943b305bc93ab1a 100644
--- a/tests/ngtcp2_conn_test.c
+++ b/tests/ngtcp2_conn_test.c
@@ -4796,7 +4796,8 @@ void test_ngtcp2_conn_handshake_probe(void) {
   spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
-  CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+  CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable);
+  CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
   CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left);
 
   fr.type = NGTCP2_FRAME_ACK;
@@ -4816,6 +4817,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t);
 
   CU_ASSERT(0 == rv);
+  CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable);
   CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
   CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
 
@@ -4824,7 +4826,8 @@ void test_ngtcp2_conn_handshake_probe(void) {
   spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
-  CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+  CU_ASSERT(0 == conn->in_pktns->rtb.num_retransmittable);
+  CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
   CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left);
 
   it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
@@ -4842,7 +4845,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t);
 
   CU_ASSERT(0 == rv);
-  CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
+  CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting);
   CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
 
   /* This sends anti-deadlock Handshake packet even if we have nothing
@@ -4850,6 +4853,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
+  CU_ASSERT(0 == conn->hs_pktns->rtb.num_retransmittable);
   CU_ASSERT(1 == conn->hs_pktns->rtb.num_ack_eliciting);
   CU_ASSERT(0 == conn->hs_pktns->rtb.probe_pkt_left);
 
@@ -5674,7 +5678,7 @@ void test_ngtcp2_conn_rtb_reclaim_on_pto(void) {
     }
   }
 
-  CU_ASSERT(2 == num_reclaim_pkt);
+  CU_ASSERT(3 == num_reclaim_pkt);
 
   ngtcp2_conn_del(conn);
 }