diff --git a/lib/zonecut.c b/lib/zonecut.c
index 4ec4cdc314298c4784f8509a643b2dbdbfdbc1dc..241485238348aefb926c0561929d7130a6878c35 100644
--- a/lib/zonecut.c
+++ b/lib/zonecut.c
@@ -267,6 +267,21 @@ int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 	return ret;
 }
 
+int kr_zonecut_del_all(struct kr_zonecut *cut, const knot_dname_t *ns)
+{
+	if (!cut || !ns) {
+		return kr_error(EINVAL);
+	}
+
+	/* Find the address list; then free and remove it. */
+	pack_t *pack = kr_zonecut_find(cut, ns);
+	if (pack == NULL) {
+		return kr_error(ENOENT);
+	}
+	free_addr_set((const char *)ns, pack, cut->pool);
+	return map_del(&cut->nsset, (const char *)ns);
+}
+
 pack_t *kr_zonecut_find(struct kr_zonecut *cut, const knot_dname_t *ns)
 {
 	if (!cut || !ns) {
diff --git a/lib/zonecut.h b/lib/zonecut.h
index 2395fd2cf4b99a6ee63e711d5c57949a0c08e737..90d7747ae80b093c19beb871b30ce834e3d926eb 100644
--- a/lib/zonecut.h
+++ b/lib/zonecut.h
@@ -32,7 +32,7 @@ struct kr_zonecut {
 	knot_rrset_t* key;  /**< Zone cut DNSKEY. */
 	knot_rrset_t* trust_anchor; /**< Current trust anchor. */
 	struct kr_zonecut *parent; /**< Parent zone cut. */
-    map_t nsset;        /**< Map of nameserver => address_set. */
+	map_t nsset;        /**< Map of nameserver => address_set. */
 	knot_mm_t *pool;     /**< Memory pool. */
 };
 
@@ -104,6 +104,15 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
 KR_EXPORT
 int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata);
 
+/**
+ * Delete all addresses associated with the given name.
+ * @param  cut
+ * @param  ns    name server name
+ * @return       0 or error code
+ */
+KR_EXPORT
+int kr_zonecut_del_all(struct kr_zonecut *cut, const knot_dname_t *ns);
+
 /**
  * Find nameserver address list in the zone cut.
  *
diff --git a/modules/hints/README.rst b/modules/hints/README.rst
index 9a0e7123bd06eab5053a3acb2bd77121ba37f33c..71f2e8eeb7da3b4a50e38487ac6b788589743993 100644
--- a/modules/hints/README.rst
+++ b/modules/hints/README.rst
@@ -30,7 +30,7 @@ Properties
 
   :param string path:  path to hosts file, default: ``"/etc/hosts"``
   :return: ``{ result: bool }``
-  
+
   Load specified hosts file.
 
 .. function:: hints.get(hostname)
@@ -39,24 +39,32 @@ Properties
   :return: ``{ result: [address1, address2, ...] }``
 
   Return list of address record matching given name.
+  If no hostname is specified, all hints are returned in the table format used by ``hints.root()``.
 
 .. function:: hints.set(pair)
 
   :param string pair:  ``hostname address`` i.e. ``"localhost 127.0.0.1"``
   :return: ``{ result: bool }``
 
-  Set hostname - address pair hint.
+  Add a hostname - address pair hint.
+
+.. function:: hints.del(pair)
+
+  :param string pair:  ``hostname address`` i.e. ``"localhost 127.0.0.1"``, or just ``hostname``
+  :return: ``{ result: bool }``
+
+  Remove a hostname - address pair hint.  If address is omitted, all addresses for the given name are deleted.
 
 .. function:: hints.root()
 
-  :return: ``{ ['a.root-servers.net'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
+  :return: ``{ ['a.root-servers.net.'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
 
   .. tip:: If no parameters are passed, returns current root hints set.
 
 .. function:: hints.root(root_hints)
 
   :param table root_hints: new set of root hints i.e. ``{['name'] = 'addr', ...}``
-  :return: ``{ ['a.root-servers.net'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
+  :return: ``{ ['a.root-servers.net.'] = { '1.2.3.4', '5.6.7.8', ...}, ... }``
 
   Replace current root hints and return the current table of root hints.
 
@@ -76,4 +84,4 @@ Properties
     }
 
   .. tip:: A good rule of thumb is to select only a few fastest root hints. The server learns RTT and NS quality over time, and thus tries all servers available. You can help it by preselecting the candidates.
-  
\ No newline at end of file
+
diff --git a/modules/hints/hints.c b/modules/hints/hints.c
index f7348680dffddfd77f380c476c9c6f42826c3afa..cae6b1532936ea507f27cbed90bc491390572d7c 100644
--- a/modules/hints/hints.c
+++ b/modules/hints/hints.c
@@ -216,6 +216,22 @@ static int parse_addr_str(struct sockaddr_storage *sa, const char *addr)
 	return 0;
 }
 
+/** @warning _NOT_ thread-safe; returns a pointer to static data! */
+static const knot_rdata_t * addr2rdata(const char *addr) {
+	/* Parse address string */
+	struct sockaddr_storage ss;
+	if (parse_addr_str(&ss, addr) != 0) {
+		return NULL;
+	}
+
+	/* Build RDATA */
+	static knot_rdata_t rdata_arr[RDATA_ARR_MAX];
+	size_t addr_len = kr_inaddr_len((struct sockaddr *)&ss);
+	const uint8_t *raw_addr = (const uint8_t *)kr_inaddr((struct sockaddr *)&ss);
+	knot_rdata_init(rdata_arr, addr_len, raw_addr, 0);
+	return rdata_arr;
+}
+
 static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr)
 {
 	/* Build key */
@@ -223,20 +239,34 @@ static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr
 	if (!knot_dname_from_str(key, name, sizeof(key))) {
 		return kr_error(EINVAL);
 	}
+	const knot_rdata_t *rdata = addr2rdata(addr);
+	if (!rdata) {
+		return kr_error(EINVAL);
+	}
 
-	/* Parse address string */
-	struct sockaddr_storage ss;
-	if (parse_addr_str(&ss, addr) != 0) {
+	return kr_zonecut_add(hints, key, rdata);
+}
+
+/** For a given name, remove either one address or all of them (if == NULL). */
+static int del_pair(struct kr_zonecut *hints, const char *name, const char *addr)
+{
+	/* Build key */
+	knot_dname_t key[KNOT_DNAME_MAXLEN];
+	if (!knot_dname_from_str(key, name, sizeof(key))) {
 		return kr_error(EINVAL);
 	}
 
-	/* Build RDATA */
-	size_t addr_len = kr_inaddr_len((struct sockaddr *)&ss);
-	const uint8_t *raw_addr = (const uint8_t *)kr_inaddr((struct sockaddr *)&ss);
-	/* @warning _NOT_ thread-safe */
-	static knot_rdata_t rdata_arr[RDATA_ARR_MAX];
-	knot_rdata_init(rdata_arr, addr_len, raw_addr, 0);
-	return kr_zonecut_add(hints, key, rdata_arr);
+        if (addr) {
+		/* Remove the pair. */
+		const knot_rdata_t *rdata = addr2rdata(addr);
+		if (!rdata) {
+			return kr_error(EINVAL);
+		}
+		return kr_zonecut_del(hints, key, rdata);
+	} else {
+		/* Remove the whole name. */
+		return kr_zonecut_del_all(hints, key);
+	}
 }
 
 static int load_map(struct kr_zonecut *hints, FILE *fp)
@@ -327,6 +357,25 @@ static char* hint_set(void *env, struct kr_module *module, const char *args)
 	return result;
 }
 
+static char* hint_del(void *env, struct kr_module *module, const char *args)
+{
+	struct kr_zonecut *hints = module->data;
+	auto_free char *args_copy = strdup(args);
+
+	int ret = -1;
+	char *addr = strchr(args_copy, ' ');
+	if (addr) {
+		*addr = '\0';
+		++addr;
+	}
+	ret = del_pair(hints, args_copy, addr);
+
+	char *result = NULL;
+	if (-1 == asprintf(&result, "{ \"result\": %s }", ret == 0 ? "true" : "false"))
+		result = NULL;
+	return result;
+}
+
 /** @internal Pack address list into JSON array. */
 static JsonNode *pack_addrs(pack_t *pack)
 {
@@ -345,8 +394,9 @@ static JsonNode *pack_addrs(pack_t *pack)
 	return root;
 }
 
+static char* pack_hints(struct kr_zonecut *hints);
 /**
- * Retrieve address hint for given name.
+ * Retrieve address hints, either for given name or for all names.
  *
  * Input:  name
  * Output: { address1, address2, ... }
@@ -354,6 +404,11 @@ static JsonNode *pack_addrs(pack_t *pack)
 static char* hint_get(void *env, struct kr_module *module, const char *args)
 {
 	struct kr_zonecut *hints = module->data;
+
+	if (!args) {
+		return pack_hints(hints);
+	}
+
 	knot_dname_t key[KNOT_DNAME_MAXLEN];
 	pack_t *pack = NULL;
 	if (knot_dname_from_str(key, args, sizeof(key))) {
@@ -386,6 +441,17 @@ static int pack_hint(const char *k, void *v, void *baton)
 	return kr_ok();
 }
 
+/** @internal Pack all hints into serialized JSON. */
+static char* pack_hints(struct kr_zonecut *hints) {
+	char *result = NULL;
+	JsonNode *root_node = json_mkobject();
+	if (map_walk(&hints->nsset, pack_hint, root_node) == 0) {
+		result = json_encode(root_node);
+	}
+	json_delete(root_node);
+	return result;
+}
+
 static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const char *name)
 {
 	JsonNode *node = NULL;
@@ -418,13 +484,7 @@ static char* hint_root(void *env, struct kr_module *module, const char *args)
 		json_delete(root_node);
 	}
 	/* Return current root hints */
-	char *result = NULL;
-	JsonNode *root_node = json_mkobject();
-	if (map_walk(&root_hints->nsset, pack_hint, root_node) == 0) {
-		result = json_encode(root_node);
-	}
-	json_delete(root_node);
-	return result;
+	return pack_hints(root_hints);
 }
 
 /*
@@ -471,6 +531,7 @@ struct kr_prop *hints_props(void)
 {
 	static struct kr_prop prop_list[] = {
 	    { &hint_set,    "set", "Set {name, address} hint.", },
+	    { &hint_del,    "del", "Delete one {name, address} hint or all addresses for the name.", },
 	    { &hint_get,    "get", "Retrieve hint for given name.", },
 	    { &hint_root,   "root", "Replace root hints set (empty value to return current list).", },
 	    { NULL, NULL, NULL }