diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index edc666ebd96aa125e7db33cfc236f9d14cfcb38f..dfb7c8762f4bbdf714d30eb1f356edf66d99de14 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -1026,6 +1026,21 @@ static void bound_ttls(ranked_rr_array_t *array, uint32_t qry_uid,
 	}
 }
 
+static void ede_passthru(const knot_pkt_t *pkt, struct kr_request *req)
+{
+	const uint8_t *ede_raw = pkt->edns_opts ?
+			pkt->edns_opts->ptr[KNOT_EDNS_OPTION_EDE] : NULL;
+	if (!ede_raw) return;
+	kr_require(ede_raw[0] * 256 + ede_raw[1] == KNOT_EDNS_OPTION_EDE);
+	uint16_t ede_len = ede_raw[2] * 256 + ede_raw[3];
+	if (ede_len < 2) return;
+	uint16_t ede_code = ede_raw[4] * 256 + ede_raw[5];
+	if (ede_code >= KNOT_EDNS_EDE_INDETERMINATE // long range of DNSSEC codes
+			&& ede_code <= KNOT_EDNS_EDE_NSEC_MISS) {
+		kr_request_set_extended_error(req, ede_code, "V5T7: forwarded EDE code");
+	}
+}
+
 /** Resolve input query or continue resolution with followups.
  *
  *  This roughly corresponds to RFC1034, 5.3.3 4a-d.
@@ -1129,6 +1144,10 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
 		}
 		ret = KR_STATE_FAIL;
 		selection_error = KR_SELECTION_SERVFAIL;
+		if (query->flags.FORWARD) {
+			/* additionally pass some of the EDE codes through */
+			ede_passthru(pkt, req);
+		}
 		break;
 	case KNOT_RCODE_FORMERR:
 		ret = KR_STATE_FAIL;
diff --git a/lib/selection.c b/lib/selection.c
index 5aa2992c93616911959f623f6e0fce7cd9e66e6c..c25782e62aafa9e06e06ddafe2efafdba74a7379 100644
--- a/lib/selection.c
+++ b/lib/selection.c
@@ -686,6 +686,13 @@ void error(struct kr_query *qry, struct address_state *addr_state,
 		break;
 	case KR_SELECTION_REFUSED:
 	case KR_SELECTION_SERVFAIL:
+		if (qry->flags.FORWARD || qry->flags.STUB) {
+			/* The NS might not be broken, but this state is just for this query
+			 * and it doesn't make sense to retry on the same NS immediately. */
+			addr_state->broken = true;
+			break;
+		}
+		/* For authoritative servers we try some fallback workarounds. */
 		if (qry->flags.NO_MINIMIZE && qry->flags.NO_0X20 && qry->flags.TCP) {
 			addr_state->broken = true;
 		} else if (qry->flags.NO_MINIMIZE) {
diff --git a/lib/selection_forward.c b/lib/selection_forward.c
index 54f9a12262562c9f8f1efc064a665e4d4ab439e3..3fcfc75454a52bc14ec2c8ce71ef56ce936eb737 100644
--- a/lib/selection_forward.c
+++ b/lib/selection_forward.c
@@ -66,7 +66,7 @@ void forward_choose_transport(struct kr_query *qry,
 
 		update_address_state(addr_state, address, addr_len, qry);
 
-		if (addr_state->generation == -1) {
+		if (addr_state->generation == -1 || addr_state->broken) {
 			continue;
 		}
 		addr_state->choice_array_index = i;