diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
index 89420c2353a686db7e229ea7d38d6fc1187ef6e5..989ef30e833473868a8967afa030b791b132efd5 100644
--- a/src/knot/nameserver/internet.c
+++ b/src/knot/nameserver/internet.c
@@ -468,10 +468,17 @@ static int name_not_found(knot_pkt_t *pkt, struct query_data *qdata)
 	dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);
 
 	/* Name is covered by wildcard. */
-	const knot_node_t *wildcard_node = knot_node_wildcard_child(qdata->encloser);
-	if (wildcard_node) {
+	if (knot_node_has_wildcard_child(qdata->encloser)) {
 		dbg_ns("%s: name %p covered by wildcard\n", __func__, qdata->name);
+
+		/* Find wildcard child in the zone. */
+		const knot_node_t *wildcard_node =
+		                knot_zone_contents_find_wildcard_child(
+		                        qdata->zone->contents, qdata->encloser);
+
 		qdata->node = wildcard_node;
+		assert(qdata->node != NULL);
+
 		/* keep encloser */
 		qdata->previous = NULL;
 
@@ -480,7 +487,7 @@ static int name_not_found(knot_pkt_t *pkt, struct query_data *qdata)
 
 		/* Put to wildcard node list. */
 		if (wildcard_visit(qdata, wildcard_node, qdata->name) != KNOT_EOK) {
-				next_state = ERROR;
+			next_state = ERROR;
 		}
 
 		return next_state;
diff --git a/src/knot/updates/xfr-in.c b/src/knot/updates/xfr-in.c
index 170055c7bbaec71ef2bb238f993f7e47aed7fe84..5ff46af34d7b8f85ac7271219d8479564f5a4ac9 100644
--- a/src/knot/updates/xfr-in.c
+++ b/src/knot/updates/xfr-in.c
@@ -987,8 +987,9 @@ static int xfrin_mark_empty(knot_node_t **node_p, void *data)
 		}
 		knot_node_set_empty(node);
 		if (node->parent) {
-			if (node->parent->wildcard_child == node) {
-				node->parent->wildcard_child = NULL;
+			if (knot_node_has_wildcard_child(node->parent)
+			    && knot_dname_is_wildcard(node->owner)) {
+				knot_node_clear_wildcard_child(node->parent);
 			}
 			node->parent->children--;
 			// Recurse using the parent node
diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c
index a27c351aba63b67d1fdc11f8e876525ab4340f19..76291fb011831ab47d931753b169f16481d9c95e 100644
--- a/src/knot/zone/node.c
+++ b/src/knot/zone/node.c
@@ -377,37 +377,27 @@ knot_dname_t *knot_node_get_owner(const knot_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
-knot_node_t *knot_node_get_wildcard_child(const knot_node_t *node)
+void knot_node_set_wildcard_child(knot_node_t *node)
 {
 	if (node == NULL) {
-		return NULL;
+		return;
 	}
 
-	return node->wildcard_child;
+	knot_node_flags_set(node, KNOT_NODE_FLAGS_WILDCARD_CHILD);
 }
 
 /*----------------------------------------------------------------------------*/
 
-void knot_node_set_wildcard_child(knot_node_t *node,
-                                  knot_node_t *wildcard_child)
+int knot_node_has_wildcard_child(const knot_node_t *node)
 {
-	if (node == NULL) {
-		return;
-	}
-
-	node->wildcard_child = wildcard_child;
-//	assert(wildcard_child->parent == node);
+	return knot_node_flags_get(node, KNOT_NODE_FLAGS_WILDCARD_CHILD);
 }
 
 /*----------------------------------------------------------------------------*/
 
-const knot_node_t *knot_node_wildcard_child(const knot_node_t *node)
+void knot_node_clear_wildcard_child(knot_node_t *node)
 {
-	if (node == NULL) {
-		return NULL;
-	}
-
-	return knot_node_get_wildcard_child(node);
+	knot_node_flags_clear(node, KNOT_NODE_FLAGS_WILDCARD_CHILD);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -641,4 +631,3 @@ bool knot_node_rrtype_exists(const knot_node_t *node, uint16_t type)
 {
 	return knot_node_rrs(node, type) != NULL;
 }
-
diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h
index 22f8c859b3666201bee38ca767e61c285e342447..a18fefe8008bf5aed946aa68068a0d835aaf10a7 100644
--- a/src/knot/zone/node.h
+++ b/src/knot/zone/node.h
@@ -48,9 +48,6 @@ struct knot_node {
 	/*! \brief Type-ordered array of RRSets belonging to this node. */
 	struct rr_data *rrs;
 
-	/*! \brief Wildcard node being the direct descendant of this node. */
-	struct knot_node *wildcard_child;
-
 	/*!
 	 * \brief Previous node in canonical order.
 	 *
@@ -75,10 +72,7 @@ struct knot_node {
 	/*!
 	 * \brief Various flags.
 	 *
-	 *   0x01 - node is a delegation point
-	 *   0x02 - node is non-authoritative (under a delegation point)
-	 *   0x04 - NSEC(3) was removed from the node.
-	 *   0x10 - node is empty and will be deleted after update
+	 * \ref knot_node_flags_t
 	 */
 	uint8_t flags;
 };
@@ -106,6 +100,8 @@ typedef enum {
 	/*! \brief Node is empty and will be deleted after update.
 	 *  \todo Remove after dname refactoring, update description in node. */
 	KNOT_NODE_FLAGS_EMPTY = 1 << 4,
+	/*! \brief Node has a wildcard child. */
+	KNOT_NODE_FLAGS_WILDCARD_CHILD = 1 << 5
 } knot_node_flags_t;
 
 /*----------------------------------------------------------------------------*/
@@ -260,29 +256,37 @@ void knot_node_set_nsec3_node(knot_node_t *node, knot_node_t *nsec3_node);
 const knot_dname_t *knot_node_owner(const knot_node_t *node);
 
 /*!
- * \todo Document me.
+ * \brief Returns the owner of the node as a non-const reference.
+ *
+ * \param node Node to get the owner of.
+ *
+ * \return Owner of the given node.
  */
 knot_dname_t *knot_node_get_owner(const knot_node_t *node);
 
 /*!
- * \brief Returns the wildcard child of the node.
- *
- * \param node Node to get the owner of.
+ * \brief Sets the wildcard child flag of the node.
  *
- * \return Wildcard child of the given node or NULL if it has none.
+ * \param node Node that has wildcard.
  */
-const knot_node_t *knot_node_wildcard_child(const knot_node_t *node);
+void knot_node_set_wildcard_child(knot_node_t *node);
 
 /*!
- * \brief Sets the wildcard child of the node.
+ * \brief Checks if node has a wildcard child.
  *
- * \param node Node to set the wildcard child of.
- * \param wildcard_child Wildcard child of the node.
+ * \param node Node to check.
+ *
+ * \retval > 0 if the node has a wildcard child.
+ * \retval 0 otherwise.
  */
-void knot_node_set_wildcard_child(knot_node_t *node,
-                                  knot_node_t *wildcard_child);
+int knot_node_has_wildcard_child(const knot_node_t *node);
 
-knot_node_t *knot_node_get_wildcard_child(const knot_node_t *node);
+/*!
+ * \brief Clears the node's wildcard child flag.
+ *
+ * \param node Node to clear the flag in.
+ */
+void knot_node_clear_wildcard_child(knot_node_t *node);
 
 /*!
  * \brief Mark the node as a delegation point.
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index b80f4bd33286a40dbc19009832e75f2bfecf910b..6fee227fc8dc9a9c7365ad682b50b3505e53ecd1 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -304,9 +304,6 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	}
 
 	if (knot_rrsig_type_covered(rrsig, 0) != rrset->type) {
-		/* zoneparser would not let this happen
-		 * but to be on the safe side
-		 */
 		err_handler_handle_error(handler, node,
 		                         ZC_ERR_RRSIG_RDATA_TYPE_COVERED,
 		                         info_str);
@@ -1092,9 +1089,6 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 
 		/* Check it points somewhere first. */
 		if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
-			assert(knot_zone_contents_find_node(zone,
-			                                    next_dname) ==
-			                                    NULL);
 			err_handler_handle_error(handler, last_nsec3_node,
 						 ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 		} else {
@@ -1108,9 +1102,9 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
 		/* Directly discard. */
 		knot_dname_free(&next_dname, NULL);
 
-	} else if (do_checks == 2 ) {
+	} else if (do_checks == SEM_CHECK_NSEC) {
 		if (last_node == NULL) {
-			err_handler_handle_error(handler, last_node,
+			err_handler_handle_error(handler, zone->apex,
 				ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC, NULL);
 				return;
 		} else {
diff --git a/src/knot/zone/zone-contents.c b/src/knot/zone/zone-contents.c
index 867b578f55078f9940c05747a702515731d1f9bc..b0fea95a56665c13f9e654d390dcab042a4df444 100644
--- a/src/knot/zone/zone-contents.c
+++ b/src/knot/zone/zone-contents.c
@@ -178,8 +178,12 @@ static int discover_additionals(struct rr_data *rr_data,
 		/* Try to find node for the dname in the RDATA. */
 		dname = knot_rdata_name(rrs, i, rr_data->type);
 		knot_zone_contents_find_dname(zone, dname, &node, &encloser, &prev);
-		if (node == NULL && encloser && encloser->wildcard_child) {
-			node = encloser->wildcard_child;
+		if (node == NULL && encloser
+		    && knot_node_has_wildcard_child(encloser)) {
+			/* Find wildcard child in the zone. */
+			node = knot_zone_contents_find_wildcard_child(zone,
+			                                              encloser);
+			assert(node != NULL);
 		}
 
 		rr_data->additional[i] = (knot_node_t *)node;
@@ -203,11 +207,14 @@ static int adjust_pointers(knot_node_t **tnode, void *data)
 		args->first_node = node;
 	}
 
+	// clear Removed NSEC flag so that no relicts remain
+	knot_node_clear_removed_nsec(node);
+
 	// check if this node is not a wildcard child of its parent
 
 	if (knot_dname_is_wildcard(knot_node_owner(node))) {
 		assert(knot_node_parent(node) != NULL);
-		knot_node_set_wildcard_child(knot_node_get_parent(node), node);
+		knot_node_set_wildcard_child(knot_node_get_parent(node));
 	}
 
 	// set flags (delegation point, non-authoritative)
@@ -554,7 +561,7 @@ dbg_zone_exec_detail(
 
 		// check if the node is not wildcard child of the parent
 		if (knot_dname_is_wildcard(knot_node_owner(node))) {
-			knot_node_set_wildcard_child(zone->apex, node);
+			knot_node_set_wildcard_child(zone->apex);
 		}
 	} else {
 		while (parent != NULL &&
@@ -578,7 +585,7 @@ dbg_zone_exec_detail(
 			/* Update node pointers. */
 			knot_node_set_parent(node, next_node);
 			if (knot_dname_is_wildcard(knot_node_owner(node))) {
-				knot_node_set_wildcard_child(next_node, node);
+				knot_node_set_wildcard_child(next_node);
 			}
 
 			++zone->node_count;
@@ -1122,6 +1129,20 @@ const knot_node_t *knot_zone_contents_apex(
 
 /*----------------------------------------------------------------------------*/
 
+const knot_node_t *knot_zone_contents_find_wildcard_child(
+                const knot_zone_contents_t *contents, const knot_node_t *parent)
+{
+	if (contents == NULL || parent == NULL || parent->owner == NULL) {
+		return NULL;
+	}
+
+	knot_dname_t wildcard[KNOT_DNAME_MAXLEN] = { 0x01, '*' };
+	knot_dname_to_wire(wildcard + 2, parent->owner, KNOT_DNAME_MAXLEN - 2);
+	return knot_zone_contents_find_node(contents, wildcard);
+}
+
+/*----------------------------------------------------------------------------*/
+
 static int knot_zone_contents_adjust_nodes(knot_zone_tree_t *nodes,
                                            knot_zone_adjust_arg_t *adjust_arg,
                                            knot_zone_tree_apply_cb_t callback)
diff --git a/src/knot/zone/zone-contents.h b/src/knot/zone/zone-contents.h
index 01ed95c234061b3de57c9f1a3d7e4db88d06e9a5..f25c8c3e3b2801a9e22a63b2cb2bb16ba668377b 100644
--- a/src/knot/zone/zone-contents.h
+++ b/src/knot/zone/zone-contents.h
@@ -198,6 +198,9 @@ int knot_zone_contents_find_nsec3_for_name(
 const knot_node_t *knot_zone_contents_apex(
 	const knot_zone_contents_t *contents);
 
+const knot_node_t *knot_zone_contents_find_wildcard_child(
+               const knot_zone_contents_t *contents, const knot_node_t *parent);
+
 /*!
  * \brief Sets parent and previous pointers and node flags. (cheap operation)
  *        For both normal and NSEC3 tree
diff --git a/src/knot/zone/zone-create.c b/src/knot/zone/zone-create.c
index 0a76151bbabcd3ef08a32320778b85885fa3534e..7c12851ca09c32cb774ff687a0db39b180d0fcf6 100644
--- a/src/knot/zone/zone-create.c
+++ b/src/knot/zone/zone-create.c
@@ -289,7 +289,7 @@ knot_zone_contents_t *zonefile_load(zloader_t *loader)
 		assert(!knot_rrset_empty(&soa_rr)); // In this point, SOA has to exist
 		const bool have_nsec3param =
 			knot_node_rrtype_exists(zc->z->apex, KNOT_RRTYPE_NSEC3PARAM);
-		if (knot_zone_contents_is_signed(zc->z) && have_nsec3param) {
+		if (knot_zone_contents_is_signed(zc->z) && !have_nsec3param) {
 			/* Set check level to DNSSEC. */
 			check_level = SEM_CHECK_NSEC;
 		} else if (knot_zone_contents_is_signed(zc->z) && have_nsec3param) {
diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py
index 87d1d3b37b851ffe7827e359faea681b1bd819d5..c1254f53d6c7ec96d4403c43ccb8cb72931c7311 100644
--- a/tests-extra/tools/dnstest/server.py
+++ b/tests-extra/tools/dnstest/server.py
@@ -878,6 +878,7 @@ class Knot(Server):
         s.item("zonefile-sync", "0")
         s.item("notify-timeout", "5")
         s.item("notify-retries", "5")
+        s.item("semantic-checks", "on")
         if self.disable_any:
             s.item("disable-any", "on")
         if self.dnssec_enable: