diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
index f38e433b135f998e54213dfc76eb55a3f460af03..e1e938204cf85050bdb63b7942fe3dfce96b4fcc 100644
--- a/src/knot/nameserver/internet.c
+++ b/src/knot/nameserver/internet.c
@@ -120,7 +120,7 @@ static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname
 /*! \brief DNSSEC both requested & available. */
 static bool have_dnssec(struct query_data *qdata)
 {
-	return knot_pkt_have_dnssec(qdata->query) &&
+	return pkt_has_dnssec(qdata->query) &&
 	       zone_contents_is_signed(qdata->zone->contents);
 }
 
@@ -655,11 +655,7 @@ static int solve_additional(int state, knot_pkt_t *pkt,
                             struct query_data *qdata, void *ctx)
 {
 	/* Put OPT RR. */
-	server_t *server = qdata->param->server;
-	int ret = knot_pkt_write_opt(pkt, server->edns,
-	                             knot_pkt_have_nsid(qdata->query)
-	                             && server->edns->nsid != NULL
-	                             && server->edns->nsid_len > 0);
+	int ret = knot_pkt_put(pkt, COMPR_HINT_NONE, pkt->opt_rr, 0);
 	if (ret != KNOT_EOK) {
 		return ERROR;
 	}
@@ -834,7 +830,7 @@ int internet_query(knot_pkt_t *response, struct query_data *qdata)
 	NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED);
 
 	/* No applicable ACL, refuse transaction security. */
-	if (knot_pkt_have_tsig(qdata->query)) {
+	if (knot_pkt_has_tsig(qdata->query)) {
 		/* We have been challenged... */
 		NS_NEED_AUTH(qdata->zone->xfr_out, qdata);
 
diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c
index 13afdc3f81e5ff0d6d39dbc899dde1db6508300b..da2a83292f36c17f6ca76ed6c47b9dd8c63fa4ff 100644
--- a/src/knot/nameserver/process_query.c
+++ b/src/knot/nameserver/process_query.c
@@ -549,25 +549,36 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_proces
 	}
 
 	/* Check if EDNS is supported. */
-	if (!knot_pkt_have_edns(query)) {
+	if (knot_pkt_has_edns(query)) {
 		return KNOT_EOK;
 	}
 
-	assert(resp->rrset_count < KNOT_PKT_MAX_RRS);
-	/* Reserve OPT RR to the packet, initialized by server's EDNS parameters. */
-	ret = knot_pkt_reserve(resp, knot_edns_wire_size(server->edns));
-	if (ret != KNOT_EOK) {
-		dbg_ns("%s: can't reserve OPT RR in response (%d)\n", __func__, ret);
-		/* Free the OPT RR. */
-		return ret;
+	/* Create copy of the OPT RR and store it in packet. */
+	resp->opt_rr = knot_rrset_copy(server->opt_rr, &resp->mm);
+	if (resp->opt_rr == NULL) {
+		return KNOT_ENOMEM;
 	}
 
-	/* Copy DO bit if set (DNSSEC requested). */
-	if (knot_pkt_have_dnssec(query)) {
+	/* Remove OPTIONs from RDATA. Retain NSID if requested. */
+	knot_edns_clear_options(resp->opt_rr, pkt_has_nsid(query));
+
+	/* Set DO bit if set (DNSSEC requested). */
+	if (pkt_has_dnssec(query)) {
 		dbg_ns("%s: setting DO=1 in OPT RR\n", __func__);
 		knot_edns_set_do(resp->opt_rr);
 	}
 
+	/* Reserve space for OPT RR in the packet. Using size of the server's
+	 * OPT RR, because that's the maximum size (RDATA may or may not be
+	 * used).
+	 */
+	ret = knot_pkt_reserve(resp, knot_edns_wire_size(resp->opt_rr));
+	if (ret != KNOT_EOK) {
+		dbg_ns("%s: can't reserve OPT RR in response (%d)\n", __func__, ret);
+		knot_rrset_free(&resp->opt_rr, &resp->mm);
+		return ret;
+	}
+
 	/* Get minimal supported size from EDNS(0). */
 	uint16_t client_maxlen = knot_edns_get_payload(query->opt_rr);
 	uint16_t server_maxlen = knot_edns_get_payload(resp->opt_rr);
@@ -579,7 +590,7 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_proces
 		dbg_ns("%s: packet size limit <= %zuB\n", __func__, resp->max_size);
 	}
 
-	/* In the response, always advertise its maximum UPD payload size.*/
+	/* In the response, always advertise server's maximum UPD payload. */
 
 	return ret;
 }
diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h
index 2b1c3d304b574fca25c4b5bf112f9e3b507875c5..07137ecbd970a4196825a10f1741a88dde279ea0 100644
--- a/src/knot/nameserver/process_query.h
+++ b/src/knot/nameserver/process_query.h
@@ -199,6 +199,19 @@ int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata);
 
 int process_query_hooks(int qclass, int stage, knot_pkt_t *pkt, struct query_data *qdata);
 
+/*! \brief Checks if DO bit is set in the packet's OPT RR. */
+static inline bool pkt_has_dnssec(const knot_pkt_t *pkt)
+{
+	return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr);
+}
+
+/*! \brief Checks if there is an NSID OPTION in the packet's OPT RR. */
+static inline bool pkt_has_nsid(const knot_pkt_t *pkt)
+{
+	return knot_pkt_has_edns(pkt)
+	       && knot_edns_has_option(pkt->opt_rr, KNOT_EDNS_OPTION_NSID);
+}
+
 #endif /* _PROCESS_QUERY_H_ */
 
 /*! @} */
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 8fb033a1e79f428f5339e342689001d494c49d42..b9b3382a06b0be2c08d84cefd33f26652f9e33a3 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -328,8 +328,8 @@ void server_deinit(server_t *server)
 	/* Free remaining events. */
 	evsched_deinit(&server->sched);
 
-	/* Free EDNS parameters. */
-	knot_edns_free_params(&server->edns);
+	/* Free OPT RR. */
+	knot_rrset_free(&server->opt_rr, NULL);
 
 	/* Clear the structure. */
 	memset(server, 0, sizeof(server_t));
@@ -479,21 +479,19 @@ static int edns_reconfigure(const struct conf_t *conf, server_t *server)
 {
 	dbg_server("%s(%p, %p)\n", __func__, conf, server);
 
-	/* New EDNS options: keep the old pointer and free it after RCU sync. */
-	knot_edns_params_t *edns =
-		knot_edns_new_params(conf->max_udp_payload, KNOT_EDNS_VERSION,
-	                             KNOT_EDNS_DEFAULT_FLAGS, conf->nsid_len,
-	                             (uint8_t *)conf->nsid);
-	if (edns == NULL) {
+	knot_rrset_t *opt_rr = knot_edns_new(conf->max_udp_payload, 0,
+	                                     KNOT_EDNS_VERSION,
+	                                     KNOT_EDNS_DEFAULT_FLAGS, NULL);
+	if (opt_rr == NULL) {
 		log_server_error("Couldn't initialize EDNS(0), please restart.\n");
 		return KNOT_ENOMEM;
 	}
 
-	knot_edns_params_t *edns_old = server->edns;
-	server->edns = edns;
+	knot_rrset_t *opt_old = server->opt_rr;
+	server->opt_rr = opt_rr;
 
 	synchronize_rcu();
-	knot_edns_free_params(&edns_old);
+	knot_rrset_free(&opt_old, NULL);
 
 	return KNOT_EOK;
 }
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index edd8688ba4796b568fb366ce4450eeb37be69685..a70f812552c74a1960911cb8cb137f8c9aa26407 100644
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
@@ -107,7 +107,7 @@ typedef struct server_t {
 
 	knot_zonedb_t *zone_db; /*!< Zone database. */
 
-	knot_edns_params_t *edns;   /*!< Server-specific EDNS(0) parameters. */
+	knot_rrset_t *opt_rr;   /*!< Server-specific OPT RR (EDNS(0)). */
 
 	/*! \brief I/O handlers. */
 	unsigned tu_size;
diff --git a/src/libknot/edns.c b/src/libknot/edns.c
index 263e11fce450e8e0a2fdabee94118cfa3e0423ef..ed6d158367ed377025db402188029b7eeee849bb 100644
--- a/src/libknot/edns.c
+++ b/src/libknot/edns.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <assert.h>
 
+#include <string.h>
 #include "libknot/edns.h"
 #include "libknot/common.h"
 #include "common/descriptor.h"
@@ -39,84 +40,27 @@ enum knot_edns_private_consts {
 
 #define DUMMY_RDATA_SIZE 1
 
-/*----------------------------------------------------------------------------*/
-/* EDNS server parameters handling functions                                  */
-/*----------------------------------------------------------------------------*/
-
-knot_edns_params_t *knot_edns_new_params(uint16_t max_payload, uint8_t ver,
-                                         uint16_t flags, uint16_t nsid_len,
-                                         uint8_t *nsid)
-{
-	knot_edns_params_t *edns =
-	               (knot_edns_params_t *)malloc(sizeof(knot_edns_params_t));
-	CHECK_ALLOC_LOG(edns, NULL);
-
-	edns->version = ver;
-	edns->payload = max_payload;
-	edns->flags = flags;
-
-	if (nsid_len > 0 && nsid != NULL) {
-		edns->nsid = (uint8_t *)malloc(edns->nsid_len);
-		if (edns->nsid == NULL) {
-			free(edns);
-			return NULL;
-		}
-		memcpy(edns->nsid, nsid, nsid_len);
-		edns->nsid_len = nsid_len;
-	} else {
-		edns->nsid = NULL;
-		edns->nsid_len = 0;
-	}
-
-	return edns;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_edns_free_params(knot_edns_params_t **edns)
-{
-	if (edns == NULL || *edns == NULL) {
-		return;
-	}
-
-	free((*edns)->nsid);
-	free(*edns);
-	*edns = NULL;
-}
-
 /*----------------------------------------------------------------------------*/
 
-size_t knot_edns_wire_size(knot_edns_params_t *edns)
+knot_rrset_t *knot_edns_new(uint16_t max_pld, uint8_t ext_rcode,
+                            uint8_t ver, uint16_t flags, mm_ctx_t *mm)
 {
-	if (edns == NULL) {
-		return 0;
-	}
-
-#warning: TODO: check if this is enough please
-	return KNOT_EDNS_MIN_SIZE + edns->nsid_len;
-}
-
-/*----------------------------------------------------------------------------*/
-/* EDNS OPT RR handling functions.                                            */
-/*----------------------------------------------------------------------------*/
-
-static int init_opt(knot_rrset_t *opt_rr, uint16_t max_pld, uint8_t ext_rcode,
-                    uint8_t ver, uint16_t flags, mm_ctx_t *mm)
-{
-	assert(opt_rr != NULL);
-
-	/* First clear the RR, so that no old data remains there. */
-	knot_rrset_init_empty(opt_rr);
-
 	/* Owner: root label. */
+	/*! \todo Maybe later modify dname_from_str() to use memory pool. */
 	size_t pos = 0;
-	opt_rr->owner = knot_dname_parse((uint8_t *)"\0", &pos, 1, mm);
-
-	/* TYPE = OPT */
-	opt_rr->type = KNOT_RRTYPE_OPT;
+	knot_dname_t *owner = knot_dname_parse((uint8_t *)"\0", &pos, 1, mm);
+	if (owner == NULL) {
+		ERR_ALLOC_FAILED;
+		return NULL;
+	}
 
-	/* CLASS = max UDP payload */
-	opt_rr->rclass = max_pld;
+	knot_rrset_t *opt_rr = knot_rrset_new(owner, KNOT_RRTYPE_OPT, max_pld,
+	                                      mm);
+	if (opt_rr == NULL) {
+		knot_dname_free(&owner, mm);
+		ERR_ALLOC_FAILED;
+		return NULL;
+	}
 
 	/* Empty RDATA */
 	uint8_t rdata[1] = { 0 };
@@ -136,30 +80,25 @@ static int init_opt(knot_rrset_t *opt_rr, uint16_t max_pld, uint8_t ext_rcode,
 	uint32_t ttl_local = knot_wire_read_u32((uint8_t *)&ttl);
 
 	int ret = knot_rrset_add_rdata(opt_rr, rdata, 0, ttl_local, mm);
+	if (ret != KNOT_EOK) {
+		knot_rrset_free(&opt_rr, mm);
+	}
 
-	return ret;
+	return opt_rr;
 }
 
 /*----------------------------------------------------------------------------*/
 
-int knot_edns_init_from_params(knot_rrset_t *opt_rr,
-                               const knot_edns_params_t *params, bool add_nsid,
-                               mm_ctx_t *mm)
+size_t knot_edns_wire_size(knot_rrset_t *opt_rr)
 {
-	if (opt_rr == NULL || params == NULL) {
-		return KNOT_EINVAL;
+	if (opt_rr == NULL) {
+		return 0;
 	}
 
-	init_opt(opt_rr, params->payload, 0, params->version, params->flags,
-	         mm);
-
-	int ret = KNOT_EOK;
-	if (add_nsid) {
-		ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
-		                           params->nsid_len, params->nsid, mm);
-	}
+	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
+	assert(rdata != NULL);
 
-	return ret;
+	return KNOT_EDNS_MIN_SIZE + knot_rdata_rdlen(rdata);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -271,6 +210,62 @@ void knot_edns_set_do(knot_rrset_t *opt_rr)
 	knot_rdata_set_ttl(knot_rdataset_at(&opt_rr->rrs, 0), ttl);
 }
 
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Find OPTION with the given code in the OPT RDATA.
+ *
+ * \param rdata     RDATA to search in.
+ * \param opt_code  Code of the OPTION to find.
+ * \param[out] pos  Position of the OPTION or NULL if not found.
+ */
+static void find_option(knot_rdata_t *rdata, uint16_t opt_code, uint8_t **pos)
+{
+	uint8_t *data = knot_rdata_data(rdata);
+	uint16_t rdlength = knot_rdata_rdlen(rdata);
+
+	*pos = NULL;
+
+	int i = 0;
+	while (i + 4 < rdlength) {
+		uint16_t code = knot_wire_read_u16(data + i);
+		if (opt_code == code) {
+			*pos = data + i;
+			return;
+		}
+		uint16_t opt_len = knot_wire_read_u16(data + i + 2);
+		i += (4 + opt_len);
+	}
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_clear_options(knot_rrset_t *opt_rr, bool retain_nsid)
+{
+	/*! \todo [OPT] IMPLEMENT */
+	if (opt_rr == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
+
+	if (retain_nsid) {
+		/* Search for NSID and move it to the beginning. Then crop. */
+		uint8_t *pos = NULL;
+		find_option(rdata, KNOT_EDNS_OPTION_NSID, &pos);
+
+		uint16_t nsid_len = knot_wire_read_u16(pos + 2);
+		uint16_t total_len = nsid_len + 4;
+
+		memmove(knot_rdata_data(rdata), pos, total_len);
+		knot_rdata_set_rdlen(rdata, total_len);
+	} else {
+		/* Clear the whole RDATA. No other OPTIONS supported now. */
+		knot_rdata_set_rdlen(rdata, 0);
+	}
+
+	return KNOT_EOK;
+}
+
 /*----------------------------------------------------------------------------*/
 
 int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
@@ -338,28 +333,20 @@ bool knot_edns_has_option(const knot_rrset_t *opt_rr, uint16_t code)
 	assert(opt_rr != NULL);
 	assert(opt_rr->rrs.rr_count == 1);
 
-	// Get the actual RDATA
-	uint8_t *data = knot_rdata_data(knot_rdataset_at(&opt_rr->rrs, 0));
-	uint16_t data_len = knot_rdata_rdlen(knot_rdataset_at(&opt_rr->rrs, 0));
+	knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0);
+	assert(rdata != NULL);
 
-	int pos = 0;
-	while (data_len - pos >= 4) {
-		uint16_t opt_code = knot_wire_read_u16(data + pos);
-		if (opt_code == code) {
-			return true;
-		}
-		uint16_t opt_len = knot_wire_read_u16(data + pos + 2);
-		pos += (4 + opt_len);
-	}
+	uint8_t *pos = NULL;
+	find_option(rdata, KNOT_EDNS_OPTION_NSID, &pos);
 
-	return false;
+	return pos != NULL;
 }
 
 /*----------------------------------------------------------------------------*/
 
 bool knot_edns_check_record(knot_rrset_t *opt_rr)
 {
-	/* \todo Semantic checks for the OPT. */
+	/*! \todo [OPT] Semantic checks for the OPT. */
 #warning: check semantics here
 	return true;
 }
diff --git a/src/libknot/edns.h b/src/libknot/edns.h
index 184701d420e2ef4b3cd01941a27de7a4ae4a47f1..c90b145b7f8f11242fc62ad1feacce68a7c37504 100644
--- a/src/libknot/edns.h
+++ b/src/libknot/edns.h
@@ -65,15 +65,6 @@ enum knot_edns_const {
 	KNOT_EDNS_MIN_SIZE        = 11
 };
 
-/*! \brief Enumeration of named options. */
-enum knot_edns_option {
-	KNOT_PKT_EDNS_PAYLOAD = 0,
-	KNOT_PKT_EDNS_VERSION = 1,
-	KNOT_PKT_EDNS_RCODE   = 2,
-	KNOT_PKT_EDNS_FLAG_DO = 3,
-	KNOT_PKT_EDNS_NSID    = 4
-};
-
 /*! \brief EDNS flags.
  *
  * \note Use only with unsigned 2-byte variables.
@@ -84,59 +75,20 @@ enum knot_edns_flags {
 };
 
 /*----------------------------------------------------------------------------*/
-/* EDNS server parameters handling functions                                  */
+/* EDNS OPT RR handling functions.                                            */
 /*----------------------------------------------------------------------------*/
-/*!
- * \brief Creates new structure for holding server's EDNS parameters.
- *
- * \param max_payload  Max UDP payload.
- * \param ver          EDNS version.
- * \param flags        Flags (in wire byte order).
- * \param nsid_len     Length of the NSID string. (Set to 0 if none.)
- * \param nsid         NSID string. (Set to NULL if none.)
- *
- * \return New EDNS parameters structure or NULL if an error occured.
- */
-knot_edns_params_t *knot_edns_new_params(uint16_t max_payload, uint8_t ver,
-                                         uint16_t flags, uint16_t nsid_len,
-                                         uint8_t *nsid);
-
-/*!
- * \brief Properly frees the EDNS parameters structure. (With the NSID.)
- *
- * \param edns EDNS parameters structure to be freed.
- */
-void knot_edns_free_params(knot_edns_params_t **edns);
 
+knot_rrset_t *knot_edns_new(uint16_t max_pld, uint8_t ext_rcode,
+                            uint8_t ver, uint16_t flags, mm_ctx_t *mm);
 
 /*!
  * \brief Returns size of the OPT RR in wire format.
  *
- * \param edns EDNS parameters.
+ * \param opt_rr OPT RR to count the wire size of.
  *
  * \return Size of the OPT RR in bytes.
  */
-size_t knot_edns_wire_size(knot_edns_params_t *edns);
-
-/*----------------------------------------------------------------------------*/
-/* EDNS OPT RR handling functions.                                            */
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Initializes given RRSet structure as an OPT RR with parameters taken
- *        from the given EDNS params.
- *
- * \param opt_rr    RRSet to initialize.
- * \param params    EDNS parameters structure to use.
- * \param add_nsid  Add NSID from the parameters to the OPT RR.
- * \param mm        Memory context to use (set to NULL if none available).
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_EINVAL when bad parameters are supplied.
- * \retval KNOT_ENOMEM if some allocation failed.
- */
-int knot_edns_init_from_params(knot_rrset_t *opt_rr,
-                               const knot_edns_params_t *params, bool add_nsid,
-                               mm_ctx_t *mm);
+size_t knot_edns_wire_size(knot_rrset_t *opt_rr);
 
 /*!
  * \brief Returns the Max UDP payload value stored in the OPT RR.
@@ -238,6 +190,17 @@ bool knot_edns_do(const knot_rrset_t *opt_rr);
  */
 void knot_edns_set_do(knot_rrset_t *opt_rr);
 
+/*!
+ * \brief Removes all OPTIONs from OPT RDATA, possibly except NSID.
+ *
+ * \param opt_rr       OPT RR to remove the OPTIONs from.
+ * \param retain_nsid  Set to true if NSID OPTION should be left in the OPT RR.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL if \a opt_rr is not set.
+ */
+int knot_edns_clear_options(knot_rrset_t *opt_rr, bool retain_nsid);
+
 /*!
  * \brief Adds EDNS Option to the OPT RR.
  *
diff --git a/src/libknot/packet/pkt.c b/src/libknot/packet/pkt.c
index c62e4e5cf4092716413f674bb3a780a5547eb8a4..7e68aec0616e52300f74156fbde472a0ed166d79 100644
--- a/src/libknot/packet/pkt.c
+++ b/src/libknot/packet/pkt.c
@@ -292,6 +292,9 @@ void knot_pkt_free(knot_pkt_t **pkt)
 		(*pkt)->mm.free((*pkt)->wire);
 	}
 
+	/* Free the OPT RR. */
+	knot_rrset_free(&(*pkt)->opt_rr, &(*pkt)->mm);
+
 	dbg_packet("Freeing packet structure\n");
 	(*pkt)->mm.free(*pkt);
 	*pkt = NULL;
@@ -789,41 +792,3 @@ int knot_pkt_parse_payload(knot_pkt_t *pkt, unsigned flags)
 
 	return KNOT_EOK;
 }
-
-/*----------------------------------------------------------------------------*/
-/* EDNS(0)-related functions                                                  */
-/*----------------------------------------------------------------------------*/
-
-int knot_pkt_write_opt(knot_pkt_t *pkt, knot_edns_params_t *edns, bool add_nsid)
-{
-	if (pkt == NULL) {
-		assert(0);
-		return KNOT_EINVAL;
-	}
-
-	/* OPT should be only in the AR. */
-	if (pkt->current != KNOT_ADDITIONAL) {
-		assert(0);
-		return KNOT_ENOTSUP;
-	}
-
-	/* Initialize an OPT RR. */
-	knot_rrset_t opt_rr;
-	int ret = knot_edns_init_from_params(&opt_rr, edns, add_nsid, &pkt->mm);
-	if (ret != KNOT_EOK) {
-		assert(0);
-		return ret;
-	}
-
-	/* Write it to the wire. */
-	ret = knot_pkt_put(pkt, COMPR_HINT_NONE, &opt_rr, 0);
-	if (ret != KNOT_EOK) {
-		assert(0);
-		knot_rrset_clear(&opt_rr, &pkt->mm);
-		return ret;
-	}
-
-	/* Store for compatibility with the packet parsing code. */
-	pkt->opt_rr = &pkt->rr[pkt->rrset_count - 1];
-	return ret;
-}
diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h
index 578255116582d576ba37158a4f49059bb04863de..bd881f525fc3e00b267e035e57bb03e39df05727 100644
--- a/src/libknot/packet/pkt.h
+++ b/src/libknot/packet/pkt.h
@@ -230,34 +230,6 @@ int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
 const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt,
                                           knot_section_t section_id);
 
-/*
- * EDNS(0)-related API
- */
-
-/*!
- * \brief Write OPT RR to wireformat.
- *
- * If used properly (i.e. the OPT RR was added by knot_pkt_add_opt()), there
- * should be space reserved for the OPT RR, so it does not matter when during
- * Additional processing this function is called. (Well, it should not be used
- * after signing the packet with TSIG, of course.)
- *
- * \note This function should be called only after the Additional section of
- *       packet was started.
- *
- * \param pkt Packet in which the OPT RR should be written.
- *
- * \retval KNOT_EOK if successful.
- * \retval KNOT_EINVAL if the parameter is NULL.
- * \retval KNOT_ENOTSUP if the Additional section of the packet was not yet
- *                      started.
- * \retval KNOT_ESPACE if the RR did not fit in. (Should not happen as the
- *                     packet should have reserved space for the OPT RR when it
- *                     was added.
- */
-int knot_pkt_write_opt(knot_pkt_t *pkt, knot_edns_params_t *edns, bool add_nsid);
-
-
 /*
  * Packet parsing API.
  */
@@ -316,39 +288,21 @@ int knot_pkt_parse_section(knot_pkt_t *pkt, unsigned flags);
 int knot_pkt_parse_payload(knot_pkt_t *pkt, unsigned flags);
 
 /*!
- * \brief Checks if EDNS is supported (i.e. has EDNS VERSION != UNSUPPORTED).
+ * \brief Checks if there is an OPT RR in the packet.
  */
-static inline bool knot_pkt_have_edns(const knot_pkt_t *pkt)
+static inline bool knot_pkt_has_edns(const knot_pkt_t *pkt)
 {
-	return pkt && (pkt->opt_rr != NULL);
+	return pkt != NULL && pkt->opt_rr != NULL;
 }
 
 /*!
  * \brief Checks if TSIG is present.
  */
-static inline bool knot_pkt_have_tsig(const knot_pkt_t *pkt)
+static inline bool knot_pkt_has_tsig(const knot_pkt_t *pkt)
 {
 	return pkt && pkt->tsig_rr;
 }
 
-/*!
- * \brief Checks if DNSSEC was requested (i.e. the DO bit was set).
- */
-static inline bool knot_pkt_have_dnssec(const knot_pkt_t *pkt)
-{
-	return knot_pkt_have_edns(pkt) && knot_edns_do(pkt->opt_rr);
-}
-
-/*!
- * \brief Checks if NSID was requested (i.e. the NSID option was
- *        present in the query OPT RR).
- */
-static inline bool knot_pkt_have_nsid(const knot_pkt_t *pkt)
-{
-	return knot_pkt_have_edns(pkt)
-	       && knot_edns_has_option(pkt->opt_rr, KNOT_EDNS_OPTION_NSID);
-}
-
 #endif /* _KNOT_PACKET_H_ */
 
 /*! @} */
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
index fb8342570da9342bbc99aa7879f24108f08f3060..51521115886578efb57f297a19e9eb7ced77f734 100644
--- a/src/utils/common/exec.c
+++ b/src/utils/common/exec.c
@@ -101,7 +101,7 @@ static void print_header(const knot_pkt_t *packet, const style_t *style)
 	uint16_t nscount = knot_wire_get_nscount(packet->wire);
 	uint16_t arcount = knot_wire_get_arcount(packet->wire);
 
-	if (knot_pkt_have_tsig(packet)) {
+	if (knot_pkt_has_tsig(packet)) {
 		arcount++;
 	}
 
@@ -475,7 +475,7 @@ void print_data_xfr(const knot_pkt_t *packet,
 		print_section_full(answers->rr, answers->count, style, true);
 
 		// Print TSIG record.
-		if (style->show_tsig && knot_pkt_have_tsig(packet)) {
+		if (style->show_tsig && knot_pkt_has_tsig(packet)) {
 			print_section_full(packet->tsig_rr, 1, style, false);
 		}
 		break;
@@ -531,7 +531,7 @@ void print_packet(const knot_pkt_t *packet,
 
 	// Disable additionals printing if there are no other records.
 	// OPT record may be placed anywhere within additionals!
-	if (knot_pkt_have_edns(packet) && arcount == 1) {
+	if (knot_pkt_has_edns(packet) && arcount == 1) {
 		arcount = 0;
 	}
 
@@ -541,7 +541,7 @@ void print_packet(const knot_pkt_t *packet,
 	}
 
 	// Print EDNS section.
-	if (style->show_edns && knot_pkt_have_edns(packet)) {
+	if (style->show_edns && knot_pkt_has_edns(packet)) {
 		printf("\n;; EDNS PSEUDOSECTION:\n;; ");
 		print_section_opt(packet->opt_rr);
 	}
@@ -613,7 +613,7 @@ void print_packet(const knot_pkt_t *packet,
 	}
 
 	// Print TSIG section.
-	if (style->show_tsig && knot_pkt_have_tsig(packet)) {
+	if (style->show_tsig && knot_pkt_has_tsig(packet)) {
 		printf("\n;; TSIG PSEUDOSECTION:\n");
 		print_section_full(packet->tsig_rr, 1, style, false);
 	}
diff --git a/src/utils/dig/dig_exec.c b/src/utils/dig/dig_exec.c
index 66a130ee85a5fb7ca121492b0865b6dcd1b2c209..84304b84154ef8983118126ccb08283e01be579a 100644
--- a/src/utils/dig/dig_exec.c
+++ b/src/utils/dig/dig_exec.c
@@ -316,17 +316,31 @@ static knot_pkt_t* create_query_packet(const query_t *query)
 	// Create EDNS section if required.
 	if (query->udp_size > 0 || query->flags.do_flag || query->nsid ||
 	    query->edns > -1) {
-		knot_edns_params_t edns_params = {
-			.payload = max_size,
-			.version = query->edns > -1 ? query->edns : 0,
-			.nsid_len = 0,
-			.nsid = NULL,
-			.flags = query->flags.do_flag ? KNOT_EDNS_FLAG_DO : 0
-		};
+		packet->opt_rr = knot_edns_new(max_size, 0,
+		                               query->edns > -1
+		                                 ? query->edns : 0,
+		                               query->flags.do_flag
+		                                 ? KNOT_EDNS_FLAG_DO : 0, NULL);
+		if (packet->opt_rr == NULL) {
+			ERR("can't set up EDNS section\n");
+			knot_pkt_free(&packet);
+			return NULL;
+		}
+
+		if (query->nsid) {
+			ret = knot_edns_add_option(packet->opt_rr,
+			                           KNOT_EDNS_OPTION_NSID, 0,
+			                           NULL, NULL);
+			if (ret != KNOT_EOK) {
+				ERR("can't set up EDNS section\n");
+				knot_pkt_free(&packet);
+				return NULL;
+			}
+		}
 
 		// Write prepared OPT to wire
 		knot_pkt_begin(packet, KNOT_ADDITIONAL);
-		ret = knot_pkt_write_opt(packet, &edns_params, query->nsid);
+		ret = knot_pkt_put(packet, COMPR_HINT_NONE, packet->opt_rr, 0);
 		if (ret != KNOT_EOK) {
 			ERR("can't set up EDNS section\n");
 			knot_pkt_free(&packet);
diff --git a/tests/edns.c b/tests/edns.c
index 79242edc989eda8185cf9203d5c8ae8ef40983f8..066126b9ae1e296815ab551465ad33f14f138f2c 100644
--- a/tests/edns.c
+++ b/tests/edns.c
@@ -331,6 +331,8 @@ int main(int argc, char *argv[])
 	plan(TEST_COUNT);
 	int done = 0;
 
+	/*! \todo [OPT] REWRITE */
+
 	knot_dname_t *root = knot_dname_from_str(".");
 	knot_rrset_t *opt_rr = knot_rrset_new(root, KNOT_RRTYPE_OPT,
 	                                      KNOT_CLASS_IN, NULL);