diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index 78b5480f3cfb202e38dfd413bbe6f6cbcff2705c..d0292e8ff847c822a01bcf4d494b1b7e770871b9 100644
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -270,10 +270,12 @@ int notify_process_request(knot_nameserver_t *ns,
 	}
 
 	// find the zone
+	rcu_read_lock();
 	const knot_dname_t *qname = knot_packet_qname(notify);
 	const knot_zone_t *z = knot_zonedb_find_zone_for_name(
 			ns->zone_db, qname);
 	if (z == NULL) {
+		rcu_read_unlock();
 		dbg_notify("notify: failed to find zone by name\n");
 		knot_ns_error_response_from_query(ns, notify,
 		                                  KNOT_RCODE_FORMERR, buffer,
@@ -282,7 +284,7 @@ int notify_process_request(knot_nameserver_t *ns,
 	}
 
 	notify_check_and_schedule(ns, z, from);
-
+	rcu_read_unlock();
 	return KNOTD_EOK;
 }
 
@@ -302,13 +304,16 @@ int notify_process_response(knot_nameserver_t *nameserver,
 	*size = 0;
 
 	/* Find matching zone. */
+	rcu_read_lock();
 	const knot_dname_t *zone_name = knot_packet_qname(notify);
 	knot_zone_t *zone = knot_zonedb_find_zone(nameserver->zone_db,
 	                                              zone_name);
 	if (!zone) {
+		rcu_read_unlock();
 		return KNOTD_ENOENT;
 	}
 	if (!knot_zone_data(zone)) {
+		rcu_read_unlock();
 		return KNOTD_ENOENT;
 	}
 
@@ -326,6 +331,7 @@ int notify_process_response(knot_nameserver_t *nameserver,
 
 	/* Found waiting NOTIFY query? */
 	if (!match) {
+		rcu_read_unlock();
 		pthread_mutex_unlock(&zd->lock);
 		return KNOTD_ERROR;
 	}
@@ -335,6 +341,8 @@ int notify_process_response(knot_nameserver_t *nameserver,
 
 	/* Zone was removed/reloaded. */
 	pthread_mutex_unlock(&zd->lock);
+	
+	rcu_read_unlock();
 
 	return KNOTD_EOK;
 }
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index eb11c89c2037c7543add5329bbf04244c029d37d..f52ea779ef967f45ff99a37d4330cd7e14f74809 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -93,6 +93,9 @@ static void xfr_free_task(knot_ns_xfr_t *task)
 		}
 	}
 	
+	/* No further access to zone. */
+	knot_zone_release(task->zone);
+	
 	/* Deinitialize */
 	xfr_request_deinit(task);
 	if (!task->session_closed) {
@@ -1289,6 +1292,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
 		return KNOTD_EINVAL;
 	}
 	
+	rcu_read_lock();
 	int ret = knot_ns_init_xfr(ns, xfr);
 
 	int xfr_failed = (ret != KNOT_EOK);
@@ -1372,6 +1376,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
 	free(xfr->tsig_data);
 	xfr->tsig_data = NULL;
 	xfr_request_deinit(xfr);
+	rcu_read_unlock();
 	
 	/* Cleanup. */
 	free(xfr->digest);
@@ -1423,6 +1428,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
 		
 		/* Report. */
 		if (ret != KNOTD_EOK && ret != KNOTD_EACCES) {
+			/*! \todo #1976 Do not reschedule if zone is being discarded. */
 			if (zd != NULL && !knot_zone_contents(xfr.zone)) {
 				/* Reschedule request (120 - 240s random delay). */
 				int tmr_s = AXFR_BOOTSTRAP_RETRY * 2; /* Malus x2 */
@@ -1439,6 +1445,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
 				log_server_error("%s %s\n",
 				                 xfr.msgpref, knotd_strerror(ret));
 			}
+			knot_zone_release(xfr.zone); /* No further access to zone. */
 		}
 		
 		break;
@@ -1447,14 +1454,14 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
 		/* Register task. */
 		task = xfr_register_task(w, &xfr);
 		if (!task) {
+			knot_zone_release(xfr.zone); /* No further access to zone. */
 			ret = KNOTD_ENOMEM;
-			break;
+		} else {
+			/* Add timeout. */
+			fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD);
+			log_server_info("%s Query issued.\n", xfr.msgpref);
+			ret = KNOTD_EOK;
 		}
-		
-		/* Add timeout. */
-		fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD);
-		log_server_info("%s Query issued.\n", xfr.msgpref);
-		ret = KNOTD_EOK;
 		break;
 	default:
 		log_server_error("Unknown XFR request type (%d).\n", xfr.type);
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 65ad797c47e866b2653de4ec99192f56b70a80a0..edec93affd8bc8ba224f5659f73e15388e06d960 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -377,8 +377,10 @@ static int zones_expire_ev(event_t *e)
 static int zones_refresh_ev(event_t *e)
 {
 	dbg_zones("zones: REFRESH or RETRY timer event\n");
+	rcu_read_lock();
 	knot_zone_t *zone = (knot_zone_t *)e->data;
 	if (zone == NULL || zone->data == NULL) {
+		rcu_read_unlock();
 		return KNOTD_EINVAL;
 	}
 
@@ -386,7 +388,6 @@ static int zones_refresh_ev(event_t *e)
 	zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
 
 	/* Check for contents. */
-	rcu_read_lock();
 	if (!knot_zone_contents(zone)) {
 
 		/* Bootstrap from XFR master. */
@@ -429,13 +430,20 @@ static int zones_refresh_ev(event_t *e)
 		++zd->xfr_in.scheduled;
 		pthread_mutex_unlock(&zd->xfr_in.lock);
 		
+		/* Retain pointer to zone for processing. */
+		knot_zone_retain(xfr_req.zone);
+		
 		/* Unlock zone contents. */
 		rcu_read_unlock();
 		
 		/* Mark as finished to prevent stalling. */
 		evsched_event_finished(e->parent);
+		int ret = xfr_request(zd->server->xfr_h, &xfr_req);
+		if (ret != KNOTD_EOK) {
+			knot_zone_release(xfr_req.zone); /* Discard */
+		}
+		return ret;
 		
-		return xfr_request(zd->server->xfr_h, &xfr_req);
 	}
 	
 	/* Do not issue SOA query if transfer is pending. */
@@ -446,6 +454,7 @@ static int zones_refresh_ev(event_t *e)
 		          zd->conf->name);
 		
 		/* Reschedule as RETRY timer. */
+		/*! \todo #1976 Do not reschedule if zone is being discarded. */
 		uint32_t retry_tmr = zones_jitter(zones_soa_retry(zone));
 		evsched_schedule(e->parent, e, retry_tmr);
 		dbg_zones("zones: RETRY of '%s' after %u seconds\n",
@@ -470,6 +479,7 @@ static int zones_refresh_ev(event_t *e)
 	}
 	
 	/* Reschedule as RETRY timer. */
+	/*! \todo #1976 Do not reschedule if zone is being discarded. */
 	uint32_t retry_tmr = zones_jitter(zones_soa_retry(zone));
 	evsched_schedule(e->parent, e, retry_tmr);
 	dbg_zones("zones: RETRY of '%s' after %u seconds\n",
@@ -558,10 +568,14 @@ static int zones_refresh_ev(event_t *e)
 	memcpy(&req.saddr, &zd->xfr_in.via, sizeof(sockaddr_t));
 	sockaddr_update(&req.addr);
 	sockaddr_update(&req.saddr);
+	
+	/* Retain pointer to zone and issue. */
+	knot_zone_retain(req.zone);
 	if (ret == KNOTD_EOK) {
 		ret = xfr_request(zd->server->xfr_h, &req);
 	}
 	if (ret != KNOTD_EOK) {
+		knot_zone_release(req.zone); /* Discard */
 		log_server_warning("Failed to issue SOA query for zone '%s' (%s).\n",
 		                   zd->conf->name, errstr);
 	}
@@ -580,22 +594,23 @@ static int zones_refresh_ev(event_t *e)
 static int zones_notify_send(event_t *e)
 {
 	dbg_notify("notify: NOTIFY timer event\n");
-	
+	rcu_read_lock();
 	notify_ev_t *ev = (notify_ev_t *)e->data;
 	if (ev == NULL) {
+		rcu_read_unlock();
 		log_zone_error("NOTIFY invalid event received\n");
 		return KNOTD_EINVAL;
 	}
+	
 	knot_zone_t *zone = ev->zone;
 	if (zone == NULL || zone->data == NULL) {
+		rcu_read_unlock();
 		log_zone_error("NOTIFY invalid event data received\n");
 		evsched_event_free(e->parent, e);
 		free(ev);
 		return KNOTD_EINVAL;
 	}
 
-	rcu_read_lock();
-
 	/* Check for answered/cancelled query. */
 	zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
 	knot_zone_contents_t *contents = knot_zone_get_contents(zone);
@@ -621,6 +636,7 @@ static int zones_notify_send(event_t *e)
 	int retry_tmr = ev->timeout * 1000;
  
 	/* Reschedule. */
+	/*! \todo #1976 Do not reschedule if zone is being discarded. */
 	evsched_schedule(e->parent, e, retry_tmr);
 	dbg_notify("notify: Query RETRY after %u secs (zone '%s')\n",
 	           retry_tmr / 1000, zd->conf->name);
@@ -675,7 +691,13 @@ static int zones_notify_send(event_t *e)
 		req.zone = zone;
 		memcpy(&req.addr, &ev->addr, sizeof(sockaddr_t));
 		memcpy(&req.saddr, &ev->saddr, sizeof(sockaddr_t));
-		xfr_request(zd->server->xfr_h, &req);
+		
+		/* Retain pointer to zone and issue request. */
+		knot_zone_retain(req.zone);
+		ret = xfr_request(zd->server->xfr_h, &req);
+		if (ret != KNOTD_EOK) {
+			knot_zone_release(req.zone); /* Discard */
+		}
 	}
 	
 	free(qbuf);
@@ -2440,6 +2462,7 @@ int zones_process_response(knot_nameserver_t *nameserver,
 		}
 
 		/* Find matching zone and ID. */
+		rcu_read_lock();
 		const knot_dname_t *zone_name = knot_packet_qname(packet);
 		/*! \todo Change the access to the zone db. */
 		knot_zone_t *zone = knot_zonedb_find_zone(
@@ -2447,7 +2470,6 @@ int zones_process_response(knot_nameserver_t *nameserver,
 		                        zone_name);
 
 		/* Get zone contents. */
-		rcu_read_lock();
 		const knot_zone_contents_t *contents =
 				knot_zone_contents(zone);
 
@@ -2526,9 +2548,13 @@ int zones_process_response(knot_nameserver_t *nameserver,
 		/* Unlock zone contents. */
 		rcu_read_unlock();
 
-		/* Enqueue XFR request. */
-		return xfr_request(((server_t *)knot_ns_get_data(
-		                     nameserver))->xfr_h, &xfr_req);
+		/* Retain pointer to zone for processing. */
+		knot_zone_retain(xfr_req.zone);
+		ret = xfr_request(((server_t *)knot_ns_get_data(
+		                  nameserver))->xfr_h, &xfr_req);
+		if (ret != KNOTD_EOK) {
+			knot_zone_release(xfr_req.zone); /* Discard */
+		}
 	}
 
 	return KNOTD_EOK;
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index d214763739194f29608db519c4a27ae07138df35..134fa8a6596138982d94f15976a77bc587eda3af 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -3936,9 +3936,9 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver,
 		return KNOT_ENOZONE;
 	}
 
-	// find the zone in the zone db
-	knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db,
-			knot_node_owner(knot_zone_contents_apex(zone)));
+	/* Zone must not be looked-up from server, as it may be a different zone if
+	 * a reload occurs when transfer is pending. */
+	knot_zone_t *z = xfr->zone;
 	if (z == NULL) {
 		char *name = knot_dname_to_str(knot_node_owner(
 				knot_zone_contents_apex(zone)));
@@ -3957,6 +3957,8 @@ dbg_ns_exec_verb(
 	dbg_ns_verb("Zone db contents: (zone count: %zu)\n",
 	            nameserver->zone_db->zone_count);
 
+	/* Warning: may not show updated zone if updated zone that is already
+	 *          discarded from zone db (reload with pending transfer). */
 	const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db);
 	for (int i = 0; i < knot_zonedb_zone_count
 	     (nameserver->zone_db); i++) {