diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua
index 3d4e7ced23be06e66765f1410c165946c6029455..4dc2b402bc2d3de0e6fcfda44ad051182f339021 100644
--- a/daemon/lua/kres.lua
+++ b/daemon/lua/kres.lua
@@ -631,7 +631,7 @@ ffi.metatype( knot_pkt_t, {
 		-- Question
 		qname = function(pkt)
 			assert(ffi.istype(knot_pkt_t, pkt))
-			-- inlined knot_pkt_qname(), basically
+			-- inlined knot_pkt_qname(), basically but not lower-cased
 			if pkt == nil or pkt.qname_size == 0 then return nil end
 			return ffi.string(pkt.wire + 12, pkt.qname_size)
 		end,
diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index ab5370849828c9121a84b9f997dd3ebd3c160a0c..b1c3df40f01625f4b94d3b329622d573c6ee91e9 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -77,7 +77,7 @@ static bool is_paired_to_query(const knot_pkt_t *answer, struct kr_query *query)
 	       qtype          == knot_pkt_qtype(answer) &&
 	       /* qry->secret had been xor-applied to answer already,
 		* so this also checks for correctness of case randomization */
-	       knot_dname_is_equal(qname, knot_pkt_qname(answer));
+	       knot_dname_is_equal(qname, kr_pkt_qname_raw(answer));
 }
 
 /** Relaxed rule for AA, either AA=1 or SOA matching zone cut is required. */
diff --git a/lib/resolve.c b/lib/resolve.c
index a1bed095dc15ea567d78fdaacceeb4d921e9b2b0..877b078a3270f65a044285676cc44eaa188f2058 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -813,7 +813,7 @@ int kr_resolve_consume(struct kr_request *request, struct kr_transport **transpo
 		return KR_STATE_PRODUCE;
 
 	/* Packet cleared, derandomize QNAME. */
-	knot_dname_t *qname_raw = knot_pkt_qname(packet);
+	knot_dname_t *qname_raw = kr_pkt_qname_raw(packet);
 	if (qname_raw && qry->secret != 0) {
 		randomized_qname_case(qname_raw, qry->secret);
 	}
@@ -1425,7 +1425,7 @@ int kr_resolve_produce(struct kr_request *request, struct kr_transport **transpo
 
 	/* Randomize query case (if not in not turned off) */
 	qry->secret = qry->flags.NO_0X20 ? 0 : kr_rand_bytes(sizeof(qry->secret));
-	knot_dname_t *qname_raw = knot_pkt_qname(packet);
+	knot_dname_t *qname_raw = kr_pkt_qname_raw(packet);
 	randomized_qname_case(qname_raw, qry->secret);
 
 	/*
@@ -1529,9 +1529,9 @@ int kr_resolve_checkout(struct kr_request *request, const struct sockaddr *src,
 	}
 
 	/* Randomize query case (if secret changed) */
-	knot_dname_t *qname = knot_pkt_qname(packet);
+	knot_dname_t *qname_raw = kr_pkt_qname_raw(packet);
 	if (qry->secret != old_minimization_secret) {
-		randomized_qname_case(qname, qry->secret);
+		randomized_qname_case(qname_raw, qry->secret);
 	}
 
 	/* Write down OPT unless in safemode */
diff --git a/lib/utils.h b/lib/utils.h
index cede7bef3d4c0a8c36733a8b8f2e928f0df9fc23..c3661d361d922d4523de5954cf1caff2a51a9883 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -248,6 +248,20 @@ int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
 KR_EXPORT
 void kr_pkt_make_auth_header(knot_pkt_t *pkt);
 
+/** Get pointer to the in-header QNAME.
+ *
+ * That's normally not lower-cased.  However, when receiving packets from upstream
+ * we xor-apply the secret during packet-parsing, so it would get lower-cased
+ * after that point if the case was right.
+ */
+static inline knot_dname_t * kr_pkt_qname_raw(const knot_pkt_t *pkt)
+{
+	if (pkt == NULL || pkt->qname_size == 0) {
+		return NULL;
+	}
+	return pkt->wire + KNOT_WIRE_HEADER_SIZE;
+}
+
 /** Simple storage for IPx address and their ports or AF_UNSPEC. */
 union kr_sockaddr {
 	struct sockaddr ip;