diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c
index ad6469766ed8910924abe457face9306e48c7c1e..2823711700a6cec6cbc3e6dd183907ee74d3a359 100644
--- a/src/knot/conf/tools.c
+++ b/src/knot/conf/tools.c
@@ -34,6 +34,7 @@
 #include "knot/conf/conf.h"
 #include "knot/conf/scheme.h"
 #include "knot/common/log.h"
+#include "knot/nameserver/query_module.h"
 #include "libknot/errcode.h"
 #include "libknot/yparser/yptrafo.h"
 #include "contrib/wire_ctx.h"
@@ -77,33 +78,31 @@ int mod_id_to_bin(
 
 	// Check for "mod_name/mod_id" format.
 	const uint8_t *pos = (uint8_t *)strchr((char *)in->position, '/');
-	if (pos == NULL || pos >= stop) {
-		return KNOT_EINVAL;
-	}
-
-	// Check for missing module name.
 	if (pos == in->position) {
+		// Missing module name.
+		return KNOT_EINVAL;
+	} else if (pos >= stop - 1) {
+		// Missing module identifier after slash.
 		return KNOT_EINVAL;
 	}
 
 	// Write mod_name in the yp_name_t format.
-	uint8_t name_len = pos - in->position;
+	uint8_t name_len = (pos != NULL) ? (pos - in->position) :
+	                                   wire_ctx_available(in);
 	wire_ctx_write_u8(out, name_len);
 	wire_ctx_write(out, in->position, name_len);
 	wire_ctx_skip(in, name_len);
 
-	// Skip the separator.
-	wire_ctx_skip(in, sizeof(uint8_t));
-
-	// Check for missing id.
-	if (wire_ctx_available(in) == 0) {
-		return KNOT_EINVAL;
-	}
+	// Check for mod_id.
+	if (pos != NULL) {
+		// Skip the separator.
+		wire_ctx_skip(in, sizeof(uint8_t));
 
-	// Write mod_id as a zero terminated string.
-	int ret = yp_str_to_bin(in, out, stop);
-	if (ret != KNOT_EOK) {
-		return ret;
+		// Write mod_id as a zero terminated string.
+		int ret = yp_str_to_bin(in, out, stop);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	YP_CHECK_RET;
@@ -119,13 +118,16 @@ int mod_id_to_txt(
 	wire_ctx_write(out, in->position, name_len);
 	wire_ctx_skip(in, name_len);
 
-	// Write the separator.
-	wire_ctx_write_u8(out, '/');
+	// Check for mod_id.
+	if (wire_ctx_available(in) > 0) {
+		// Write the separator.
+		wire_ctx_write_u8(out, '/');
 
-	// Write mod_id.
-	int ret = yp_str_to_txt(in, out);
-	if (ret != KNOT_EOK) {
-		return ret;
+		// Write mod_id.
+		int ret = yp_str_to_txt(in, out);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	YP_CHECK_RET;
@@ -332,6 +334,16 @@ int check_modref(
 	const uint8_t *id = args->data + 1 + args->data[0];
 	size_t id_len = args->data_len - 1 - args->data[0];
 
+	// Check if the module requires some configuration.
+	if (id_len == 0) {
+		static_module_t *mod = find_module(mod_name);
+		if (mod == NULL) {
+			return KNOT_EINVAL;
+		}
+
+		return mod->opt_conf ? KNOT_EOK : KNOT_YP_ENOID;
+	}
+
 	// Try to find a module with the id.
 	if (!conf_rawid_exists_txn(args->conf, args->txn, mod_name, id, id_len)) {
 		args->err_str = "invalid module reference";
diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c
index f60069d7947c874d912ed7d0d3b24421b07e9de2..204db16912e5f6adb17c820b2f3203f28103b6c9 100644
--- a/src/knot/nameserver/query_module.c
+++ b/src/knot/nameserver/query_module.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -32,27 +32,20 @@
 #include "knot/modules/whoami.h"
 #include "knot/modules/noudp.h"
 
-typedef struct static_module {
-	const yp_name_t *name;
-	qmodule_load_t load;
-	qmodule_unload_t unload;
-	unsigned scope;
-} static_module_t;
-
 /*! \note All modules should be dynamically loaded later on. */
 static_module_t MODULES[] = {
-        { C_MOD_SYNTH_RECORD, &synth_record_load, &synth_record_unload, MOD_SCOPE_ANY },
-        { C_MOD_DNSPROXY,     &dnsproxy_load,     &dnsproxy_unload,     MOD_SCOPE_ANY },
-        { C_MOD_ONLINE_SIGN,  &online_sign_load,  &online_sign_unload,  MOD_SCOPE_ZONE },
+	{ C_MOD_SYNTH_RECORD, &synth_record_load, &synth_record_unload, MOD_SCOPE_ANY },
+	{ C_MOD_DNSPROXY,     &dnsproxy_load,     &dnsproxy_unload,     MOD_SCOPE_ANY },
+	{ C_MOD_ONLINE_SIGN,  &online_sign_load,  &online_sign_unload,  MOD_SCOPE_ZONE, true },
 #ifdef HAVE_ROSEDB
-        { C_MOD_ROSEDB,       &rosedb_load,       &rosedb_unload,       MOD_SCOPE_ANY },
+	{ C_MOD_ROSEDB,       &rosedb_load,       &rosedb_unload,       MOD_SCOPE_ANY },
 #endif
 #if USE_DNSTAP
-        { C_MOD_DNSTAP,       &dnstap_load,       &dnstap_unload,       MOD_SCOPE_ANY },
+	{ C_MOD_DNSTAP,       &dnstap_load,       &dnstap_unload,       MOD_SCOPE_ANY },
 #endif
-        { C_MOD_WHOAMI,       &whoami_load,       &whoami_unload,       MOD_SCOPE_ANY },
-	{ C_MOD_NOUDP,        &noudp_load,        &noudp_unload,        MOD_SCOPE_ANY },
-        { NULL }
+	{ C_MOD_WHOAMI,       &whoami_load,       &whoami_unload,       MOD_SCOPE_ANY, true },
+	{ C_MOD_NOUDP,        &noudp_load,        &noudp_unload,        MOD_SCOPE_ANY, true },
+	{ NULL }
 };
 
 struct query_plan *query_plan_create(knot_mm_t *mm)
@@ -114,7 +107,7 @@ int query_plan_step(struct query_plan *plan, int stage, qmodule_process_t proces
 	return KNOT_EOK;
 }
 
-static static_module_t *find_module(const yp_name_t *name)
+static_module_t *find_module(const yp_name_t *name)
 {
 	/* Search for the module by name. */
 	static_module_t *module = NULL;
diff --git a/src/knot/nameserver/query_module.h b/src/knot/nameserver/query_module.h
index 599afb73b20f2eecf3069f07563336a913ed9e83..1b7e129f6dd94b1ad0b27b3130d9f1599e3c7906 100644
--- a/src/knot/nameserver/query_module.h
+++ b/src/knot/nameserver/query_module.h
@@ -78,6 +78,14 @@ typedef int (*qmodule_load_t)(struct query_plan *plan, struct query_module *self
 typedef int (*qmodule_unload_t)(struct query_module *self);
 typedef int (*qmodule_process_t)(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx);
 
+typedef struct static_module {
+	const yp_name_t *name;
+	qmodule_load_t load;
+	qmodule_unload_t unload;
+	unsigned scope;
+	bool opt_conf;
+} static_module_t;
+
 /*!
  * Query module is a dynamically loadable unit that can alter query processing plan.
  * Module requires load and unload callback handlers and is provided with a context
@@ -110,6 +118,8 @@ struct query_plan {
 	list_t stage[QUERY_PLAN_STAGES];
 };
 
+static_module_t *find_module(const yp_name_t *name);
+
 /*! \brief Create an empty query plan. */
 struct query_plan *query_plan_create(knot_mm_t *mm);
 
diff --git a/tests-extra/tools/dnstest/module.py b/tests-extra/tools/dnstest/module.py
index 5b6457754c2e537a2ebbe69671f0cc0604a68c57..df40d466a7328edafe806b10be2f46793b542c46 100644
--- a/tests-extra/tools/dnstest/module.py
+++ b/tests-extra/tools/dnstest/module.py
@@ -16,6 +16,8 @@ class KnotModule(object):
     src_name = None
     # Module name in the configuration.
     conf_name = None
+    # Empty configuration
+    empty = False
 
     def __init__(self):
         self.conf_id = "id%s" % type(self).count
@@ -47,9 +49,12 @@ class KnotModule(object):
             raise Skip("Module '%s' not detected" % cls.conf_name)
 
     def get_conf_ref(self):
-        return "%s/%s" % (self.conf_name, self.conf_id)
+        if self.empty:
+            return str(self.conf_name)
+        else:
+            return "%s/%s" % (self.conf_name, self.conf_id)
 
-    def get_conf(self): pass
+    def get_conf(self, conf=None): pass
 
 class ModSynthRecord(KnotModule):
     '''Automatic forward/reverse records module'''
@@ -139,20 +144,11 @@ class ModWhoami(KnotModule):
 
     src_name = "whoami_load"
     conf_name = "mod-whoami"
+    empty = True
 
     def __init__(self):
         super().__init__()
 
-    def get_conf(self, conf=None):
-        if not conf:
-            conf = dnstest.config.KnotConf()
-
-        conf.begin(self.conf_name)
-        conf.id_item("id", self.conf_id)
-        conf.end()
-
-        return conf
-
 class ModRosedb(KnotModule):
     '''Rosedb module'''
 
diff --git a/tests/conf_tools.c b/tests/conf_tools.c
index e3e450835948802cbe0841ca3bc387e08b9825da..a9f617ae41361f109aca8cbbc0941526b7bd2635 100644
--- a/tests/conf_tools.c
+++ b/tests/conf_tools.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
 
 	/* Module id tests. */
 	mod_id_test("module/id", "\x06moduleid");
-	mod_id_bad_test("module", KNOT_EINVAL);
+	mod_id_test("module", "\x06module");
 	mod_id_bad_test("module/", KNOT_EINVAL);
 	mod_id_bad_test("/", KNOT_EINVAL);
 	mod_id_bad_test("/id", KNOT_EINVAL);