diff --git a/src/libknot/dnssec/zone-nsec.c b/src/libknot/dnssec/zone-nsec.c
index 1d2f1f56e82ee78a598f80c1049f295df2d45195..b5cb1aedf78624bd90ba26b93c305497d02b83fb 100644
--- a/src/libknot/dnssec/zone-nsec.c
+++ b/src/libknot/dnssec/zone-nsec.c
@@ -31,6 +31,15 @@
 #include "libknot/zone/zone-diff.h"
 #include "libknot/util/debug.h"
 
+/*!
+ * \brief Parameters to be used in connect_nsec_nodes callback.
+ */
+typedef struct {
+	uint32_t ttl;
+	knot_changeset_t *changeset;
+	const knot_zone_contents_t *zone;
+} nsec_chain_iterate_data_t;
+
 /* - NSEC chain iteration -------------------------------------------------- */
 
 typedef int (*chain_iterate_cb)(knot_node_t *, knot_node_t *, void *);
@@ -75,7 +84,14 @@ static int chain_iterate(knot_zone_tree_t *nodes, chain_iterate_cb callback,
 		current = (knot_node_t *)*hattrie_iter_val(it);
 
 		int result = callback(previous, current, data);
-		if (result != KNOT_EOK) {
+		if (result == NSEC_NODE_SKIP) {
+			// No NSEC should be created for this node, skip 2 nodes
+			hattrie_iter_next(it);
+			if (hattrie_iter_finished(it)) {
+				break;
+			}
+			current = (knot_node_t *)*hattrie_iter_val(it);
+		} else if (result != KNOT_EOK) {
 			hattrie_iter_free(it);
 			return result;
 		}
@@ -89,6 +105,55 @@ static int chain_iterate(knot_zone_tree_t *nodes, chain_iterate_cb callback,
 	return callback(current, first, data);
 }
 
+/*!
+ * \brief Add entry for removed NSEC to the changeset.
+ *
+ * \param oldrr      Old NSEC RR set to be removed (including RRSIG).
+ * \param changeset  Changeset to add the old RR into.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int changeset_remove_nsec(const knot_rrset_t *oldrr,
+                                 knot_changeset_t *changeset)
+{
+	assert(oldrr);
+	assert(changeset);
+
+	int result;
+
+	// extract copy of NSEC and RRSIG
+
+	knot_rrset_t *old_nsec = NULL;
+	knot_rrset_t *old_rrsigs = NULL;
+
+	result = knot_rrset_deep_copy(oldrr, &old_nsec, 1);
+	if (result != KNOT_EOK)
+		return result;
+
+	old_rrsigs = old_nsec->rrsigs;
+	old_nsec->rrsigs = NULL;
+
+	// update changeset
+
+	result = knot_changeset_add_rrset(changeset, old_nsec,
+	                                  KNOT_CHANGESET_REMOVE);
+	if (result != KNOT_EOK) {
+		knot_rrset_deep_free(&old_nsec, 1, 1);
+		knot_rrset_deep_free(&old_rrsigs, 1, 1);
+		return result;
+	}
+
+	if (old_rrsigs) {
+		result = knot_changeset_add_rrset(changeset, old_rrsigs,
+		                                  KNOT_CHANGESET_REMOVE);
+		if (result != KNOT_EOK) {
+			knot_rrset_deep_free(&old_rrsigs, 1, 1);
+			return result;
+		}
+	}
+
+	return KNOT_EOK;
+}
 
 /* - NSEC nodes construction ----------------------------------------------- */
 
@@ -142,6 +207,7 @@ static knot_rrset_t *create_nsec_rrset(const knot_node_t *from,
 	return rrset;
 }
 
+<<<<<<< HEAD
 /*!
  * \brief Add entry for removed NSEC to the changeset..
  *
@@ -179,28 +245,57 @@ static int changeset_remove_nsec(const knot_rrset_t *oldrr,
 		knot_rrset_deep_free(&old_nsec, 1, 1);
 		knot_rrset_deep_free(&old_rrsigs, 1, 1);
 		return result;
+=======
+static int fix_nsec_chain_for(const knot_node_t *node,
+                              const knot_node_t *to,
+                              const nsec_chain_iterate_data_t *data)
+{
+	assert(node);
+	assert(to);
+	assert(data);
+	/*!
+	 * Previous NSEC can either be in changeset, or in
+	 * zone (we get to it via node->prev), but never in both.
+	 */
+	const knot_rrset_t *last_rr = knot_changeset_last_rr(data->changeset,
+	                                                     KNOT_CHANGESET_ADD);
+	assert(!(knot_node_rrset(node->prev, KNOT_RRTYPE_NSEC) && last_rr &&
+	         last_rr->type == KNOT_RRTYPE_NSEC));
+	const knot_rrset_t *prev_nsec = node->prev ?
+	                                knot_node_rrset(node->prev,
+	                                                KNOT_RRTYPE_NSEC) :
+	                                last_rr;
+	assert(prev_nsec);
+	assert(prev_nsec->type == KNOT_RRTYPE_NSEC);
+
+	if (last_rr) {
+		// Force remove last NSEC from changeset (has bogus next field)
+		knot_changeset_remove_last_rr(data->changeset,
+		                              KNOT_CHANGESET_ADD);
+	} else {
+		// Remove last NSEC from zone (ditto)
+		int ret = changeset_remove_nsec(prev_nsec, data->changeset);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
+>>>>>>> DNSSEC: Initial solution for chain reconnection
 	}
 
-	if (old_rrsigs) {
-		result = knot_changeset_add_rrset(changeset, old_rrsigs,
-		                                  KNOT_CHANGESET_REMOVE);
-		if (result != KNOT_EOK) {
-			knot_rrset_deep_free(&old_rrsigs, 1, 1);
-			return result;
-		}
+	// Create new RR pointing to the 'to' node (node after deleted node)
+	const knot_node_t *prev_node =
+		node->prev ? node->prev :
+		knot_zone_contents_find_previous(data->zone, node->owner);
+	assert(prev_node); // This fails for apex, but it should never happen
+	knot_rrset_t *fixed_nsec = create_nsec_rrset(prev_node, to, data->ttl);
+	if (fixed_nsec == NULL) {
+		return KNOT_ENOMEM;
 	}
 
-	return KNOT_EOK;
+	// Add new NSEC RR to changeset
+	return knot_changeset_add_rrset(data->changeset, fixed_nsec,
+	                                KNOT_CHANGESET_ADD);
 }
 
-/*!
- * \brief Parameters to be used in connect_nsec_nodes callback.
- */
-typedef struct {
-	uint32_t ttl;
-	knot_changeset_t *changeset;
-} nsec_chain_iterate_data_t;
-
 /*!
  * \brief Connect two nodes by adding a NSEC RR into the first node.
  *
@@ -215,11 +310,11 @@ typedef struct {
  */
 static int connect_nsec_nodes(knot_node_t *a, knot_node_t *b, void *d)
 {
-	nsec_chain_iterate_data_t *data = (nsec_chain_iterate_data_t *)d;
-
-	dbg_dnssec_detail("Changeset emtpy during generating NSEC chain: %d\n",
-	                  knot_changeset_is_empty(data->changeset));
+	if (b->rrset_count == 0 || knot_node_is_non_auth(b)) {
+		return NSEC_NODE_SKIP;
+	}
 
+	nsec_chain_iterate_data_t *data = (nsec_chain_iterate_data_t *)d;
 	knot_rrset_t *old_nsec = knot_node_get_rrset(a, KNOT_RRTYPE_NSEC);
 
 	int ret = 0;
@@ -235,7 +330,11 @@ dbg_dnssec_exec_detail(
 		free(name);
 );
 		ret = changeset_remove_nsec(old_nsec, data->changeset);
-		return ret;
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
+		// Fix NSEC chain
+		return fix_nsec_chain_for(a, b, data);
 	}
 
 	// create new NSEC
@@ -286,7 +385,7 @@ static int create_nsec_chain(const knot_zone_contents_t *zone, uint32_t ttl,
 	assert(zone);
 	assert(zone->nodes);
 
-	nsec_chain_iterate_data_t data = { ttl, changeset };
+	nsec_chain_iterate_data_t data = { ttl, changeset, zone };
 
 	return chain_iterate(zone->nodes, connect_nsec_nodes, &data);
 }
@@ -561,11 +660,12 @@ static int connect_nsec3_nodes(knot_node_t *a, knot_node_t *b, void *data)
 	uint8_t *rdata_hash = nsec3_rdata_hash(a->rrset_tree[0]->rdata);
 
 	uint8_t *b32_hash = (uint8_t *)knot_dname_to_str(b->owner);
-	if (!b32_hash)
+	if (!b32_hash) {
 		return KNOT_ENOMEM;
+	}
 
-	int written = base32hex_decode(b32_hash, KNOT_NSEC3_HASH_B32_LENGTH,
-	                               rdata_hash, KNOT_NSEC3_HASH_LENGTH);
+	int32_t written = base32hex_decode(b32_hash, KNOT_NSEC3_HASH_B32_LENGTH,
+	                                   rdata_hash, KNOT_NSEC3_HASH_LENGTH);
 
 	free(b32_hash);
 
@@ -579,7 +679,7 @@ static int connect_nsec3_nodes(knot_node_t *a, knot_node_t *b, void *data)
  * \brief Get zone apex as a string.
  */
 static bool get_zone_apex_str(const knot_zone_contents_t *zone,
-			      char **apex, size_t *apex_size)
+                              char **apex, size_t *apex_size)
 {
 	assert(zone);
 	assert(zone->apex);
@@ -672,9 +772,6 @@ static int create_nsec3_nodes(const knot_zone_contents_t *zone, uint32_t ttl,
 		if (result != KNOT_EOK)
 			break;
 
-		// Caused invalid reads after updating zone w/ changesets
-		//node->nsec3_node = nsec3_node;
-
 		hattrie_iter_next(it);
 	}
 
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
index 81781f75d46dc4f75446e4acbe020a7266131b82..33fdd030a45bf2f0eabc34c066bbc12cd3619396 100644
--- a/src/libknot/updates/changesets.c
+++ b/src/libknot/updates/changesets.c
@@ -122,6 +122,40 @@ knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *chs)
 	return (knot_changeset_t *)(TAIL(chs->sets));
 }
 
+const knot_rrset_t *knot_changeset_last_rr(const knot_changeset_t *ch,
+                                           knot_changeset_part_t part)
+{
+	if (ch == NULL) {
+		return NULL;
+	}
+
+	if (part == KNOT_CHANGESET_ADD) {
+		knot_rr_ln_t *n = TAIL(ch->add);
+		return n ? n->rr : NULL;
+	} else if (part == KNOT_CHANGESET_REMOVE) {
+		knot_rr_ln_t *n = TAIL(ch->remove);
+		return n ? n->rr : NULL;
+	}
+
+	return NULL;
+}
+
+void knot_changeset_remove_last_rr(knot_changeset_t *ch,
+                                   knot_changeset_part_t part)
+{
+	if (ch == NULL) {
+		return;
+	}
+
+	if (part == KNOT_CHANGESET_ADD) {
+		knot_rr_ln_t *n = TAIL(ch->add);
+		rem_node((node *)n);
+	} else if (part == KNOT_CHANGESET_REMOVE) {
+		knot_rr_ln_t *n = TAIL(ch->remove);
+		rem_node((node *)n);
+	}
+}
+
 int knot_changeset_add_rrset(knot_changeset_t *chgs, knot_rrset_t *rrset,
                              knot_changeset_part_t part)
 {
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index dc09cc6dc73a6dc0b5ca49a864a7c14235c0d77e..bd92cf67f4b9ab0298ccc175977abedc6c67dd06 100644
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -59,13 +59,13 @@ typedef struct knot_changeset {
 
 /*----------------------------------------------------------------------------*/
 
-/*! \brief Wrapper BIRD lists. Storing: RRSet. */
+/*! \brief Wrapper for BIRD lists. Storing: RRSet. */
 typedef struct knot_rr_ln {
 	node n; /*!< List node. */
 	knot_rrset_t *rr; /*!< Actual usable data. */
 } knot_rr_ln_t;
 
-/*! \brief Wrapper BIRD lists. Storing: Node. */
+/*! \brief Wrapper for BIRD lists. Storing: Node. */
 typedef struct knot_node_ln {
 	node n; /*!< List node. */
 	knot_node_t *node; /*!< Actual usable data. */
@@ -173,6 +173,11 @@ knot_changeset_t *knot_changesets_create_changeset(knot_changesets_t *ch);
  */
 knot_changeset_t *knot_changesets_get_last(const knot_changesets_t *ch);
 
+const knot_rrset_t *knot_changeset_last_rr(const knot_changeset_t *ch,
+                                           knot_changeset_part_t part);
+void knot_changeset_remove_last_rr(knot_changeset_t *ch,
+                                   knot_changeset_part_t part);
+
 /*!
  * \brief Add RRSet to changeset. RRSet is either inserted to 'add' or to
  *        'remove' list. Will *not* try to merge with previous RRSets.
@@ -209,7 +214,7 @@ int knot_changeset_add_rr(knot_changeset_t *chgs,
  * \param part To which part we store SOA (from = REMOVE, add = TO)
  */
 void knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
-                           knot_changeset_part_t part);
+                            knot_changeset_part_t part);
 
 /*!
  * \brief Checks whether changeset is empty.