diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c
index 28db3c9804bf33c172dc2536ac1759b0ad9347a4..d8ad4498925eba80a511aeb5df51a0e8d9a44285 100644
--- a/src/utils/common/netio.c
+++ b/src/utils/common/netio.c
@@ -547,6 +547,17 @@ int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len)
 		return KNOT_EINVAL;
 	}
 
+#ifdef LIBNGTCP2
+	// Send data over QUIC.
+	if (net->quic.params.enable) {
+		int ret = quic_send_dns_query((quic_ctx_t *)&net->quic,
+		                              net->sockfd, net->srv, buf, buf_len);
+		if (ret != KNOT_EOK) {
+			WARN("can't send query to %s\n", net->remote_str);
+			return KNOT_NET_ESEND;
+		}
+	} else
+#endif
 	// Send data over UDP.
 	if (net->socktype == SOCK_DGRAM) {
 		if (sendto(net->sockfd, buf, buf_len, 0, net->srv->ai_addr,
diff --git a/src/utils/common/quic.c b/src/utils/common/quic.c
index ffa52c869e200b2c8cf51be55c31870a327707dc..a12e494ef61ddaa4cd27f05aca8ccb859f417336 100644
--- a/src/utils/common/quic.c
+++ b/src/utils/common/quic.c
@@ -740,4 +740,68 @@ int quic_ctx_connect(quic_ctx_t *ctx, int sockfd, struct addrinfo *dst_addr)
 	return KNOT_EOK;
 }
 
+int quic_send_dns_query(quic_ctx_t *ctx, int sockfd, struct addrinfo *srv,
+        const uint8_t *buf, const size_t buf_len)
+{
+	if (ctx == NULL || buf == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	ngtcp2_transport_params params;
+	uint16_t query_length = htons(buf_len);
+	ngtcp2_vec datav[] = {
+		{
+			.base = (uint8_t *)&query_length,
+			.len = sizeof(uint16_t)
+		},{
+			.base = (uint8_t *)buf,
+			.len = buf_len
+		}
+	};
+	size_t datavlen = sizeof(datav)/sizeof(*datav);
+	ngtcp2_vec *pdatav = datav;
+
+	struct pollfd pfd = {
+		.fd = sockfd,
+		.events = POLLIN,
+		.revents = 0,
+	};
+
+	ctx->stream.out_ack += buf_len + sizeof(uint16_t);
+	while (ctx->stream.out_ack) {
+		if (quic_timeout(ctx->idle_ts, ctx->tls->wait)) {
+			WARN("QUIC, failed to send\n");
+			set_application_error(ctx, DOQ_REQUEST_CANCELLED,
+			                (uint8_t *)"Connection timeout",
+			                sizeof("Connection timeout") - 1);
+			return KNOT_NET_ETIMEOUT;
+		}
+		int ret = quic_send_data(ctx, sockfd, srv->ai_family, pdatav,
+		                         datavlen);
+		if (ret != KNOT_EOK) {
+			WARN("QUIC, failed to send\n");
+			return ret;
+		}
+		pdatav = NULL;
+		datavlen = 0;
+
+		ngtcp2_conn_get_remote_transport_params(ctx->conn, &params);
+		int timeout = quic_ceil_duration_to_ms(params.max_ack_delay);
+		ret = poll(&pfd, 1, timeout);
+		if (ret < 0) {
+			WARN("QUIC, failed to send\n");
+			return knot_map_errno();
+		} else if (ret == 0) {
+			continue;
+		}
+		ret = quic_recv(ctx, sockfd);
+		if (ret != KNOT_EOK) {
+			WARN("QUIC, failed to send\n");
+			return ret;
+		}
+	}
+
+	return KNOT_EOK;
+}
+
 #endif
diff --git a/src/utils/common/quic.h b/src/utils/common/quic.h
index 1b4ad1e448ad4b41ff2f70ad6d2718a441680abc..87ba18222851c3e0980527e573fc44972d1d7a0d 100644
--- a/src/utils/common/quic.h
+++ b/src/utils/common/quic.h
@@ -108,4 +108,7 @@ int quic_ctx_init(quic_ctx_t *ctx, tls_ctx_t *tls_ctx, const quic_params_t *para
 
 int quic_ctx_connect(quic_ctx_t *ctx, int sockfd, struct addrinfo *dst_addr);
 
+int quic_send_dns_query(quic_ctx_t *ctx, int sockfd, struct addrinfo *srv,
+        const uint8_t *buf, const size_t buf_len);
+
 #endif //LIBNGTCP2
diff --git a/src/utils/kdig/kdig_exec.c b/src/utils/kdig/kdig_exec.c
index eb4031cb9c771902da68028b6c1b41762736c430..13556a3212293b359357007901ab63c5ad570d6d 100644
--- a/src/utils/kdig/kdig_exec.c
+++ b/src/utils/kdig/kdig_exec.c
@@ -392,8 +392,8 @@ static knot_pkt_t *create_query_packet(const query_t *query)
 
 	// Set ID = 0 for packet send over HTTPS
 	// Due HTTP cache it is convenient to set the query ID to 0 - GET messages has same header then
-#ifdef LIBNGHTTP2
-	if (query->https.enable) {
+#if defined(LIBNGHTTP2) || defined(LIBNGTCP2)
+	if (query->https.enable || query->quic.enable) {
 		knot_wire_set_id(packet->wire, 0);
 	}
 #endif