diff --git a/lib/core/context.c b/lib/core/context.c
index e3ae08bbbaf3f2b5ebf3d77f8ac095c46c2f80f7..70d33d47e181a2937c81c1daa86265c709c25a19 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -528,9 +528,9 @@ lws_create_vhost(struct lws_context *context,
 		lws_free(lwsp);
 	}
 
-	vh->same_vh_protocol_list = (struct lws **)
-			lws_zalloc(sizeof(struct lws *) * vh->count_protocols,
-				   "same vh list");
+	vh->same_vh_protocol_heads = (struct lws_dll_lws *)
+			lws_zalloc(sizeof(struct lws_dll_lws) *
+				   vh->count_protocols, "same vh list");
 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	vh->http.mount_list = info->mounts;
 #endif
@@ -1449,7 +1449,7 @@ __lws_vhost_destroy2(struct lws_vhost *vh)
 	if (vh->protocol_vh_privs)
 		lws_free(vh->protocol_vh_privs);
 	lws_ssl_SSL_CTX_destroy(vh);
-	lws_free(vh->same_vh_protocol_list);
+	lws_free(vh->same_vh_protocol_heads);
 
 	if (context->plugin_list ||
 	    (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
diff --git a/lib/core/pollfd.c b/lib/core/pollfd.c
index f2deea9ffbb6455aaba696d81a941fe3011bbda5..2568b046a1959b67dc918d7ef38e45bd76da0a63 100644
--- a/lib/core/pollfd.c
+++ b/lib/core/pollfd.c
@@ -472,25 +472,13 @@ lws_callback_on_writable(struct lws *wsi)
 void
 lws_same_vh_protocol_insert(struct lws *wsi, int n)
 {
-	if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) {
-		lws_same_vh_protocol_remove(wsi);
-		lwsl_info("Attempted to attach wsi twice to same vh prot\n");
-	}
-
 	lws_vhost_lock(wsi->vhost);
 
-	wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
-	/* old first guy is our next */
-	wsi->same_vh_protocol_next =  wsi->vhost->same_vh_protocol_list[n];
-	/* we become the new first guy */
-	wsi->vhost->same_vh_protocol_list[n] = wsi;
-
-	if (wsi->same_vh_protocol_next)
-		/* old first guy points back to us now */
-		wsi->same_vh_protocol_next->same_vh_protocol_prev =
-				&wsi->same_vh_protocol_next;
+	if (!lws_dll_is_null(&wsi->same_vh_protocol))
+		lws_dll_lws_remove(&wsi->same_vh_protocol);
 
-	wsi->on_same_vh_list = 1;
+	lws_dll_lws_add_front(&wsi->same_vh_protocol,
+			      &wsi->vhost->same_vh_protocol_heads[n]);
 
 	lws_vhost_unlock(wsi->vhost);
 }
@@ -498,38 +486,13 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
 void
 lws_same_vh_protocol_remove(struct lws *wsi)
 {
-	/*
-	 * detach ourselves from vh protocol list if we're on one
-	 * A -> B -> C
-	 * A -> C , or, B -> C, or A -> B
-	 *
-	 * OK to call on already-detached wsi
-	 */
-	lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
-
-	if (!wsi->vhost || !wsi->on_same_vh_list)
+	if (!wsi->vhost)
 		return;
 
 	lws_vhost_lock(wsi->vhost);
 
-	if (wsi->same_vh_protocol_prev) {
-		assert (*(wsi->same_vh_protocol_prev) == wsi);
-		lwsl_info("have prev %p, setting him to our next %p\n",
-			 wsi->same_vh_protocol_prev,
-			 wsi->same_vh_protocol_next);
-
-		/* guy who pointed to us should point to our next */
-		*(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next;
-	}
-
-	/* our next should point back to our prev */
-	if (wsi->same_vh_protocol_next)
-		wsi->same_vh_protocol_next->same_vh_protocol_prev =
-				wsi->same_vh_protocol_prev;
-
-	wsi->same_vh_protocol_prev = NULL;
-	wsi->same_vh_protocol_next = NULL;
-	wsi->on_same_vh_list = 0;
+	if (!lws_dll_is_null(&wsi->same_vh_protocol))
+		lws_dll_lws_remove(&wsi->same_vh_protocol);
 
 	lws_vhost_unlock(wsi->vhost);
 }
@@ -537,9 +500,10 @@ lws_same_vh_protocol_remove(struct lws *wsi)
 
 LWS_VISIBLE int
 lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
-				      const struct lws_protocols *protocol)
+				           const struct lws_protocols *protocol)
 {
 	struct lws *wsi;
+	int n;
 
 	if (protocol < vhost->protocols ||
 	    protocol >= (vhost->protocols + vhost->count_protocols)) {
@@ -550,18 +514,16 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
 		return -1;
 	}
 
-	wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols];
-	while (wsi) {
-		assert(wsi->protocol == protocol);
-		assert(*wsi->same_vh_protocol_prev == wsi);
-		if (wsi->same_vh_protocol_next)
-			assert(wsi->same_vh_protocol_next->
-					same_vh_protocol_prev ==
-					&wsi->same_vh_protocol_next);
+	n = protocol - vhost->protocols;
 
+	lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+				   vhost->same_vh_protocol_heads[n].next) {
+		wsi = lws_container_of(d, struct lws, same_vh_protocol);
+
+		assert(wsi->protocol == protocol);
 		lws_callback_on_writable(wsi);
-		wsi = wsi->same_vh_protocol_next;
-	}
+
+	} lws_end_foreach_dll_safe(d, d1);
 
 	return 0;
 }
diff --git a/lib/core/private.h b/lib/core/private.h
index c25dcc45f76170c0b45c40b27b0492f9fba9c968..8bf067421fa48bfdf0d0f1bc154edbd1830cfcd2 100644
--- a/lib/core/private.h
+++ b/lib/core/private.h
@@ -508,7 +508,7 @@ struct lws_vhost {
 	void **protocol_vh_privs;
 	const struct lws_protocol_vhost_options *pvo;
 	const struct lws_protocol_vhost_options *headers;
-	struct lws **same_vh_protocol_list;
+	struct lws_dll_lws *same_vh_protocol_heads;
 	struct lws_vhost *no_listener_vhost_list;
 #if !defined(LWS_NO_CLIENT)
 	struct lws_dll_lws dll_active_client_conns;
@@ -872,7 +872,7 @@ struct lws {
 	struct lws *sibling_list; /* subsequent children at same level */
 
 	const struct lws_protocols *protocol;
-	struct lws **same_vh_protocol_prev, *same_vh_protocol_next;
+	struct lws_dll_lws same_vh_protocol;
 
 	struct lws_dll_lws dll_timeout;
 	struct lws_dll_lws dll_hrtimer;
@@ -956,7 +956,6 @@ struct lws {
 	unsigned int seen_zero_length_recv:1;
 	unsigned int rxflow_will_be_applied:1;
 	unsigned int event_pipe:1;
-	unsigned int on_same_vh_list:1;
 	unsigned int handling_404:1;
 	unsigned int protocol_bind_balance:1;
 	unsigned int unix_skt:1;
diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c
index ad2d6f5dc92d62a3e6a7881379b7f6d40f0b91e0..8c4c04b2748ca422628f038282816653bdd72ffb 100644
--- a/lib/roles/ws/client-ws.c
+++ b/lib/roles/ws/client-ws.c
@@ -377,22 +377,7 @@ check_extensions:
 	 * X <-> pAn <-> pB
 	 */
 
-	lws_vhost_lock(wsi->vhost);
-
-	wsi->same_vh_protocol_prev = /* guy who points to us */
-		&wsi->vhost->same_vh_protocol_list[n];
-	wsi->same_vh_protocol_next = /* old first guy is our next */
-			wsi->vhost->same_vh_protocol_list[n];
-	/* we become the new first guy */
-	wsi->vhost->same_vh_protocol_list[n] = wsi;
-
-	if (wsi->same_vh_protocol_next)
-		/* old first guy points back to us now */
-		wsi->same_vh_protocol_next->same_vh_protocol_prev =
-				&wsi->same_vh_protocol_next;
-	wsi->on_same_vh_list = 1;
-
-	lws_vhost_unlock(wsi->vhost);
+	lws_same_vh_protocol_insert(wsi, n);
 
 #if !defined(LWS_WITHOUT_EXTENSIONS)
 	/* instantiate the accepted extensions */
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 52d369fed88470efb7a137c10095126a1aea0457..dd69ca3fe1ae3d3b560ba574bcfb46a352a54ab2 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -1401,9 +1401,12 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
 		lws_vhost_lock(vh);
 
 		for (n = 0; n < vh->count_protocols; n++) {
-			struct lws *wsi = vh->same_vh_protocol_list[n];
 
-			while (wsi) {
+			lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
+					  vh->same_vh_protocol_heads[n].next) {
+				struct lws *wsi = lws_container_of(d,
+						struct lws, same_vh_protocol);
+
 				if (lwsi_role_ws(wsi) &&
 				    !wsi->socket_is_permanently_unusable &&
 				    !wsi->ws->send_check_ping &&
@@ -1420,8 +1423,8 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
 					lws_callback_on_writable(wsi);
 					wsi->ws->time_next_ping_check = now;
 				}
-				wsi = wsi->same_vh_protocol_next;
-			}
+
+			} lws_end_foreach_dll_safe(d, d1);
 		}
 
 		lws_vhost_unlock(vh);