diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index eac4921025b5b6f31a7d7f966db9ae555814aad6..e653443ea17501020aa3720f729896048fe9cbe5 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -154,8 +154,16 @@ static int resolve_auth(knot_pkt_t *pkt, struct kr_layer_param *param)
 		return KNOT_NS_PROC_FAIL;
 	}
 
+	/* Authoritative response for min QNAME => disable and retry */
+	bool is_minimized = (knot_pkt_qtype(pkt) != cur->stype &&
+	                     !knot_dname_is_equal(knot_pkt_qname(pkt), cur->sname));
+	if (is_minimized && knot_wire_get_rcode(pkt->wire) == KNOT_RCODE_NOERROR) {
+		cur->flags |= QUERY_NO_MINIMIZE;
+		return KNOT_NS_PROC_DONE;
+	}
+
 	/* Is relevant for original query? */
-	bool update_orig_answer = (cur == kr_rplan_last(param->rplan));
+	bool update_orig_answer = (cur == kr_rplan_last(param->rplan) && !is_minimized);
 
 	const knot_dname_t *cname = cur->sname;
 	const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
@@ -200,6 +208,21 @@ static int resolve_error(knot_pkt_t *pkt, struct kr_layer_param *param, int errc
 	return KNOT_NS_PROC_FAIL;
 }
 
+/*! \brief Return minimized QNAME for current zone cut. */
+static const knot_dname_t *minimized_qname(struct kr_query *query, struct kr_zonecut *cut)
+{
+	/* Minimize name to contain current zone cut + 1 label. */
+	const knot_dname_t *qname = query->sname;
+	int cut_labels = knot_dname_labels(cut->name, NULL);
+	int qname_labels = knot_dname_labels(qname, NULL);
+	while(qname_labels > cut_labels + 1) {
+		qname = knot_wire_next_label(qname, NULL);
+		qname_labels -= 1;
+	}
+
+	return qname;
+}
+
 /*! \brief Answer is paired to query. */
 static bool is_answer_to_query(const knot_pkt_t *answer, struct kr_rplan *rplan)
 {
@@ -208,10 +231,19 @@ static bool is_answer_to_query(const knot_pkt_t *answer, struct kr_rplan *rplan)
 		return -1;
 	}
 
+	uint16_t expect_qtype = expect->stype;
+	const knot_dname_t *expect_qname = expect->sname;
+	if (!expect->flags & QUERY_NO_MINIMIZE) {
+		expect_qname = minimized_qname(expect, &rplan->zone_cut);
+		if (expect_qname != expect->sname) {
+			expect_qtype = KNOT_RRTYPE_NS;
+		}
+	}
+
 	return expect->id      == knot_wire_get_id(answer->wire) &&
 	       expect->sclass  == knot_pkt_qclass(answer) &&
-	       expect->stype   == knot_pkt_qtype(answer) &&
-	       knot_dname_is_equal(expect->sname, knot_pkt_qname(answer));
+	       expect_qtype    == knot_pkt_qtype(answer) &&
+	       knot_dname_is_equal(expect_qname, knot_pkt_qname(answer));
 }
 
 /* State-less single resolution iteration step, not needed. */
@@ -234,9 +266,20 @@ static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
 		return ctx->state;
 	}
 
+	/* Minimize QNAME (if possible). */
+	struct kr_zonecut *zone_cut = &param->rplan->zone_cut;
+	const knot_dname_t *qname = cur->sname;
+	uint16_t qtype = cur->stype;
+	if (!(cur->flags & QUERY_NO_MINIMIZE)) {
+		qname = minimized_qname(cur, zone_cut);
+		if (qname != cur->sname) {
+			qtype = KNOT_RRTYPE_NS;
+		}
+	}
+
 	/* Form a query for the authoritative. */
 	knot_pkt_clear(pkt);
-	int ret = knot_pkt_put_question(pkt, cur->sname, cur->sclass, cur->stype);
+	int ret = knot_pkt_put_question(pkt, qname, cur->sclass, qtype);
 	if (ret != KNOT_EOK) {
 		return KNOT_NS_PROC_FAIL;
 	}
@@ -245,9 +288,12 @@ static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
 	knot_wire_set_id(pkt->wire, cur->id);
 
 #ifndef NDEBUG
-	char name_str[KNOT_DNAME_MAXLEN];
-	knot_dname_to_str(name_str, param->rplan->zone_cut.ns, sizeof(name_str));
-	DEBUG_MSG("sending query to %s\n", name_str);
+	char name_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[KNOT_DNAME_MAXLEN], type_str[16];
+	knot_dname_to_str(ns_str, zone_cut->ns, sizeof(ns_str));
+	knot_dname_to_str(zonecut_str, zone_cut->name, sizeof(zonecut_str));
+	knot_dname_to_str(name_str, qname, sizeof(name_str));
+	knot_rrtype_to_string(qtype, type_str, sizeof(type_str));
+	DEBUG_MSG("query '%s %s' zone cut '%s' nameserver '%s'\n", name_str, type_str, zonecut_str, ns_str);
 #endif
 
 	/* Query built, expect answer. */
diff --git a/lib/resolve.c b/lib/resolve.c
index 4c299ecee9c4c6c3a104fb997be6863fe5fc1587..748f110584783b69e9dd6e53b728cc0895dca6a7 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -66,13 +66,6 @@ static int iterate(struct knot_requestor *requestor, struct kr_layer_param *para
 		return invalidate_ns(rplan, cur);
 	}
 
-	char name_str[KNOT_DNAME_MAXLEN], zonecut_str[KNOT_DNAME_MAXLEN], ns_str[KNOT_DNAME_MAXLEN], type_str[16];
-	knot_dname_to_str(ns_str, rplan->zone_cut.ns, sizeof(ns_str));
-	knot_dname_to_str(zonecut_str, rplan->zone_cut.name, sizeof(zonecut_str));
-	knot_dname_to_str(name_str, cur->sname, sizeof(name_str));
-	knot_rrtype_to_string(cur->stype, type_str, sizeof(type_str));
-	DEBUG_MSG("resolve '%s %s' zone cut '%s' nameserver '%s'\n", name_str, type_str, zonecut_str, ns_str);
-
 	/* Prepare query resolution. */
 	struct sockaddr *ns_addr = (struct sockaddr *)&rplan->zone_cut.addr;
 	knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm);
diff --git a/lib/rplan.h b/lib/rplan.h
index cbada68d7d3d969bce80150533c61553ca1aabb8..f73231e6e15afba10ee481cc0f68e787300324f7 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -24,6 +24,11 @@
 #include "lib/context.h"
 #include "lib/zonecut.h"
 
+/* Query flags */
+enum {
+	QUERY_NO_MINIMIZE = 1 << 0 /*!< Don't minimize QNAME. */
+};
+
 /*!
  * \brief Single query representation.
  */
diff --git a/lib/zonecut.c b/lib/zonecut.c
index d153968678e681ae1b194b101d81ad598d6f17bf..fb7620255438dae8818dcd11dd2d2d83deb6b01c 100644
--- a/lib/zonecut.c
+++ b/lib/zonecut.c
@@ -120,7 +120,6 @@ int kr_find_zone_cut(struct kr_zonecut *cut, const knot_dname_t *name, namedb_tx
 		return KNOT_EINVAL;
 	}
 
-
 	/* No cache, start with SBELT. */
 	if (txn == NULL) {
 		return set_sbelt_zone_cut(cut);