diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
index 27161860d03..9dda61deeab 100644
--- a/bin/named/bind9.xsl
+++ b/bin/named/bind9.xsl
@@ -15,7 +15,7 @@
   <xsl:output method="html" indent="yes" version="4.0"/>
   <!-- the version number **below** must match version in bin/named/statschannel.c -->
   <!-- don't forget to update "/xml/v<STATS_XML_VERSION_MAJOR>" in the HTTP endpoints listed below -->
-  <xsl:template match="statistics[@version=&quot;3.12&quot;]">
+  <xsl:template match="statistics[@version=&quot;3.13&quot;]">
     <html>
       <head>
         <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
index cd560292456..f6ce4254459 100644
--- a/bin/named/statschannel.c
+++ b/bin/named/statschannel.c
@@ -55,11 +55,11 @@
 #include "xsl_p.h"
 
 #define STATS_XML_VERSION_MAJOR "3"
-#define STATS_XML_VERSION_MINOR "12"
+#define STATS_XML_VERSION_MINOR "13"
 #define STATS_XML_VERSION	STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR
 
 #define STATS_JSON_VERSION_MAJOR "1"
-#define STATS_JSON_VERSION_MINOR "6"
+#define STATS_JSON_VERSION_MINOR "7"
 #define STATS_JSON_VERSION	 STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR
 
 #define CHECK(m)                               \
@@ -351,6 +351,7 @@ init_desc(void) {
 	SET_NSSTATDESC(reclimitdropped,
 		       "queries dropped due to recursive client limit",
 		       "RecLimitDropped");
+	SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota");
 
 	INSIST(i == ns_statscounter_max);
 
diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst
index ade41a956a8..80022998cea 100644
--- a/doc/arm/reference.rst
+++ b/doc/arm/reference.rst
@@ -7905,6 +7905,11 @@ Name Server Statistics Counters
 ``UpdateBadPrereq``
     This indicates the number of dynamic updates rejected due to a prerequisite failure.
 
+``UpdateQuota``
+    This indicates the number of times a dynamic update or update
+    forwarding request was rejected because the number of pending
+    requests exceeded :any:`update-quota`.
+
 ``RateDropped``
     This indicates the number of responses dropped due to rate limits.
 
diff --git a/lib/ns/include/ns/server.h b/lib/ns/include/ns/server.h
index 0f11612d1e2..4393e9798cc 100644
--- a/lib/ns/include/ns/server.h
+++ b/lib/ns/include/ns/server.h
@@ -84,6 +84,7 @@ struct ns_server {
 	isc_quota_t recursionquota;
 	isc_quota_t tcpquota;
 	isc_quota_t xfroutquota;
+	isc_quota_t updquota;
 	ISC_LIST(isc_quota_t) http_quotas;
 	isc_mutex_t http_quotas_lock;
 
diff --git a/lib/ns/include/ns/stats.h b/lib/ns/include/ns/stats.h
index 2de556423be..c235384f48f 100644
--- a/lib/ns/include/ns/stats.h
+++ b/lib/ns/include/ns/stats.h
@@ -107,7 +107,9 @@ enum {
 
 	ns_statscounter_reclimitdropped = 66,
 
-	ns_statscounter_max = 67,
+	ns_statscounter_updatequota = 67,
+
+	ns_statscounter_max = 68,
 };
 
 void
diff --git a/lib/ns/server.c b/lib/ns/server.c
index 7baf2efc37d..ce46aad2be1 100644
--- a/lib/ns/server.c
+++ b/lib/ns/server.c
@@ -54,6 +54,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
 	isc_quota_init(&sctx->xfroutquota, 10);
 	isc_quota_init(&sctx->tcpquota, 10);
 	isc_quota_init(&sctx->recursionquota, 100);
+	isc_quota_init(&sctx->updquota, 100);
 	ISC_LIST_INIT(sctx->http_quotas);
 	isc_mutex_init(&sctx->http_quotas_lock);
 
@@ -136,6 +137,7 @@ ns_server_detach(ns_server_t **sctxp) {
 			isc_mem_put(sctx->mctx, altsecret, sizeof(*altsecret));
 		}
 
+		isc_quota_destroy(&sctx->updquota);
 		isc_quota_destroy(&sctx->recursionquota);
 		isc_quota_destroy(&sctx->tcpquota);
 		isc_quota_destroy(&sctx->xfroutquota);
diff --git a/lib/ns/update.c b/lib/ns/update.c
index 186eef1d1de..1f296488c77 100644
--- a/lib/ns/update.c
+++ b/lib/ns/update.c
@@ -1645,6 +1645,19 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
 	update_event_t *event = NULL;
 	isc_task_t *zonetask = NULL;
 
+	result = isc_quota_attach(&client->manager->sctx->updquota,
+				  &(isc_quota_t *){ NULL });
+	if (result != ISC_R_SUCCESS) {
+		update_log(client, zone, LOGLEVEL_PROTOCOL,
+			   "update failed: too many DNS UPDATEs queued (%s)",
+			   isc_result_totext(result));
+		ns_stats_increment(client->manager->sctx->nsstats,
+				   ns_statscounter_updatequota);
+		ns_client_drop(client, result);
+		isc_nmhandle_detach(&client->reqhandle);
+		return (DNS_R_DROP);
+	}
+
 	event = (update_event_t *)isc_event_allocate(
 		client->mctx, client, DNS_EVENT_UPDATE, update_action, NULL,
 		sizeof(*event));
@@ -1783,12 +1796,19 @@ failure:
 		       dns_zone_gettype(zone) == dns_zone_mirror);
 		inc_stats(client, zone, ns_statscounter_updaterej);
 	}
+
 	/*
 	 * We failed without having sent an update event to the zone.
 	 * We are still in the client task context, so we can
 	 * simply give an error response without switching tasks.
 	 */
-	respond(client, result);
+	if (result == DNS_R_DROP) {
+		ns_client_drop(client, result);
+		isc_nmhandle_detach(&client->reqhandle);
+	} else {
+		respond(client, result);
+	}
+
 	if (zone != NULL) {
 		dns_zone_detach(&zone);
 	}
@@ -3669,6 +3689,7 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
 
 	respond(client, uev->result);
 
+	isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
 	isc_event_free(&event);
 	isc_nmhandle_detach(&client->updatehandle);
 }
@@ -3685,6 +3706,8 @@ forward_fail(isc_task_t *task, isc_event_t *event) {
 	INSIST(client->nupdates > 0);
 	client->nupdates--;
 	respond(client, DNS_R_SERVFAIL);
+
+	isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
 	isc_event_free(&event);
 	isc_nmhandle_detach(&client->updatehandle);
 }
@@ -3722,6 +3745,8 @@ forward_done(isc_task_t *task, isc_event_t *event) {
 	client->nupdates--;
 	ns_client_sendraw(client, uev->answer);
 	dns_message_detach(&uev->answer);
+
+	isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
 	isc_event_free(&event);
 	isc_nmhandle_detach(&client->reqhandle);
 	isc_nmhandle_detach(&client->updatehandle);
@@ -3757,6 +3782,17 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
 	update_event_t *event = NULL;
 	isc_task_t *zonetask = NULL;
 
+	result = isc_quota_attach(&client->manager->sctx->updquota,
+				  &(isc_quota_t *){ NULL });
+	if (result != ISC_R_SUCCESS) {
+		update_log(client, zone, LOGLEVEL_PROTOCOL,
+			   "update failed: too many DNS UPDATEs queued (%s)",
+			   isc_result_totext(result));
+		ns_stats_increment(client->manager->sctx->nsstats,
+				   ns_statscounter_updatequota);
+		return (DNS_R_DROP);
+	}
+
 	event = (update_event_t *)isc_event_allocate(
 		client->mctx, client, DNS_EVENT_UPDATE, forward_action, NULL,
 		sizeof(*event));
