diff --git a/src/knot/server/zone-load.c b/src/knot/server/zone-load.c
index e2bed82b7027518be448a3599ff8a8ff37562193..d7f68e2431df2f1ac95afdf77224a03659ee2114 100644
--- a/src/knot/server/zone-load.c
+++ b/src/knot/server/zone-load.c
@@ -619,6 +619,26 @@ static int update_zone(knot_zone_t **dst, conf_zone_t *conf, knot_nameserver_t *
 
 	zones_schedule_ixfr_sync(new_zone, conf->dbsync_timeout);
 
+	knot_zone_contents_t *new_contents = new_zone->contents;
+	if (new_contents) {
+		/* Check NSEC3PARAM state if present. */
+		result = knot_zone_contents_load_nsec3param(new_contents);
+		if (result != KNOT_EOK) {
+			log_zone_error("NSEC3 signed zone has invalid or no "
+				       "NSEC3PARAM record.\n");
+			goto fail;
+		}
+		/* Check minimum EDNS0 payload if signed. (RFC4035/sec. 3) */
+		if (knot_zone_contents_is_signed(new_contents)) {
+			unsigned edns_dnssec_min = EDNS_MIN_DNSSEC_PAYLOAD;
+			if (knot_edns_get_payload(ns->opt_rr) < edns_dnssec_min) {
+				log_zone_warning("EDNS payload lower than %uB for "
+						 "DNSSEC-enabled zone '%s'.\n",
+						 edns_dnssec_min, conf->name);
+			}
+		}
+	}
+
 fail:
 	assert(new_zone);
 
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index a041597b6e30fa813c8b73c725a5ac549f4e58aa..1a06ff230e45c34d88b70e1d8444355bec9a0869 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -1944,12 +1944,6 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data)
 	knot_nameserver_t *ns = (knot_nameserver_t *)data;
 	dbg_zones_verb("zones: reconfiguring name server.\n");
 
-	knot_zonedb_t *old_db = NULL;
-	int ret = zones_update_db_from_config(conf, ns, &old_db);
-	if (ret != KNOT_EOK) {
-		return ret;
-	}
-
 	/* Server identification, RFC 4892. */
 	ns->identity = conf->identity;
 	ns->version = conf->version;
@@ -1971,6 +1965,12 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data)
 	knot_opt_rr_t *opt_rr_old = ns->opt_rr;
 	ns->opt_rr = opt_rr;
 
+	knot_zonedb_t *old_db = NULL;
+	int ret = zones_update_db_from_config(conf, ns, &old_db);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
 	synchronize_rcu();
 
 	knot_edns_free(&opt_rr_old);
diff --git a/src/libknot/edns.h b/src/libknot/edns.h
index c5e48a32c8ca3428b80cdf17edd0827e320d82f3..947628730c9633a34603b1986153bd10c37c6750 100644
--- a/src/libknot/edns.h
+++ b/src/libknot/edns.h
@@ -75,6 +75,7 @@ typedef struct knot_opt_rr knot_opt_rr_t;
 /*! \brief Constants for EDNS. */
 enum knot_edns_const {
 	EDNS_MIN_UDP_PAYLOAD = 512,  /*!< Minimal UDP payload with EDNS enabled. */
+	EDNS_MIN_DNSSEC_PAYLOAD = 1220, /*!< Minimal payload when using DNSSEC (RFC4035/sec.3) */
 	EDNS_MAX_UDP_PAYLOAD = 4096, /*!< Maximal UDP payload with EDNS enabled. */
 	EDNS_VERSION         = 0,    /*!< Supported EDNS version. */
 	EDNS_NOT_SUPPORTED   = 255,  /*!< EDNS not supported. */
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index f8f1a8d8b885c319cc92d892f8c0320bc3d6b2aa..71c6819b56fd5b2861f09ddf3c8c58c81ed19597 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -2045,10 +2045,21 @@ int knot_zone_contents_integrity_check(const knot_zone_contents_t *contents)
 	return data.errors;
 }
 
-unsigned knot_zone_serial(const knot_zone_contents_t *zone)
+uint32_t knot_zone_serial(const knot_zone_contents_t *zone)
 {
 	if (!zone) return 0;
 	const knot_rrset_t *soa = NULL;
 	soa = knot_node_rrset(knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
 	return knot_rdata_soa_serial(soa);
 }
+
+bool knot_zone_contents_is_signed(const knot_zone_contents_t *zone)
+{
+	const knot_rrset_t *soa = NULL;
+	if (zone->apex) {
+		/* Returns true if SOA has a RRSIG (basic check). */
+		soa = knot_node_rrset(zone->apex, KNOT_RRTYPE_SOA);
+		return soa && soa->rrsigs;
+	}
+	return false;
+}
diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h
index d2438d542ad0bfbf866f7eb97a31e90aa7f238b6..94ec1e902f3d8ac20bd239f5e339ed9dd81af8b8 100644
--- a/src/libknot/zone/zone-contents.h
+++ b/src/libknot/zone/zone-contents.h
@@ -503,7 +503,12 @@ int knot_zone_contents_integrity_check(const knot_zone_contents_t *contents);
  *
  * \return serial or 0
  */
-unsigned knot_zone_serial(const knot_zone_contents_t *zone);
+uint32_t knot_zone_serial(const knot_zone_contents_t *zone);
+
+/*!
+ * \brief Return true if zone is signed.
+ */
+bool knot_zone_contents_is_signed(const knot_zone_contents_t *zone);
 
 #endif
 
diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c
index 40ed8575d962258bf46d497355f87592eed5c1cd..3cdbecbda8990366489ac65e115bcde042858a57 100644
--- a/src/libknot/zone/zonedb.c
+++ b/src/libknot/zone/zonedb.c
@@ -134,18 +134,6 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone)
 		return KNOT_EINVAL;
 	}
 
-	/*! \todo Why is this check here? */
-	int ret = KNOT_EOK;
-	if (knot_zone_contents(zone)) {
-		ret = knot_zone_contents_load_nsec3param(
-				knot_zone_get_contents(zone));
-		if (ret != KNOT_EOK) {
-			log_zone_error("NSEC3 signed zone has invalid or no "
-			               "NSEC3PARAM record.\n");
-			return ret;
-		}
-	}
-
 	/* Invalidate search index. */
 	db->stack_height = 0;
 
@@ -153,7 +141,7 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone)
 	assert(db->count < db->reserved); /* Should be already checked. */
 	db->array[db->count++] = zone;
 
-	return ret;
+	return KNOT_EOK;
 }
 
 /*----------------------------------------------------------------------------*/