diff --git a/NEWS b/NEWS
index 3cfd78dba21c45d9e73980e4a8f01db7d083d089..8db7980fd8409ed77491e5bc3d48c67b3f5efff3 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Knot Resolver 4.x.y (2019-0m-dd)
 Improvements
 ------------
 - DNS-over-HTTPS: answers include `access-control-allow-origin: *` (!823)
+- support named AF_UNIX stream sockets for the http module (again)
 
 Bugfixes
 --------
diff --git a/daemon/io.c b/daemon/io.c
index 296804ac2dddd023eed73061a15e4b18ffe1e5ae..6a7ae9589e13822ac3639dd8988bb1f915c3f079 100644
--- a/daemon/io.c
+++ b/daemon/io.c
@@ -373,7 +373,7 @@ int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bo
 #ifdef TCP_DEFER_ACCEPT
 	val = KR_CONN_RTT_MAX/1000;
 	if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val))) {
-		kr_log_error("[ io ] tcp_bind (defer_accept): %s\n", strerror(errno));
+		kr_log_error("[ io ] listen TCP (defer_accept): %s\n", strerror(errno));
 	}
 #endif
 
@@ -390,7 +390,7 @@ int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bo
 	val = 1; /* Accepts on/off */
 	#endif
 	if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &val, sizeof(val))) {
-		kr_log_error("[ io ] tcp_bind (fastopen): %s\n", strerror(errno));
+		kr_log_error("[ io ] listen TCP (fastopen): %s\n", strerror(errno));
 	}
 #endif
 
diff --git a/daemon/main.c b/daemon/main.c
index 8f0e84c7bc1171be013adb3a81cb61bbf0e7c4e0..54b8d83c1b99bcd25270e98be2a13e9f75782f63 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -586,28 +586,42 @@ static int parse_args(int argc, char **argv, struct args *args)
 }
 
 /** Just convert addresses to file-descriptors; clear *addrs on success.
+ * @note AF_UNIX is supported (starting with '/').
  * @return zero or exit code for main()
  */
 static int bind_sockets(addr_array_t *addrs, bool tls, flagged_fd_array_t *fds)
 {
+	bool has_error = false;
 	for (size_t i = 0; i < addrs->len; ++i) {
+		/* Get port and separate address string. */
 		uint16_t port = tls ? KR_DNS_TLS_PORT : KR_DNS_PORT;
-		char addr_str[INET6_ADDRSTRLEN + 1];
-		int ret = kr_straddr_split(addrs->at[i], addr_str, &port);
+		char addr_buf[INET6_ADDRSTRLEN + 1];
+		int ret;
+		const char *addr_str;
+		const int family = kr_straddr_family(addrs->at[i]);
+		if (family == AF_UNIX) {
+			ret = 0;
+			addr_str = addrs->at[i];
+		} else { /* internet socket (or garbage) */
+			ret = kr_straddr_split(addrs->at[i], addr_buf, &port);
+			addr_str = addr_buf;
+		}
+		/* Get sockaddr. */
 		struct sockaddr *sa = NULL;
 		if (ret == 0) {
 			sa = kr_straddr_socket(addr_str, port, NULL);
 			if (!sa) ret = kr_error(EINVAL); /* could be ENOMEM but unlikely */
 		}
 		flagged_fd_t ffd = { .flags = { .tls = tls } };
-		if (ret == 0 && !tls) {
+		if (ret == 0 && !tls && family != AF_UNIX) {
+			/* AF_UNIX can do SOCK_DGRAM, but let's not support that *here*. */
 			ffd.fd = io_bind(sa, SOCK_DGRAM);
 			if (ffd.fd < 0)
 				ret = ffd.fd;
 			else if (array_push(*fds, ffd) < 0)
 				ret = kr_error(ENOMEM);
 		}
-		if (ret == 0) { /* common for TCP and TLS */
+		if (ret == 0) { /* common for TCP and TLS, including AF_UNIX cases */
 			ffd.fd = io_bind(sa, SOCK_STREAM);
 			if (ffd.fd < 0)
 				ret = ffd.fd;
@@ -616,13 +630,13 @@ static int bind_sockets(addr_array_t *addrs, bool tls, flagged_fd_array_t *fds)
 		}
 		free(sa);
 		if (ret != 0) {
-			kr_log_error("[system] bind to '%s' %s%s\n",
-				addrs->at[i], tls ? "(TLS) " : "", kr_strerror(ret));
-			return EXIT_FAILURE;
+			kr_log_error("[system] bind to '%s'%s: %s\n",
+				addrs->at[i], tls ? " (TLS)" : "", kr_strerror(ret));
+			has_error = true;
 		}
 	}
 	array_clear(*addrs);
-	return kr_ok();
+	return has_error ? EXIT_FAILURE : kr_ok();
 }
 
 static int start_listening(struct network *net, flagged_fd_array_t *fds) {
diff --git a/daemon/network.c b/daemon/network.c
index d1a87035022d47806f5de8cf5a3d5df0da2fa915..509fc40acab35a7f448f16e5a125af8e74ad119d 100644
--- a/daemon/network.c
+++ b/daemon/network.c
@@ -14,13 +14,16 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include <unistd.h>
-#include <assert.h>
-#include "daemon/bindings/impl.h"
 #include "daemon/network.h"
-#include "daemon/worker.h"
+
+#include "daemon/bindings/impl.h"
 #include "daemon/io.h"
 #include "daemon/tls.h"
+#include "daemon/worker.h"
+
+#include <assert.h>
+#include <sys/un.h>
+#include <unistd.h>
 
 void network_init(struct network *net, uv_loop_t *loop, int tcp_backlog)
 {
@@ -137,6 +140,19 @@ static void endpoint_close_lua_cb(struct network *net, struct endpoint *ep)
 static void endpoint_close(struct network *net, struct endpoint *ep, bool force)
 {
 	assert(!ep->handle != !ep->flags.kind);
+	if (ep->family == AF_UNIX) { /* The FS name would be left behind. */
+		/* Extract local address for this socket. */
+		struct sockaddr_un sa;
+		sa.sun_path[0] = '\0'; /*< probably only for lint:scan-build */
+		socklen_t addr_len = sizeof(sa);
+		if (getsockname(ep->fd, (struct sockaddr *)&sa, &addr_len)
+		    || unlink(sa.sun_path)) {
+			kr_log_error("error (ignored) when closing unix socket (fd = %d): %s\n",
+					ep->fd, strerror(errno));
+			return;
+		}
+	}
+
 	if (ep->flags.kind) { /* Special endpoint. */
 		if (ep->engaged) {
 			endpoint_close_lua_cb(net, ep);
@@ -260,6 +276,16 @@ static int open_endpoint(struct network *net, struct endpoint *ep,
 		/* .engaged seems not really meaningful with .kind == NULL, but... */
 	}
 
+	if (ep->family == AF_UNIX) {
+		/* Some parts of connection handling would need more work,
+		 * so let's support AF_UNIX only with .kind != NULL for now. */
+		kr_log_error("[system] AF_UNIX only supported with set { kind = '...' }\n");
+		return kr_error(EAFNOSUPPORT);
+		/*
+		uv_pipe_t *ep_handle = malloc(sizeof(uv_pipe_t));
+		*/
+	}
+
 	if (ep->flags.sock_type == SOCK_DGRAM) {
 		if (ep->flags.tls) {
 			assert(!EINVAL);
@@ -310,7 +336,6 @@ static struct endpoint * endpoint_get(struct network *net, const char *addr,
 static int create_endpoint(struct network *net, const char *addr_str,
 				struct endpoint *ep, const struct sockaddr *sa)
 {
-	/* Bind interfaces */
 	int ret = open_endpoint(net, ep, sa, addr_str);
 	if (ret == 0) {
 		ret = insert_endpoint(net, addr_str, ep);
@@ -351,16 +376,28 @@ int network_listen_fd(struct network *net, int fd, endpoint_flags_t flags)
 		.fd = fd,
 	};
 	/* Extract address string and port. */
-	char addr_str[INET6_ADDRSTRLEN]; /* https://tools.ietf.org/html/rfc4291 */
-	if (ss.ss_family == AF_INET) {
-		uv_ip4_name((const struct sockaddr_in*)&ss, addr_str, sizeof(addr_str));
+	char addr_buf[INET6_ADDRSTRLEN]; /* https://tools.ietf.org/html/rfc4291 */
+	const char *addr_str;
+	switch (ep.family) {
+	case AF_INET:
+		ret = uv_ip4_name((const struct sockaddr_in*)&ss, addr_buf, sizeof(addr_buf));
+		addr_str = addr_buf;
 		ep.port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
-	} else if (ss.ss_family == AF_INET6) {
-		uv_ip6_name((const struct sockaddr_in6*)&ss, addr_str, sizeof(addr_str));
+		break;
+	case AF_INET6:
+		ret = uv_ip6_name((const struct sockaddr_in6*)&ss, addr_buf, sizeof(addr_buf));
+		addr_str = addr_buf;
 		ep.port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
-	} else {
-		return kr_error(EAFNOSUPPORT);
+		break;
+	case AF_UNIX:
+		/* No SOCK_DGRAM with AF_UNIX support, at least for now. */
+		ret = flags.sock_type == SOCK_STREAM ? kr_ok() : kr_error(EAFNOSUPPORT);
+		addr_str = ((struct sockaddr_un *)&ss)->sun_path;
+		break;
+	default:
+		ret = kr_error(EAFNOSUPPORT);
 	}
+	if (ret) return ret;
 
 	/* always create endpoint for supervisor supplied fd
 	 * even if addr+port is not unique */
@@ -379,23 +416,19 @@ int network_listen(struct network *net, const char *addr, uint16_t port,
 	}
 
 	/* Parse address. */
-	int ret = 0;
-	union inaddr sa;
-	if (strchr(addr, ':') != NULL) {
-		ret = uv_ip6_addr(addr, port, &sa.ip6);
-	} else {
-		ret = uv_ip4_addr(addr, port, &sa.ip4);
-	}
-	if (ret != 0) {
-		return ret;
+	const struct sockaddr *sa = kr_straddr_socket(addr, port, NULL);
+	if (!sa) {
+		return kr_error(EINVAL);
 	}
 	struct endpoint ep = {
 		.flags = flags,
 		.fd = -1,
 		.port = port,
-		.family = sa.ip.sa_family,
+		.family = sa->sa_family,
 	};
-	return create_endpoint(net, addr, &ep, &sa.ip);
+	int ret = create_endpoint(net, addr, &ep, sa);
+	free_const(sa);
+	return ret;
 }
 
 int network_close(struct network *net, const char *addr, int port)
diff --git a/daemon/network.h b/daemon/network.h
index 4832f4ceb1af05fbce22ce95c7f44fa40b992757..219011aa25269cd725a8a6edbb8552ac8136710f 100644
--- a/daemon/network.h
+++ b/daemon/network.h
@@ -49,11 +49,12 @@ static inline bool endpoint_flags_eq(endpoint_flags_t f1, endpoint_flags_t f2)
  * There are two types: normal have handle, special have flags.kind (and never both).
  *
  * LATER: .family might be unexpected for IPv4-in-IPv6 addresses.
+ * ATM AF_UNIX is only supported with flags.kind != NULL
  */
 struct endpoint {
 	uv_handle_t *handle; /**< uv_udp_t or uv_tcp_t; NULL in case flags.kind != NULL */
 	int fd;              /**< POSIX file-descriptor; always used. */
-	int family;          /**< AF_INET or AF_INET6 or (in future) AF_UNIX */
+	int family;          /**< AF_INET or AF_INET6 or AF_UNIX */
 	uint16_t port;       /**< TCP/UDP port.  Meaningless with AF_UNIX. */
 	bool engaged;        /**< to some module or internally */
 	endpoint_flags_t flags;
diff --git a/lib/utils.c b/lib/utils.c
index 603758e9dbf73e9ed2f4f44dac392ead50bba88b..780805ea831b0d0eb21f8bcbf79c018d0727c06c 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -14,31 +14,33 @@
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include <stdarg.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <sys/time.h>
-#include <contrib/cleanup.h>
-#include <contrib/ccan/asprintf/asprintf.h>
-#include <ucw/mempool.h>
-#include <gnutls/gnutls.h>
-#include <libknot/descriptor.h>
-#include <libknot/dname.h>
-#include <libknot/rrtype/rrsig.h>
-#include <libknot/rrset-dump.h>
-#include <libknot/version.h>
-#include <uv.h>
+#include "lib/utils.h"
 
+#include "contrib/ccan/asprintf/asprintf.h"
+#include "contrib/cleanup.h"
+#include "contrib/ucw/mempool.h"
 #include "kresconfig.h"
 #include "lib/defines.h"
-#include "lib/utils.h"
 #include "lib/generic/array.h"
-#include "lib/nsrep.h"
 #include "lib/module.h"
+#include "lib/nsrep.h"
 #include "lib/resolve.h"
 
+#include <gnutls/gnutls.h>
+#include <libknot/descriptor.h>
+#include <libknot/dname.h>
+#include <libknot/rrset-dump.h>
+#include <libknot/rrtype/rrsig.h>
+#include <libknot/version.h>
+#include <uv.h>
+
+#include <arpa/inet.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/un.h>
 
 /* Always compile-in log symbols, even if disabled. */
 #undef kr_verbose_status
@@ -343,6 +345,7 @@ int kr_sockaddr_len(const struct sockaddr *addr)
 	switch (addr->sa_family) {
 	case AF_INET:  return sizeof(struct sockaddr_in);
 	case AF_INET6: return sizeof(struct sockaddr_in6);
+	case AF_UNIX:  return sizeof(struct sockaddr_un);
 	default:       return kr_error(EINVAL);
 	}
 }
@@ -440,6 +443,9 @@ int kr_straddr_family(const char *addr)
 	if (!addr) {
 		return kr_error(EINVAL);
 	}
+	if (addr[0] == '/') {
+		return AF_UNIX;
+	}
 	if (strchr(addr, ':')) {
 		return AF_INET6;
 	}
@@ -476,6 +482,17 @@ struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool)
 			return NULL;
 		}
 	}
+	case AF_UNIX: {
+		struct sockaddr_un *res;
+		const size_t alen = strlen(addr) + 1;
+		if (alen > sizeof(res->sun_path)) {
+			return NULL;
+		}
+		res = mm_alloc(pool, sizeof(*res));
+		res->sun_family = AF_UNIX;
+		memcpy(res->sun_path, addr, alen);
+		return (struct sockaddr *)res;
+	}
 	default:
 		assert(!EINVAL);
 		return NULL;
diff --git a/lib/utils.h b/lib/utils.h
index 4d8530aa7dd3cca05c5d2042bb5af14b7474749f..5b376c6e81c08779736fe8a4eee6df8a6639e9c0 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -346,7 +346,8 @@ int kr_straddr_family(const char *addr);
 KR_EXPORT KR_CONST
 int kr_family_len(int family);
 
-/** Create a sockaddr* from string+port representation (also accepts IPv6 link-local). */
+/** Create a sockaddr* from string+port representation.
+ * Also accepts IPv6 link-local and AF_UNIX starting with "/" (ignoring port) */
 KR_EXPORT
 struct sockaddr * kr_straddr_socket(const char *addr, int port, knot_mm_t *pool);
 
@@ -362,6 +363,7 @@ int kr_straddr_subnet(void *dst, const char *addr);
  * \param port[out] written in case it's specified in instr
  * \return error code
  * \note Typically you follow this by kr_straddr_socket().
+ * \note Only internet addresses are supported, i.e. no AF_UNIX sockets.
  */
 KR_EXPORT
 int kr_straddr_split(const char *instr, char ipaddr[static restrict (INET6_ADDRSTRLEN + 1)],