diff --git a/doc/man/kzonesign.1in b/doc/man/kzonesign.1in
index e2b93e36f6e905f01d72a7604b16f4d37d225deb..397b67a1977a4da096421ba87005c9fa721baad9 100644
--- a/doc/man/kzonesign.1in
+++ b/doc/man/kzonesign.1in
@@ -58,8 +58,11 @@ Write the output zone file to the specified directory instead of the configured
 Allow key roll\-overs and NSEC3 re\-salt. In order to finish possible KSK submission,
 set the KSK\(aqs \fBactive\fP timestamp to now (\fB+0\fP) using keymgr\&.
 .TP
+\fB\-v\fP, \fB\-\-verify\fP
+Instead of (re\-)signing the zone, just verify that the zone is correctly signed.
+.TP
 \fB\-t\fP, \fB\-\-time\fP \fItimestamp\fP
-Sign the zone (and roll the keys if necessary) as if it was at the time
+Sign/verify the zone (and roll the keys if necessary) as if it was at the time
 specified by timestamp.
 .TP
 \fB\-h\fP, \fB\-\-help\fP
diff --git a/doc/man_kzonesign.rst b/doc/man_kzonesign.rst
index 5c2499da2ea4c37b3ba9e4a962cba00310dfa298..a165f2ca2043a807e65281691d2e18c916764901 100644
--- a/doc/man_kzonesign.rst
+++ b/doc/man_kzonesign.rst
@@ -35,8 +35,11 @@ Options
   Allow key roll-overs and NSEC3 re-salt. In order to finish possible KSK submission,
   set the KSK's **active** timestamp to now (**+0**) using :doc:`keymgr<man_keymgr>`.
 
+**-v**, **--verify**
+  Instead of (re-)signing the zone, just verify that the zone is correctly signed.
+
 **-t**, **--time** *timestamp*
-  Sign the zone (and roll the keys if necessary) as if it was at the time
+  Sign/verify the zone (and roll the keys if necessary) as if it was at the time
   specified by timestamp.
 
 **-h**, **--help**
diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c
index 0dcb06516b200d5aba0354098d4bb9b3e8287919..655df2f25f0fd8522cf0cff786f1ab634ba164ac 100644
--- a/src/knot/dnssec/zone-events.c
+++ b/src/knot/dnssec/zone-events.c
@@ -397,10 +397,13 @@ knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx)
 	}
 }
 
-int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, bool incremental)
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental)
 {
 	kdnssec_ctx_t ctx = { 0 };
 	int ret = kdnssec_validation_ctx(conf, &ctx, update->new_cont);
+	if (now != 0) {
+		ctx.now = now;
+	}
 	if (ret == KNOT_EOK) {
 		ret = knot_zone_check_nsec_chain(update, &ctx, incremental);
 	}
diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h
index b1c389d56a2ef43a9f304fefe8d70528374a171a..d3667f351205877debba589eb0976976d3e69075 100644
--- a/src/knot/dnssec/zone-events.h
+++ b/src/knot/dnssec/zone-events.h
@@ -126,8 +126,9 @@ knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx);
  *
  * \param update         Zone update with contents.
  * \param conf           Knot configuration.
+ * \param now            If not zero: adjust "now" to this timestamp.
  * \param incremental    Try to validate incrementally.
  *
  * \return KNOT_E*
  */
-int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, bool incremental);
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental);
diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c
index 44af6079306620dfb8d667bb5262ec62119046ec..944a6f9ceaf232e34895813d6b1d506f1e455757 100644
--- a/src/knot/updates/zone-update.c
+++ b/src/knot/updates/zone-update.c
@@ -921,7 +921,7 @@ int zone_update_commit(conf_t *conf, zone_update_t *update)
 		bool incr_valid = update->flags & UPDATE_INCREMENTAL;
 		const char *msg_valid = incr_valid ? "incremental " : "";
 
-		ret = knot_dnssec_validate_zone(update, conf, incr_valid);
+		ret = knot_dnssec_validate_zone(update, conf, 0, incr_valid);
 		if (ret != KNOT_EOK) {
 			log_zone_error(update->zone->name, "DNSSEC, %svalidation failed (%s)",
 			               msg_valid, knot_strerror(ret));
diff --git a/src/utils/common/msg.h b/src/utils/common/msg.h
index 9ab5a493f1e275c650b965bfa3aa83f5c43f525f..137870a079c26b0bd6966e6e7d5daa79bff83ebd 100644
--- a/src/utils/common/msg.h
+++ b/src/utils/common/msg.h
@@ -39,3 +39,4 @@ int msg_debug(const char *fmt, ...);
 
 #define ERR2(msg, ...)	{ fprintf(stderr, "Error: "   msg, ##__VA_ARGS__); fflush(stderr); }
 #define WARN2(msg, ...)	{ fprintf(stderr, "Warning: " msg, ##__VA_ARGS__); fflush(stderr); }
+#define INFO2(msg, ...)	{ fprintf(stdout,             msg, ##__VA_ARGS__); fflush(stdout); }
diff --git a/src/utils/kzonesign/main.c b/src/utils/kzonesign/main.c
index 9c3666a4007ea48786a267478d097383023f34c2..b72e25d183fd79b9b30fba71028fc8cd2bc33fe8 100644
--- a/src/utils/kzonesign/main.c
+++ b/src/utils/kzonesign/main.c
@@ -20,6 +20,7 @@
 #include "knot/dnssec/zone-events.h"
 #include "knot/updates/zone-update.h"
 #include "knot/server/server.h"
+#include "knot/zone/adjust.h"
 #include "knot/zone/zone-load.h"
 #include "knot/zone/zonefile.h"
 #include "utils/common/msg.h"
@@ -40,6 +41,7 @@ static void print_help(void)
 	       "                           (default %s)\n"
 	       " -o, --outdir <dir_name>  Output directory.\n"
 	       " -r, --rollover           Allow key rollovers and NSEC3 re-salt.\n"
+	       " -v, --verify             Only verify if zone is signed correctly.\n"
 	       " -t, --time <timestamp>   Current time specification.\n"
 	       "                           (default current UNIX time)\n"
 	       " -h, --help               Print the program help.\n"
@@ -54,6 +56,7 @@ typedef struct {
 	const char *outdir;
 	zone_sign_roll_flags_t rollover;
 	int64_t timestamp;
+	bool verify;
 } sign_params_t;
 
 static int zonesign(sign_params_t *params)
@@ -98,6 +101,30 @@ static int zonesign(sign_params_t *params)
 		goto fail;
 	}
 
+	if (params->verify) {
+		val = conf_zone_get(conf(), C_ADJUST_THR, params->zone_name);
+		ret = zone_adjust_full(up.new_cont, conf_int(&val));
+		if (ret != KNOT_EOK) {
+			ERR2("failed to adjust the zone (%s)\n", knot_strerror(ret));
+			zone_update_clear(&up);
+			goto fail;
+		}
+
+		ret = knot_dnssec_validate_zone(&up, conf(), params->timestamp, false);
+		if (ret != KNOT_EOK) {
+			ERR2("DNSSEC validation failed (%s)\n", knot_strerror(ret));
+			char name_str[KNOT_DNAME_TXT_MAXLEN], type_str[16];
+			if (knot_dname_to_str(name_str, up.validation_hint.node, sizeof(name_str)) != NULL &&
+			    knot_rrtype_to_string(up.validation_hint.rrtype, type_str, sizeof(type_str)) >= 0) {
+				ERR2("affected node: '%s' type '%s'\n", name_str, type_str);
+			}
+		} else {
+			INFO2("DNSSEC validation successful\n");
+		}
+		zone_update_clear(&up);
+		goto fail;
+	}
+
 	kasp_db_ensure_init(&fake_server.kaspdb, conf());
 	zone_struct->server = &fake_server;
 
@@ -166,6 +193,7 @@ int main(int argc, char *argv[])
 		{ "confdb",    required_argument, NULL, 'C' },
 		{ "outdir",    required_argument, NULL, 'o' },
 		{ "rollover",  no_argument,       NULL, 'r' },
+		{ "verify" ,   no_argument,       NULL, 'v' },
 		{ "time",      required_argument, NULL, 't' },
 		{ "help",      no_argument,       NULL, 'h' },
 		{ "version",   no_argument,       NULL, 'V' },
@@ -175,7 +203,7 @@ int main(int argc, char *argv[])
 	tzset();
 
 	int opt = 0;
-	while ((opt = getopt_long(argc, argv, "c:C:o:rt:hV", opts, NULL)) != -1) {
+	while ((opt = getopt_long(argc, argv, "c:C:o:rvt:hV", opts, NULL)) != -1) {
 		switch (opt) {
 		case 'c':
 			if (util_conf_init_file(optarg) != KNOT_EOK) {
@@ -193,6 +221,9 @@ int main(int argc, char *argv[])
 		case 'r':
 			params.rollover = KEY_ROLL_ALLOW_ALL;
 			break;
+		case 'v':
+			params.verify = true;
+			break;
 		case 't':
 			; uint32_t num = 0;
 			if (str_to_u32(optarg, &num) != KNOT_EOK || num == 0) {