diff --git a/Knot.files b/Knot.files
index d35359273146f9dd89ef2ecb24835e24432ea811..e3de0a3b1defcb427a7c71682379b4a542a229cd 100644
--- a/Knot.files
+++ b/Knot.files
@@ -148,6 +148,8 @@ src/knot/conf/conf.c
 src/knot/conf/conf.h
 src/knot/conf/logconf.c
 src/knot/conf/logconf.h
+src/tests/common/acl_tests.c
+src/tests/common/acl_tests.h
 src/tests/common/da_tests.c
 src/tests/common/da_tests.h
 src/tests/common/events_tests.c
diff --git a/src/Makefile.am b/src/Makefile.am
index f3737548ad2bc52f787c830cdf901850e57d6518..f1902ef5fad32ea4e31cbfa6a12fe0b3e0eee6c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,6 +46,8 @@ knot_zcompile_SOURCES =			\
 	zcompile/parser-descriptor.c
 
 unittests_SOURCES =				\
+	tests/common/acl_tests.c		\
+	tests/common/acl_tests.h		\
 	tests/common/da_tests.c			\
 	tests/common/da_tests.h			\
 	tests/common/events_tests.c		\
diff --git a/src/common/acl.c b/src/common/acl.c
index ae51fd53cd3a9ce8afe94e6a882b8308188a97f1..a2a5059569e7ff03611264769bbd2516371f56fa 100644
--- a/src/common/acl.c
+++ b/src/common/acl.c
@@ -34,7 +34,10 @@ static int acl_compare(void *k1, void *k2)
 
 		/* Compare ports on address match. */
 		ldiff = ntohs(a1->addr4.sin_port) - ntohs(a2->addr4.sin_port);
-		return ldiff < 0 ? -1 : 1;
+		if (ldiff != 0) {
+			return ldiff < 0 ? -1 : 1;
+		}
+		return 0;
 	}
 
 	/* IPv6 matching. */
@@ -63,7 +66,10 @@ static int acl_compare(void *k1, void *k2)
 
 		/* Compare ports on address match. */
 		ldiff = ntohs(a1->addr6.sin6_port) - ntohs(a2->addr6.sin6_port);
-		return ldiff < 0 ? -1 : 1;
+		if (ldiff != 0) {
+			return ldiff < 0 ? -1 : 1;
+		}
+		return 0;
 	}
 #endif
 
diff --git a/src/common/acl.h b/src/common/acl.h
index 8229547027accb9189ec38d5d7f4bf83b89333e1..8f383a01e619ab9a786486665f8c0e1266e8bae6 100644
--- a/src/common/acl.h
+++ b/src/common/acl.h
@@ -96,6 +96,10 @@ int acl_truncate(acl_t *acl);
  * \retval ACL name.
  */
 static inline const char* acl_name(acl_t *acl) {
+	if (!acl) {
+		return 0;
+	}
+
 	return acl->name;
 }
 
diff --git a/src/knot/server/name-server.c b/src/knot/server/name-server.c
index c9ca63ca7e4c0db3f9fbceb6484201116e9b593a..d39e210f707e0fd866b50dd80bf0de917ad7f5ce 100644
--- a/src/knot/server/name-server.c
+++ b/src/knot/server/name-server.c
@@ -2007,7 +2007,7 @@ DEBUG_NS(
 	if (acl_match(zone->acl.xfr_out, &xfr->from) == ACL_DENY) {
 		/*! \todo What should we return here? */
 		debug_ns("Request for AXFR OUT is not authorized.\n");
-		dnslib_response_set_rcode(xfr->response, DNSLIB_RCODE_NOTAUTH);
+		dnslib_response_set_rcode(xfr->response, DNSLIB_RCODE_REFUSED);
 		return KNOT_EOK;
 	} else {
 		debug_ns("Authorized AXFR OUT request.\n");
diff --git a/src/tests/common/acl_tests.c b/src/tests/common/acl_tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5a7cd8e03e03cce0cf7f1527ca42bf11cee3ac3
--- /dev/null
+++ b/src/tests/common/acl_tests.c
@@ -0,0 +1,93 @@
+#include "tests/common/acl_tests.h"
+#include "common/sockaddr.h"
+#include "common/acl.h"
+
+static int acl_tests_count(int argc, char *argv[]);
+static int acl_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api acl_tests_api = {
+	"ACL",             //! Unit name
+	&acl_tests_count,  //! Count scheduled tests
+	&acl_tests_run     //! Run scheduled tests
+};
+
+static int acl_tests_count(int argc, char *argv[])
+{
+	return 13;
+}
+
+static int acl_tests_run(int argc, char *argv[])
+{
+	// 1. Create an ACL
+	acl_t *acl = acl_new(ACL_DENY, "simple ACL");
+	ok(acl != 0, "acl: new");
+
+	// 2. Create IPv4 address
+	sockaddr_t test_v4;
+	int ret = sockaddr_set(&test_v4, AF_INET, "127.0.0.1", 12345);
+	ok(ret > 0, "acl: new IPv4 address");
+
+	// 3. Create IPv6 address
+	sockaddr_t test_v6;
+	ret = sockaddr_set(&test_v6, AF_INET6, "::1", 54321);
+	ok(ret > 0, "acl: new IPv6 address");
+
+	// 4. Create simple IPv4 rule
+	ret = acl_create(acl, &test_v4, ACL_ACCEPT);
+	ok(ret == ACL_ACCEPT, "acl: inserted IPv4 rule");
+
+	// 5. Create simple IPv6 rule
+	ret = acl_create(acl, &test_v6, ACL_ACCEPT);
+	ok(ret == ACL_ACCEPT, "acl: inserted IPv6 rule");
+
+	// 6. Create simple IPv4 'any port' rule
+	sockaddr_t test_v4a;
+	sockaddr_set(&test_v4a, AF_INET, "20.20.20.20", 0);
+	ret = acl_create(acl, &test_v4a, ACL_ACCEPT);
+	ok(ret == ACL_ACCEPT, "acl: inserted IPv4 'any port' rule");
+
+	// 7. Attempt to match unmatching address
+	sockaddr_t unmatch_v4;
+	sockaddr_set(&unmatch_v4, AF_INET, "10.10.10.10", 24424);
+	ret = acl_match(acl, &unmatch_v4);
+	ok(ret == ACL_DENY, "acl: matching non-existing address");
+
+	// 8. Attempt to match unmatching IPv6 address
+	sockaddr_t unmatch_v6;
+	sockaddr_set(&unmatch_v6, AF_INET6, "2001:db8::1428:57ab", 24424);
+	ret = acl_match(acl, &unmatch_v6);
+	ok(ret == ACL_DENY, "acl: matching non-existing IPv6 address");
+
+	// 9. Attempt to match matching address
+	ret = acl_match(acl, &test_v4);
+	ok(ret == ACL_ACCEPT, "acl: matching existing address");
+
+	// 10. Attempt to match matching address
+	ret = acl_match(acl, &test_v6);
+	ok(ret == ACL_ACCEPT, "acl: matching existing IPv6 address");
+
+	// 11. Attempt to match matching 'any port' address
+	sockaddr_t match_v4a;
+	sockaddr_set(&match_v4a, AF_INET, "20.20.20.20", 24424);
+	ret = acl_match(acl, &match_v4a);
+	ok(ret == ACL_ACCEPT, "acl: matching existing IPv4 'any port' address");
+
+	// 12. Attempt to match matching address without matching port
+	sockaddr_set(&unmatch_v4, AF_INET, "127.0.0.1", 54321);
+	ret = acl_match(acl, &unmatch_v4);
+	ok(ret == ACL_DENY, "acl: matching address without matching port");
+
+	// 13. Invalid parameters
+	lives_ok({
+		acl_delete(0);
+		acl_create(0, 0, ACL_ERROR);
+		acl_match(0, 0);
+		acl_truncate(0);
+		acl_name(0);
+	}, "acl: won't crash with NULL parameters");
+
+	// Return
+	return 0;
+}
diff --git a/src/tests/common/acl_tests.h b/src/tests/common/acl_tests.h
new file mode 100644
index 0000000000000000000000000000000000000000..18c73cd0e6f748491994e6ac657ce1a4fb770794
--- /dev/null
+++ b/src/tests/common/acl_tests.h
@@ -0,0 +1,9 @@
+#ifndef _KNOT_ACL_TESTS_H_
+#define _KNOT_ACL_TESTS_H_
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api acl_tests_api;
+
+#endif /* _KNOT_ACL_TESTS_H_ */
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
index f5e7777814a104bee3d07698a30f9ea86880335e..60c72ef98e06f07cea0a3a361a3b4b065eb2caa5 100644
--- a/src/tests/unittests_main.c
+++ b/src/tests/unittests_main.c
@@ -7,6 +7,7 @@
 #include "tests/common/skiplist_tests.h"
 #include "tests/common/events_tests.h"
 #include "tests/common/da_tests.h"
+#include "tests/common/acl_tests.h"
 #include "tests/knot/dthreads_tests.h"
 #include "tests/knot/server_tests.h"
 #include "tests/knot/conf_tests.h"
@@ -25,6 +26,7 @@ int main(int argc, char *argv[])
 		&dthreads_tests_api, //! DThreads testing unit
 		&events_tests_api,   //! Events testing unit
 		&da_tests_api,       //! Dynamic array unit
+		&acl_tests_api,      //! ACLs
 
 		/* Server parts. */
 		&conf_tests_api,     //! Configuration parser tests