diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index 1eea00aace1031fa2c7cfe847849e139c44e46b6..79d2a405f3d53d117901370f03688d39154a5ca1 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -18,6 +18,7 @@
 
 #include <libknot/descriptor.h>
 #include <libknot/rrtype/rdname.h>
+#include <libknot/rrtype/rrsig.h>
 
 #include "lib/layer/iterate.h"
 #include "lib/resolve.h"
@@ -256,6 +257,24 @@ static int update_cut(knot_pkt_t *pkt, const knot_rrset_t *rr, struct kr_request
 	return state;
 }
 
+static const knot_dname_t *signature_authority(knot_pkt_t *pkt)
+{
+	/* Can't find signer for RRSIGs, bail out. */
+	if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_RRSIG) {
+		return NULL;
+	}
+	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_AUTHORITY; ++i) {
+		const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
+		for (unsigned k = 0; k < sec->count; ++k) {
+			const knot_rrset_t *rr = knot_pkt_rr(sec, k);
+			if (rr->type == KNOT_RRTYPE_RRSIG) {
+				return knot_rrsig_signer_name(&rr->rrs, 0);
+			}
+		}
+	}
+	return NULL;
+}
+
 static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
 {
 	int result = KNOT_STATE_CONSUME;
@@ -288,20 +307,41 @@ static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
 			case KNOT_STATE_FAIL: return state; break;
 			default:              /* continue */ break;
 			}
-		} else if (rr->type == KNOT_RRTYPE_SOA) {
+		} else if (rr->type == KNOT_RRTYPE_SOA && knot_dname_is_sub(rr->owner, qry->zone_cut.name)) {
 			/* SOA below cut in authority indicates different authority, but same NS set. */
-			if (knot_dname_is_sub(rr->owner, qry->zone_cut.name)) {
-				qry->zone_cut.name = knot_dname_copy(rr->owner, &req->pool);
-				if (qry->flags & QUERY_DNSSEC_WANT) { /* Treat as a referral */
-					result = KNOT_STATE_DONE;
-					break;
-				}
-			}
+			qry->zone_cut.name = knot_dname_copy(rr->owner, &req->pool);
 		}
 	}
 
+	/* Track difference between current TA and signer name.
+	 * This indicates that the NS is auth for both parent-child, and we must update DS/DNSKEY to validate it.
+	 * @todo: This has to be checked here before we put the data into packet, there is no "DEFER" or "PAUSE" action yet.
+	 */
+	const bool track_pc_change = (!(qry->flags & QUERY_CACHED) && (qry->flags & QUERY_DNSSEC_WANT));
+	const knot_dname_t *ta_name = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL;
+	const knot_dname_t *signer = signature_authority(pkt);
+	if (track_pc_change && ta_name && signer && !knot_dname_is_equal(ta_name, signer)) {
+		DEBUG_MSG(">< cut changed, needs revalidation\n");
+		if (knot_dname_is_sub(signer, qry->zone_cut.name)) {
+			qry->zone_cut.name = knot_dname_copy(signer, &req->pool);
+		} else if (!knot_dname_is_equal(signer, qry->zone_cut.name)) {
+			/* Key signer is above the current cut, so we can't validate it. This happens when
+			   a server is authoritative for both grandparent, parent and child zone.
+			   Ascend to parent cut, and refetch authority for signer. */
+			if (qry->zone_cut.parent) {
+				memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
+			} else {
+				qry->flags |= QUERY_AWAIT_CUT;
+			}
+			qry->zone_cut.name = knot_dname_copy(signer, &req->pool);
+		} /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */
+		knot_wire_set_tc(pkt->wire);
+		result = KNOT_STATE_NOOP;
+	}
+
 	/* CONSUME => Unhelpful referral.
-	 * DONE    => Zone cut updated. */
+	 * DONE    => Zone cut updated.
+	 * NOOP    => Ignore this answer. */
 	return result;
 }
 
@@ -545,6 +585,9 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
 	case KNOT_STATE_DONE: /* Referral */
 		DEBUG_MSG("<= referral response, follow\n");
 		break;
+	case KNOT_STATE_NOOP: /* Deferred, bail out. */
+		state = KNOT_STATE_CONSUME;
+		break;
 	default:
 		break;
 	}
diff --git a/lib/layer/validate.c b/lib/layer/validate.c
index 0d700bcd6e5ccabab0861d33aa98cc1cded074cf..cedda57b805bf74ca77560ec43f588cf4d00fe67 100644
--- a/lib/layer/validate.c
+++ b/lib/layer/validate.c
@@ -162,9 +162,6 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 			updated_key = true;
 		}
 	}
-	if (!qry->zone_cut.key) {
-		return kr_error(EBADMSG);
-	}
 
 	/* Check if there's a key for current TA. */
 	if (updated_key && !(qry->flags & QUERY_CACHED)) {
@@ -179,43 +176,6 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 	return kr_ok();
 }
 
-static const knot_dname_t *section_first_signer_name(knot_pkt_t *pkt, knot_section_t section_id)
-{
-	const knot_dname_t *sname = NULL;
-	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
-	if (!sec) {
-		return sname;
-	}
-
-	for (unsigned i = 0; i < sec->count; ++i) {
-		const knot_rrset_t *rr = knot_pkt_rr(sec, i);
-		if (rr->type != KNOT_RRTYPE_RRSIG) {
-			continue;
-		}
-
-		sname = knot_rrsig_signer_name(&rr->rrs, 0);
-		break;
-	}
-
-	return sname;
-}
-
-static const knot_dname_t *first_rrsig_signer_name(knot_pkt_t *answer)
-{
-	const knot_dname_t *ans_sname = section_first_signer_name(answer, KNOT_ANSWER);
-	const knot_dname_t *auth_sname = section_first_signer_name(answer, KNOT_AUTHORITY);
-
-	if (!ans_sname) {
-		return auth_sname;
-	} else if (!auth_sname) {
-		return ans_sname;
-	} else if (knot_dname_is_equal(ans_sname, auth_sname)) {
-		return ans_sname;
-	} else {
-		return NULL;
-	}
-}
-
 static knot_rrset_t *update_ds(struct kr_zonecut *cut, const knot_pktsection_t *sec)
 {
 	/* Aggregate DS records (if using multiple keys) */
@@ -328,44 +288,12 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 	}
 	/* Answer for RRSIG may not set DO=1, but all records MUST still validate. */
 	bool use_signatures = (knot_pkt_qtype(pkt) != KNOT_RRTYPE_RRSIG);
-	/* @todo do not cache RRSIG answers until RFC2181 credibility is implemented */
-	if (!use_signatures) {
-		knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); /* Prevent caching */
-	}
 	if (!(qry->flags & QUERY_CACHED) && !knot_pkt_has_dnssec(pkt) && !use_signatures) {
 		DEBUG_MSG(qry, "<= got insecure response\n");
 		qry->flags |= QUERY_DNSSEC_BOGUS;
 		return KNOT_STATE_FAIL;
 	}
 
-	/* Check if we descended to a NS, which is authoritative for both parent-child, this means that
-	 * the signatures are made using a different key than we have.
-	 * Although we can't "pause" the response procesing and fetch the keys, we can
-	 * say "do not cache this answer, and try again". This way, the resolver will realise
-	 * that the keys are missing and will schedule a subrequest before retrying.
-	 */
-	const knot_dname_t *key_own = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL;
-	const knot_dname_t *sig_name = first_rrsig_signer_name(pkt);
-	if (use_signatures && sig_name && key_own && !knot_dname_is_equal(key_own, sig_name)) {
-		DEBUG_MSG(qry, ">< cut changed, needs revalidation\n");
-		if (knot_dname_is_sub(sig_name, qry->zone_cut.name)) {
-			qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
-		} else if (!knot_dname_is_equal(sig_name, qry->zone_cut.name)) {
-			/* Key signer is above the current cut, so we can't validate it. This happens when
-			   a server is authoritative for both grandparent, parent and child zone.
-			   Ascend to parent cut, and refetch authority for signer. */
-			if (qry->zone_cut.parent) {
-				memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
-			} else {
-				qry->flags |= QUERY_AWAIT_CUT;
-			}
-			qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
-		} /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */
-		knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); /* Prevent caching */
-		qry->flags &= ~QUERY_RESOLVED;
-		return KNOT_STATE_CONSUME;
-	}
-
 	/* Check if this is a DNSKEY answer, check trust chain and store. */
 	uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
 	uint16_t qtype = knot_pkt_qtype(pkt);