diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index da1b5a46f65110a32b7b325282b8d5fe390b0ac2..b81d01c61d07bab6d6b4dc9cf13e6c5d60fa4a70 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -147,10 +147,34 @@ static void follow_cname_chain(const knot_dname_t **cname, const knot_rrset_t *r
 	}
 }
 
+/** @internal Filter ANY or loopback addresses. */
+static bool is_valid_addr(const uint8_t *addr, size_t len)
+{
+	if (len == sizeof(struct in_addr)) {
+		/* Filter ANY and 127.0.0.0/8 */
+		uint32_t ip_host = ntohl(*(const uint32_t *)(addr));
+		if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
+			return false;
+		}
+	} else if (len == sizeof(struct in6_addr)) {
+		struct in6_addr ip6_mask;
+		memset(&ip6_mask, 0, sizeof(ip6_mask));
+		/* All except last byte are zeroed, last byte defines ANY/::1 */
+		if (memcmp(addr, ip6_mask.s6_addr, sizeof(ip6_mask.s6_addr) - 1) == 0) {
+			return (addr[len - 1] > 1);
+		}
+	}
+	return true;
+}
+
 static int update_nsaddr(const knot_rrset_t *rr, struct kr_query *query)
 {
 	if (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) {
 		const knot_rdata_t *rdata = rr->rrs.data;
+		if (!(query->flags & QUERY_ALLOW_LOCAL) &&
+			!is_valid_addr(knot_rdata_data(rdata), knot_rdata_rdlen(rdata))) {
+			return KNOT_STATE_CONSUME; /* Ignore invalid addresses */
+		}
 		int ret = kr_zonecut_add(&query->zone_cut, rr->owner, rdata);
 		if (ret != 0) {
 			return KNOT_STATE_FAIL;
diff --git a/lib/rplan.h b/lib/rplan.h
index ccf300a1baf625eb03fb533a100ea00f09b325ef..e936860bec9b78914660da8a96851fc833bfe3cf 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -36,7 +36,8 @@
 	X(AWAIT_CUT  , 1 << 6) /**< Query is waiting for zone cut lookup */ \
 	X(SAFEMODE   , 1 << 7) /**< Don't use fancy stuff (EDNS...) */ \
 	X(CACHED     , 1 << 8) /**< Query response is cached. */ \
-	X(EXPIRING   , 1 << 9) /**< Query response is cached, but expiring. */
+	X(EXPIRING   , 1 << 9) /**< Query response is cached, but expiring. */ \
+	X(ALLOW_LOCAL, 1 << 10) /**< Allow queries to local or private address ranges. */
 
 /** Query flags */
 enum kr_query_flag {
diff --git a/lib/zonecut.c b/lib/zonecut.c
index 8ca63ebffb2d84af1f2d4f5d2abe815a4b1ee933..536f4102116aa56eec273513e25e07d119797d9c 100644
--- a/lib/zonecut.c
+++ b/lib/zonecut.c
@@ -143,29 +143,7 @@ int kr_zonecut_copy(struct kr_zonecut *dst, const struct kr_zonecut *src)
 	return map_walk((map_t *)&src->nsset, copy_addr_set, dst);
 }
 
-/** @internal Filter ANY or loopback addresses. */
-static bool is_valid_addr(uint8_t *addr, size_t len)
-{
-	if (len == sizeof(struct in_addr)) {
-		/* Filter ANY and 127.0.0.0/8 */
 
-		/* temporary workaround for local testing 
-		uint32_t ip_host = ntohl(*(uint32_t *)(addr));
-		if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
-			return false;
-		}
-		*/
-
-	} else if (len == sizeof(struct in6_addr)) {
-		struct in6_addr ip6_mask;
-		memset(&ip6_mask, 0, sizeof(ip6_mask));
-		/* All except last byte are zeroed, last byte defines ANY/::1 */
-		if (memcmp(addr, ip6_mask.s6_addr, sizeof(ip6_mask.s6_addr) - 1) == 0) {
-			return (addr[len - 1] > 1);
-		}
-	}
-	return true;
-}
 
 int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
 {
@@ -186,13 +164,9 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 	if (rdata == NULL) {
 		return kr_ok();
 	}
-	/* Check for invalid */
+	/* Check for duplicates */
 	uint16_t rdlen = knot_rdata_rdlen(rdata);
 	uint8_t *raw_addr = knot_rdata_data(rdata);
-	if (!is_valid_addr(raw_addr, rdlen)) {
-		return kr_error(EILSEQ);
-	}
-	/* Check for duplicates */
 	if (pack_obj_find(pack, raw_addr, rdlen)) {
 		return kr_ok();
 	}
@@ -201,7 +175,7 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 	if (ret != 0) {
 		return kr_error(ENOMEM);
 	}
-	return pack_obj_push(pack, knot_rdata_data(rdata), rdlen);
+	return pack_obj_push(pack, raw_addr, rdlen);
 }
 
 int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 82b90fc00eaad1c63cd42842939a0e808dfd472b..b1b8adaa01394b0bd8c7a21354fa6b49a1d95a1d 100755
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -205,6 +205,7 @@ def play_object(path):
     os.write(fd, "modules = {'hints'}\n")
     os.write(fd, "hints.root({['k.root-servers.net'] = '%s'})\n" % selfaddr)
     os.write(fd, "option('NO_MINIMIZE', true)\n")
+    os.write(fd, "option('ALLOW_LOCAL', true)\n") # Permit queries to localhost
     for k,v in config:
         # Enable selectively for some tests
         if k == 'query-minimization' and str2bool(v):
diff --git a/tests/test_zonecut.c b/tests/test_zonecut.c
index 772de3e99d8f07ddef1c188d706c5e311783a474..7158788429a6cc40a127286a23a499dda08153c8 100644
--- a/tests/test_zonecut.c
+++ b/tests/test_zonecut.c
@@ -35,40 +35,6 @@ static void test_zonecut_params(void **state)
 	assert_int_not_equal(kr_zonecut_find_cached(NULL, NULL, NULL, NULL, 0), 0);
 }
 
-#define TEST_IP(cut, ip, expect) { \
-	knot_rdata_t rdata[knot_rdata_array_size(sizeof(ip))]; \
-	knot_rdata_init(rdata, sizeof(ip), (uint8_t *)&ip, 0); \
-	assert_int_equal(kr_zonecut_add(&(cut), (const uint8_t *)"\x02cz", rdata), (expect)); \
-} while (0)
-
-static void test_zonecut_filter(void **state)
-{
-	struct kr_zonecut cut;
-	kr_zonecut_init(&cut, (const uint8_t *)"", NULL);
-
-	/* IPv4 */
-	uint32_t ip4 = 0; /* 0.0.0.0 */
-	TEST_IP(cut, ip4, kr_error(EILSEQ));
-	ip4 = htonl(0x7f000002); /* 127.0.0.2 */
-	TEST_IP(cut, ip4, kr_error(EILSEQ));
-	ip4 = htonl(0x7fffffff); /* 127.255.255.255 */
-	TEST_IP(cut, ip4, kr_error(EILSEQ));
-	ip4 = htonl(0xff000001); /* 255.0.0.1 */
-	TEST_IP(cut, ip4, 0);
-	/* IPv6 */
-	struct in6_addr ip6;
-	memset(&ip6, 0, sizeof(ip6)); /* :: */
-	TEST_IP(cut, ip6.s6_addr, kr_error(EILSEQ));
-	ip6.s6_addr[15] = 0x01; /* ::1 */
-	TEST_IP(cut, ip6.s6_addr, kr_error(EILSEQ));
-	ip6.s6_addr[15] = 0x02; /* ::2 */
-	TEST_IP(cut, ip6.s6_addr, 0);
-
-	kr_zonecut_deinit(&cut);
-}
-
-#undef TEST_IP
-
 static void test_zonecut_copy(void **state)
 {
 	const knot_dname_t *root = (const uint8_t *)"";
@@ -92,7 +58,6 @@ int main(void)
 {
 	const UnitTest tests[] = {
 	        unit_test(test_zonecut_params),
-	        unit_test(test_zonecut_filter),
 	        unit_test(test_zonecut_copy)
 	};