diff --git a/CMakeLists.txt b/CMakeLists.txt
index b545af31639f73f30e43708db555ac48d4e5f54b..79223f4a4b2f7a025d0729d003e4c204b66f70a7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@ set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF)
 if(WIN32)
 	set(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON)
 endif()
-set(LWS_ROLE_H1 1)
+
 set(LWS_ROLE_RAW 1)
 
 #
@@ -20,6 +20,7 @@ option(LWS_WITH_DISTRO_RECOMMENDED "Enable features recommended for distro packa
 #
 # Major individual features
 #
+option(LWS_ROLE_H1 "Compile with support for http/1 (needed for ws)" ON)
 option(LWS_ROLE_WS "Compile with support for websockets" ON)
 option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" OFF)
 option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
diff --git a/lib/context.c b/lib/context.c
index ad79b28d51bef385e3779d0a3d4f6d7b32163a12..6ae802951ccdd70fbffd07204d8ba0215a79e04c 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -560,7 +560,9 @@ lws_create_vhost(struct lws_context *context,
 	else
 		vh->name = info->vhost_name;
 
-	vh->error_document_404 = info->error_document_404;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	vh->http.error_document_404 = info->error_document_404;
+#endif
 
 	if (info->options & LWS_SERVER_OPTION_ONLY_RAW)
 		lwsl_info("%s set to only support RAW\n", vh->name);
@@ -684,8 +686,9 @@ lws_create_vhost(struct lws_context *context,
 	vh->same_vh_protocol_list = (struct lws **)
 			lws_zalloc(sizeof(struct lws *) * vh->count_protocols,
 				   "same vh list");
-
-	vh->mount_list = info->mounts;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	vh->http.mount_list = info->mounts;
+#endif
 
 #ifdef LWS_WITH_UNIX_SOCK
 	if (LWS_UNIX_SOCK_ENABLED(context)) {
@@ -736,22 +739,26 @@ lws_create_vhost(struct lws_context *context,
 	}
 
 	vh->listen_port = info->port;
-	vh->http_proxy_port = 0;
-	vh->http_proxy_address[0] = '\0';
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	vh->http.http_proxy_port = 0;
+	vh->http.http_proxy_address[0] = '\0';
+#endif
 #if defined(LWS_WITH_SOCKS5)
 	vh->socks_proxy_port = 0;
 	vh->socks_proxy_address[0] = '\0';
 #endif
 
 	/* either use proxy from info, or try get it from env var */
-
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	/* http proxy */
 	if (info->http_proxy_address) {
 		/* override for backwards compatibility */
 		if (info->http_proxy_port)
-			vh->http_proxy_port = info->http_proxy_port;
+			vh->http.http_proxy_port = info->http_proxy_port;
 		lws_set_proxy(vh, info->http_proxy_address);
-	} else {
+	} else
+#endif
+	{
 #ifdef LWS_HAVE_GETENV
 		p = getenv("http_proxy");
 		if (p)
@@ -1143,8 +1150,8 @@ lws_create_context(const struct lws_context_creation_info *info)
 		context->pt[n].context = context;
 #endif
 		context->pt[n].tid = n;
-		context->pt[n].ah_list = NULL;
-		context->pt[n].ah_pool_length = 0;
+		context->pt[n].http.ah_list = NULL;
+		context->pt[n].http.ah_pool_length = 0;
 
 		lws_pt_mutex_init(&context->pt[n]);
 	}
@@ -1716,8 +1723,8 @@ lws_context_destroy(struct lws_context *context)
 
 		lws_free_set_NULL(context->pt[n].serv_buf);
 
-		while (pt->ah_list)
-			_lws_destroy_ah(pt, pt->ah_list);
+		while (pt->http.ah_list)
+			_lws_destroy_ah(pt, pt->http.ah_list);
 	}
 	lws_plat_context_early_destroy(context);
 
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 0be9b13aed41acc0f8f296cb197578eaad32649b..f883f0aafdb2ba6ee175aab6f5558f11bf587c0f 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -104,13 +104,13 @@ __lws_free_wsi(struct lws *wsi)
 	if (wsi->vhost && wsi->vhost->lserv_wsi == wsi)
 		wsi->vhost->lserv_wsi = NULL;
 
-	ah = pt->ah_list;
+	ah = pt->http.ah_list;
 	while (ah) {
 		if (ah->in_use && ah->wsi == wsi) {
 			lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
 			ah->in_use = 0;
 			ah->wsi = NULL;
-			pt->ah_count_in_use--;
+			pt->http.ah_count_in_use--;
 			break;
 		}
 		ah = ah->next;
@@ -1740,24 +1740,25 @@ lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
 	} else
 		vhost->proxy_basic_auth_token[0] = '\0';
 
-	lws_strncpy(vhost->http_proxy_address, proxy,
-		    sizeof(vhost->http_proxy_address));
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	lws_strncpy(vhost->http.http_proxy_address, proxy,
+		    sizeof(vhost->http.http_proxy_address));
 
-	p = strchr(vhost->http_proxy_address, ':');
-	if (!p && !vhost->http_proxy_port) {
+	p = strchr(vhost->http.http_proxy_address, ':');
+	if (!p && !vhost->http.http_proxy_port) {
 		lwsl_err("http_proxy needs to be ads:port\n");
 
 		return -1;
 	} else {
 		if (p) {
 			*p = '\0';
-			vhost->http_proxy_port = atoi(p + 1);
+			vhost->http.http_proxy_port = atoi(p + 1);
 		}
 	}
 
-	lwsl_info(" Proxy %s:%u\n", vhost->http_proxy_address,
-			vhost->http_proxy_port);
-
+	lwsl_info(" Proxy %s:%u\n", vhost->http.http_proxy_address,
+			vhost->http.http_proxy_port);
+#endif
 	return 0;
 
 auth_too_long:
@@ -2991,9 +2992,9 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 			vh->conn_stats.h2_alpn,
 			vh->conn_stats.h2_subs
 	);
-
-	if (vh->mount_list) {
-		const struct lws_http_mount *m = vh->mount_list;
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	if (vh->http.mount_list) {
+		const struct lws_http_mount *m = vh->http.mount_list;
 
 		buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":[");
 		while (m) {
@@ -3024,7 +3025,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 		}
 		buf += lws_snprintf(buf, end - buf, "\n ]");
 	}
-
+#endif
 	if (vh->protocols) {
 		n = 0;
 		first = 1;
@@ -3117,8 +3118,8 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len,
 				"    \"ah_wait_list\":\"%d\"\n"
 				"    }",
 				pt->fds_count,
-				pt->ah_count_in_use,
-				pt->ah_wait_list_length);
+				pt->http.ah_count_in_use,
+				pt->http.ah_wait_list_length);
 	}
 
 	buf += lws_snprintf(buf, end - buf, "]");
@@ -3169,7 +3170,7 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len,
 #ifdef LWS_WITH_CGI
 	for (n = 0; n < context->count_threads; n++) {
 		pt = &context->pt[n];
-		pcgi = &pt->cgi_list;
+		pcgi = &pt->http.cgi_list;
 
 		while (*pcgi) {
 			pcgi = &(*pcgi)->cgi_list;
@@ -3343,17 +3344,17 @@ lws_stats_log_dump(struct lws_context *context)
 		lws_pt_lock(pt, __func__);
 
 		lwsl_notice("  AH in use / max:                  %d / %d\n",
-				pt->ah_count_in_use,
+				pt->http.ah_count_in_use,
 				context->max_http_header_pool);
 
-		wl = pt->ah_wait_list;
+		wl = pt->http.ah_wait_list;
 		while (wl) {
 			m++;
 			wl = wl->ah_wait_list;
 		}
 
 		lwsl_notice("  AH wait list count / actual:      %d / %d\n",
-				pt->ah_wait_list_length, m);
+				pt->http.ah_wait_list_length, m);
 
 		lws_pt_unlock(pt);
 	}
@@ -3384,9 +3385,13 @@ lws_stats_log_dump(struct lws_context *context)
 				if (!lws_plat_inet_ntop(df->af, df->addr, buf,
 							sizeof(buf) - 1))
 					strcpy(buf, "unknown");
-
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 				lwsl_notice("  peer %s: count wsi: %d, count ah: %d\n",
 					    buf, df->count_wsi, df->count_ah);
+#else
+				lwsl_notice("  peer %s: count wsi: %d\n",
+					    buf, df->count_wsi);
+#endif
 
 				if (!--m)
 					break;
diff --git a/lib/misc/peer-limits.c b/lib/misc/peer-limits.c
index 2fcb8bb83f0257e7009803f3b4fb0efc571199c2..741505ce2b6dcbe31f9a9437a11c76a3d99bc16a 100644
--- a/lib/misc/peer-limits.c
+++ b/lib/misc/peer-limits.c
@@ -210,7 +210,11 @@ lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
 	assert(peer->count_wsi);
 	peer->count_wsi--;
 
-	if (!peer->count_wsi && !peer->count_ah) {
+	if (!peer->count_wsi
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+			&& !peer->http.count_ah
+#endif
+	) {
 		/*
 		 * in order that we can accumulate peer activity correctly
 		 * allowing for periods when the peer has no connections,
@@ -226,13 +230,14 @@ lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
 	lws_context_unlock(context); /* ====================================> */
 }
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 int
 lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer)
 {
 	if (!peer)
 		return 0;
 
-	if (context->ip_limit_ah && peer->count_ah >= context->ip_limit_ah) {
+	if (context->ip_limit_ah && peer->http.count_ah >= context->ip_limit_ah) {
 		lwsl_info("peer reached ah limit %d, deferring\n",
 				context->ip_limit_ah);
 
@@ -249,8 +254,8 @@ lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
 		return;
 
 	lws_context_lock(context); /* <====================================== */
-	assert(peer->count_ah);
-	peer->count_ah--;
+	assert(peer->http.count_ah);
+	peer->http.count_ah--;
 	lws_context_unlock(context); /* ====================================> */
 }
-
+#endif
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index c488950e7c4e01b474141efca4b852083e5317ee..f514d1fb8e10c452b78ddf2e2637f7aef529f2b9 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -779,25 +779,26 @@ struct lws_context_per_thread {
 #endif
 	struct lws_pollfd *fds;
 	volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
+
 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
 	struct lws_pt_role_ws ws;
 #endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_pt_role_http http;
+#endif
+
 	struct lws_dll_lws dll_head_timeout;
 	struct lws_dll_lws dll_head_hrtimer;
 	struct lws_dll_lws dll_head_buflist; /* guys with pending rxflow */
 #if defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT)
 	struct lws_context *context;
 #endif
-#ifdef LWS_WITH_CGI
-	struct lws_cgi *cgi_list;
-#endif
-	void *http_header_data;
-	struct allocated_headers *ah_list;
-	struct lws *ah_wait_list;
+
+
 #if defined(LWS_HAVE_PTHREAD_H)
 	const char *last_lock_reason;
 #endif
-	int ah_wait_list_length;
+
 #if defined(LWS_WITH_TLS)
 	struct lws *pending_read_list; /* linked list */
 #endif
@@ -837,9 +838,7 @@ struct lws_context_per_thread {
 	volatile unsigned char foreign_spinlock;
 
 	unsigned int fds_count;
-	uint32_t ah_pool_length;
 
-	short ah_count_in_use;
 	unsigned char tid;
 	unsigned char lock_depth;
 #if LWS_MAX_SMP > 1
@@ -889,7 +888,6 @@ struct alpn_ctx {
 };
 
 struct lws_vhost {
-	char http_proxy_address[128];
 	char proxy_basic_auth_token[128];
 #if LWS_MAX_SMP > 1
 	pthread_mutex_t lock;
@@ -898,6 +896,9 @@ struct lws_vhost {
 #if defined(LWS_ROLE_H2)
 	struct lws_vhost_role_h2 h2;
 #endif
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_vhost_role_http http;
+#endif
 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
 	struct lws_vhost_role_ws ws;
 #endif
@@ -913,7 +914,7 @@ struct lws_vhost {
 	struct lws_conn_stats conn_stats;
 	struct lws_context *context;
 	struct lws_vhost *vhost_next;
-	const struct lws_http_mount *mount_list;
+
 	struct lws *lserv_wsi;
 	const char *name;
 	const char *iface;
@@ -931,7 +932,7 @@ struct lws_vhost {
 #if !defined(LWS_NO_CLIENT)
 	struct lws_dll_lws dll_active_client_conns;
 #endif
-	const char *error_document_404;
+
 	const char *alpn;
 #if defined(LWS_WITH_TLS)
 	lws_tls_ctx *ssl_ctx;
@@ -948,7 +949,7 @@ struct lws_vhost {
 	void *user;
 
 	int listen_port;
-	unsigned int http_proxy_port;
+
 #if defined(LWS_WITH_SOCKS5)
 	unsigned int socks_proxy_port;
 #endif
@@ -1005,10 +1006,11 @@ struct lws_peer {
 	uint8_t addr[32];
 	uint32_t hash;
 	uint32_t count_wsi;
-	uint32_t count_ah;
-
 	uint32_t total_wsi;
-	uint32_t total_ah;
+
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	struct lws_peer_role_http http;
+#endif
 
 	uint8_t af;
 };
@@ -1420,8 +1422,7 @@ struct lws {
 #if defined(LWS_WITH_PEER_LIMITS)
 	struct lws_peer *peer;
 #endif
-	struct allocated_headers *ah;
-	struct lws *ah_wait_list;
+
 	struct lws_udp *udp;
 #ifndef LWS_NO_CLIENT
 	struct client_info_stash *stash;
diff --git a/lib/roles/cgi/cgi-server.c b/lib/roles/cgi/cgi-server.c
index 8863f0ec9782bce0eabfa925818042caaacd5c37..b5c1d19bf6c93d3cfa313a5d7b53bb26bc6112ae 100644
--- a/lib/roles/cgi/cgi-server.c
+++ b/lib/roles/cgi/cgi-server.c
@@ -188,8 +188,8 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 
 	/* add us to the pt list of active cgis */
 	lwsl_debug("%s: adding cgi %p to list\n", __func__, wsi->cgi);
-	cgi->cgi_list = pt->cgi_list;
-	pt->cgi_list = cgi;
+	cgi->cgi_list = pt->http.cgi_list;
+	pt->http.cgi_list = cgi;
 
 	sum += lws_snprintf(sum, sumend - sum, "%s ", exec_array[0]);
 
@@ -199,7 +199,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 
 	if (lws_is_ssl(wsi))
 		env_array[n++] = "HTTPS=ON";
-	if (wsi->ah) {
+	if (wsi->http.ah) {
 		static const unsigned char meths[] = {
 			WSI_TOKEN_GET_URI,
 			WSI_TOKEN_POST_URI,
@@ -442,7 +442,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 
 bail3:
 	/* drop us from the pt cgi list */
-	pt->cgi_list = cgi->cgi_list;
+	pt->http.cgi_list = cgi->cgi_list;
 
 	while (--n >= 0)
 		__remove_wsi_socket_from_fds(wsi->cgi->stdwsi[n]);
@@ -961,7 +961,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
 			continue;
 		lwsl_debug("%s: observed PID %d terminated\n", __func__, n);
 
-		pcgi = &pt->cgi_list;
+		pcgi = &pt->http.cgi_list;
 
 		/* check all the subprocesses on the cgi list */
 		while (*pcgi) {
@@ -1023,7 +1023,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
 		}
 	}
 
-	pcgi = &pt->cgi_list;
+	pcgi = &pt->http.cgi_list;
 
 	/* check all the subprocesses on the cgi list */
 	while (*pcgi) {
@@ -1095,7 +1095,7 @@ void
 lws_cgi_remove_and_kill(struct lws *wsi)
 {
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	struct lws_cgi **pcgi = &pt->cgi_list;
+	struct lws_cgi **pcgi = &pt->http.cgi_list;
 
 	/* remove us from the cgi list */
 	lwsl_debug("%s: remove cgi %p from list\n", __func__, wsi->cgi);
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 9a4694f33880e4132e35b312ee9fa003166757c6..5fa929ab0cf522e53dce02ef70c685f401d0a244 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -62,7 +62,7 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 		/* fallthru */
 
 	case LRS_HEADERS:
-		if (!wsi->ah) {
+		if (!wsi->http.ah) {
 			lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
 			assert(0);
 		}
@@ -308,7 +308,7 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 	     lwsi_state(wsi) == LRS_ISSUING_FILE ||
 	     lwsi_state(wsi) == LRS_HEADERS ||
 	     lwsi_state(wsi) == LRS_BODY)) {
-		if (!wsi->ah &&
+		if (!wsi->http.ah &&
 		    lws_header_table_attach(wsi, 0)) {
 			lwsl_info("wsi %p: ah get fail\n", wsi);
 			goto try_pollout;
@@ -367,7 +367,7 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 		 * so the ah has no further meaning
 		 */
 
-		if (wsi->ah &&
+		if (wsi->http.ah &&
 		    !lwsi_role_h1(wsi) &&
 		    !lwsi_role_h2(wsi) &&
 		    !lwsi_role_cgi(wsi))
diff --git a/lib/roles/h2/hpack.c b/lib/roles/h2/hpack.c
index 2147c65331b703bdf053da54bcec26be174bfafe..6e802764aff05cad1330d14434eed2d3bf6b4c81 100644
--- a/lib/roles/h2/hpack.c
+++ b/lib/roles/h2/hpack.c
@@ -202,7 +202,7 @@ static int huftable_decode(int pos, char c)
 
 static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 
 	if (!ah) {
 		lwsl_notice("%s: no ah\n", __func__);
@@ -275,7 +275,7 @@ static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
 
 static int lws_frag_append(struct lws *wsi, unsigned char c)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 
 	ah->data[ah->pos++] = c;
 	ah->frags[ah->nfrag].len++;
@@ -290,16 +290,16 @@ static int lws_frag_end(struct lws *wsi)
 		return 1;
 
 	/* don't account for the terminating NUL in the logical length */
-	wsi->ah->frags[wsi->ah->nfrag].len--;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].len--;
 
-	wsi->ah->nfrag++;
+	wsi->http.ah->nfrag++;
 	return 0;
 }
 
 int
 lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	int n;
 
 	if (!ah)
@@ -781,7 +781,7 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
 {
 	struct lws *nwsi = lws_get_network_wsi(wsi);
 	struct lws_h2_netconn *h2n = nwsi->h2.h2n;
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	unsigned int prev;
 	unsigned char c1;
 	int n, m, plen;
diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c
index b0a45578fb80f4e110936414957b900475d12b62..c61eb841361c5782fb8d6df0a85d160022ae11d9 100644
--- a/lib/roles/h2/http2.c
+++ b/lib/roles/h2/http2.c
@@ -672,11 +672,11 @@ int lws_h2_do_pps_send(struct lws *wsi)
 				goto bail;
 
 			/* pass on the initial headers to SID 1 */
-			h2n->swsi->ah = wsi->ah;
-			wsi->ah = NULL;
+			h2n->swsi->http.ah = wsi->http.ah;
+			wsi->http.ah = NULL;
 
 			lwsl_info("%s: inherited headers %p\n", __func__,
-				  h2n->swsi->ah);
+				  h2n->swsi->http.ah);
 			h2n->swsi->h2.tx_cr =
 				h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
 			lwsl_info("initial tx credit on conn %p: %d\n",
@@ -1090,7 +1090,7 @@ lws_h2_parse_frame_header(struct lws *wsi)
 		 * ah needs attaching to child wsi, even though
 		 * we only fill it from network wsi
 		 */
-		if (!h2n->swsi->ah)
+		if (!h2n->swsi->http.ah)
 			if (lws_header_table_attach(h2n->swsi, 0)) {
 				lwsl_err("%s: Failed to get ah\n", __func__);
 				return 1;
@@ -1230,7 +1230,7 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 					    &role_ops_h2);
 
 			/* pass on the initial headers to SID 1 */
-			h2n->swsi->ah = wsi->ah;
+			h2n->swsi->http.ah = wsi->http.ah;
 			h2n->swsi->client_h2_substream = 1;
 
 			h2n->swsi->protocol = wsi->protocol;
@@ -1240,9 +1240,9 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 
 			wsi->user_space = NULL;
 
-			if (h2n->swsi->ah)
-				h2n->swsi->ah->wsi = h2n->swsi;
-			wsi->ah = NULL;
+			if (h2n->swsi->http.ah)
+				h2n->swsi->http.ah->wsi = h2n->swsi;
+			wsi->http.ah = NULL;
 
 			lwsl_info("%s: MIGRATING nwsi %p: swsi %p\n", __func__,
 				  wsi, h2n->swsi);
@@ -1422,8 +1422,8 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 		wsi->vhost->conn_stats.h2_trans++;
 		p = lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD);
 		if (!strcmp(p, "POST"))
-			h2n->swsi->ah->frag_index[WSI_TOKEN_POST_URI] =
-				h2n->swsi->ah->frag_index[WSI_TOKEN_HTTP_COLON_PATH];
+			h2n->swsi->http.ah->frag_index[WSI_TOKEN_POST_URI] =
+				h2n->swsi->http.ah->frag_index[WSI_TOKEN_HTTP_COLON_PATH];
 
 		lwsl_debug("%s: setting DEF_ACT from 0x%x\n", __func__, h2n->swsi->wsistate);
 		lwsi_set_state(h2n->swsi, LRS_DEFERRING_ACTION);
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index f11b461a0824f4367f713ab48c03703cbada4abe..8aeb167354e441e3394a66d9f9790954f286c33c 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -288,7 +288,7 @@ drain:
 
 	// lws_buflist_describe(&wsi->buflist, wsi);
 
-	if (wsi->ah
+	if (wsi->http.ah
 #if !defined(LWS_NO_CLIENT)
 			&& !wsi->client_h2_alpn
 #endif
@@ -1000,13 +1000,13 @@ rops_alpn_negotiated_h2(struct lws *wsi, const char *alpn)
 
 		/* adopt the header info */
 
-		ah = wsi->ah;
+		ah = wsi->http.ah;
 
 		lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
 				    &role_ops_h2);
 
 		/* http2 union member has http union struct at start */
-		wsi->ah = ah;
+		wsi->http.ah = ah;
 
 		if (!wsi->h2.h2n)
 			wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n), "h2n");
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index c5043bd42d1bf9e82ddb47449dca448ca2a0bc76..b2137dfd361330f96ab3b92f253071dae3770aad 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -50,7 +50,7 @@ lws_client_connect_2(struct lws *wsi)
 
 	lwsl_client("%s: %p\n", __func__, wsi);
 
-	if (!wsi->ah) {
+	if (!wsi->http.ah) {
 		cce = "ah was NULL at cc2";
 		lwsl_err("%s\n", cce);
 		goto oom4;
@@ -180,7 +180,7 @@ create_new_conn:
 	 *
 	 * Priority 1: connect to http proxy */
 
-	if (wsi->vhost->http_proxy_port) {
+	if (wsi->vhost->http.http_proxy_port) {
 		plen = sprintf((char *)pt->serv_buf,
 			"CONNECT %s:%u HTTP/1.0\x0d\x0a"
 			"User-agent: libwebsockets\x0d\x0a",
@@ -193,8 +193,8 @@ create_new_conn:
 					wsi->vhost->proxy_basic_auth_token);
 
 		plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
-		ads = wsi->vhost->http_proxy_address;
-		port = wsi->vhost->http_proxy_port;
+		ads = wsi->vhost->http.http_proxy_address;
+		port = wsi->vhost->http.http_proxy_port;
 
 #if defined(LWS_WITH_SOCKS5)
 
@@ -447,7 +447,7 @@ create_new_conn:
 	/* we are connected to server, or proxy */
 
 	/* http proxy */
-	if (wsi->vhost->http_proxy_port) {
+	if (wsi->vhost->http.http_proxy_port) {
 
 		/*
 		 * OK from now on we talk via the proxy, so connect to that
@@ -456,9 +456,9 @@ create_new_conn:
 		 * leaving old string/frag there but unreferenced)
 		 */
 		if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
-					  wsi->vhost->http_proxy_address))
+					  wsi->vhost->http.http_proxy_address))
 			goto failed;
-		wsi->c_port = wsi->vhost->http_proxy_port;
+		wsi->c_port = wsi->vhost->http.http_proxy_port;
 
 		n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen,
 			 MSG_NOSIGNAL);
@@ -665,7 +665,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->c_port = port;
 	wsi->hdr_parsing_completed = 0;
-	_lws_header_table_reset(wsi->ah);
+	_lws_header_table_reset(wsi->http.ah);
 
 	if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
 		return NULL;
@@ -887,8 +887,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 		goto bail;
 
 	wsi->context = i->context;
+#if defined(LWS_ROLE_H1)
 	/* assert the mode and union status (hdr) clearly */
 	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1);
+#else
+	lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw);
+#endif
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
 
 	/* 1) fill up the wsi with stuff from the connect_info as far as it
diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c
index a1841f55275d927480208d5d15e96bf49069b411..130766a1c7957c3eeef466590f7b10a6aadfe39c 100644
--- a/lib/roles/http/client/client.c
+++ b/lib/roles/http/client/client.c
@@ -405,10 +405,10 @@ start_ws_handshake:
 			lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
 			w->hdr_parsing_completed = 0;
 
-			w->ah->parser_state = WSI_TOKEN_NAME_PART;
-			w->ah->lextable_pos = 0;
+			w->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+			w->http.ah->lextable_pos = 0;
 			/* If we're (re)starting on headers, need other implied init */
-			wsi->ah->ues = URIES_IDLE;
+			wsi->http.ah->ues = URIES_IDLE;
 		}
 
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
@@ -428,8 +428,8 @@ start_ws_handshake:
 		}
 client_http_body_sent:
 		/* prepare ourselves to do the parsing */
-		wsi->ah->parser_state = WSI_TOKEN_NAME_PART;
-		wsi->ah->lextable_pos = 0;
+		wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+		wsi->http.ah->lextable_pos = 0;
 		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 				context->timeout_secs);
@@ -469,7 +469,7 @@ client_http_body_sent:
 		 * definitively ready from browser pov.
 		 */
 		len = 1;
-		while (wsi->ah->parser_state != WSI_PARSING_COMPLETE &&
+		while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE &&
 		       len > 0) {
 			int plen = 1;
 
@@ -496,7 +496,7 @@ client_http_body_sent:
 		 * libwebsocket timeout still active here too, so if parsing did
 		 * not complete just wait for next packet coming in this state
 		 */
-		if (wsi->ah->parser_state != WSI_PARSING_COMPLETE)
+		if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
 			break;
 
 
@@ -599,14 +599,14 @@ lws_http_transaction_completed_client(struct lws *wsi)
 	/* otherwise set ourselves up ready to go again */
 	lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
 
-	wsi->ah->parser_state = WSI_TOKEN_NAME_PART;
-	wsi->ah->lextable_pos = 0;
+	wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
+	wsi->http.ah->lextable_pos = 0;
 
 	lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 			wsi->context->timeout_secs);
 
 	/* If we're (re)starting on headers, need other implied init */
-	wsi->ah->ues = URIES_IDLE;
+	wsi->http.ah->ues = URIES_IDLE;
 
 	lwsl_info("%s: %p: new queued transaction as %p\n", __func__, wsi, wsi_eff);
 	lws_callback_on_writable(wsi);
@@ -617,10 +617,10 @@ lws_http_transaction_completed_client(struct lws *wsi)
 LWS_VISIBLE LWS_EXTERN unsigned int
 lws_http_client_http_response(struct lws *wsi)
 {
-	if (!wsi->ah)
+	if (!wsi->http.ah)
 		return 0;
 
-	return wsi->ah->http_response;
+	return wsi->http.ah->http_response;
 }
 #if defined(LWS_PLAT_OPTEE)
 char *
@@ -651,7 +651,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 
 	lws_client_stash_destroy(wsi);
 
-	ah = wsi->ah;
+	ah = wsi->http.ah;
 	if (!wsi->do_ws) {
 		/* we are being an http client...
 		 */
@@ -668,7 +668,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 					    LRS_ESTABLISHED, &role_ops_h1);
 		}
 
-		wsi->ah = ah;
+		wsi->http.ah = ah;
 		ah->http_response = 0;
 	}
 
diff --git a/lib/roles/http/header.c b/lib/roles/http/header.c
index 48beb7456fcb33677f9f1f19bd7ee6fc82c8af43..a201c97eb2043b56ee5cfb12e2e01c4cf008207c 100644
--- a/lib/roles/http/header.c
+++ b/lib/roles/http/header.c
@@ -277,15 +277,17 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 
 		return 1;
 	}
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 	if (!wsi->handling_404 &&
-	    wsi->vhost->error_document_404 &&
+	    wsi->vhost->http.error_document_404 &&
 	    code == HTTP_STATUS_NOT_FOUND)
 		/* we should do a redirect, and do the 404 there */
 		if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
-				       (uint8_t *)wsi->vhost->error_document_404,
-				       (int)strlen(wsi->vhost->error_document_404),
+				       (uint8_t *)wsi->vhost->http.error_document_404,
+				       (int)strlen(wsi->vhost->http.error_document_404),
 				       &p, end) > 0)
 			return 0;
+#endif
 
 	/* if the redirect failed, just do a simple status */
 	p = start;
diff --git a/lib/roles/http/private.h b/lib/roles/http/private.h
index 07c6b62b8d627cd4829da5b9ab43c7f907c7f5aa..e8519bd7e68807f6d8b5299ecfc1465a61b3b050 100644
--- a/lib/roles/http/private.h
+++ b/lib/roles/http/private.h
@@ -158,12 +158,38 @@ LWS_EXTERN int
 lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len);
 #endif
 
+struct lws_pt_role_http {
+	struct allocated_headers *ah_list;
+	struct lws *ah_wait_list;
+#ifdef LWS_WITH_CGI
+	struct lws_cgi *cgi_list;
+#endif
+	int ah_wait_list_length;
+	uint32_t ah_pool_length;
+
+	int ah_count_in_use;
+};
+
+struct lws_peer_role_http {
+	uint32_t count_ah;
+	uint32_t total_ah;
+};
+
+struct lws_vhost_role_http {
+	char http_proxy_address[128];
+	const struct lws_http_mount *mount_list;
+	const char *error_document_404;
+	unsigned int http_proxy_port;
+};
+
 struct _lws_http_mode_related {
 	struct lws *new_wsi_list;
 
 #if defined(LWS_WITH_HTTP_PROXY)
 	struct lws_rewrite *rw;
 #endif
+	struct allocated_headers *ah;
+	struct lws *ah_wait_list;
 
 	lws_filepos_t filepos;
 	lws_filepos_t filelen;
diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c
index 67c8dd605c6400fc605cdb7a22874eb066742040..c9ee3234dff7997c996177f4f4cec25cd4b65edc 100644
--- a/lib/roles/http/server/parsers.c
+++ b/lib/roles/http/server/parsers.c
@@ -41,13 +41,13 @@ _lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
 
 		return NULL;
 	}
-	ah->next = pt->ah_list;
-	pt->ah_list = ah;
+	ah->next = pt->http.ah_list;
+	pt->http.ah_list = ah;
 	ah->data_length = data_size;
-	pt->ah_pool_length++;
+	pt->http.ah_pool_length++;
 
 	lwsl_info("%s: created ah %p (size %d): pool length %d\n", __func__,
-		    ah, (int)data_size, pt->ah_pool_length);
+		    ah, (int)data_size, pt->http.ah_pool_length);
 
 	return ah;
 }
@@ -55,12 +55,12 @@ _lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
 int
 _lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
 {
-	lws_start_foreach_llp(struct allocated_headers **, a, pt->ah_list) {
+	lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
 		if ((*a) == ah) {
 			*a = ah->next;
-			pt->ah_pool_length--;
+			pt->http.ah_pool_length--;
 			lwsl_info("%s: freed ah %p : pool length %d\n",
-				    __func__, ah, pt->ah_pool_length);
+				    __func__, ah, pt->http.ah_pool_length);
 			if (ah->data)
 				lws_free(ah->data);
 			lws_free(ah);
@@ -88,7 +88,7 @@ _lws_header_table_reset(struct allocated_headers *ah)
 void
 __lws_header_table_reset(struct lws *wsi, int autoservice)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	struct lws_context_per_thread *pt;
 	struct lws_pollfd *pfd;
 
@@ -144,18 +144,18 @@ _lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
 {
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 	struct lws_pollargs pa;
-	struct lws **pwsi = &pt->ah_wait_list;
+	struct lws **pwsi = &pt->http.ah_wait_list;
 
 	while (*pwsi) {
 		if (*pwsi == wsi)
 			return;
-		pwsi = &(*pwsi)->ah_wait_list;
+		pwsi = &(*pwsi)->http.ah_wait_list;
 	}
 
 	lwsl_info("%s: wsi: %p\n", __func__, wsi);
-	wsi->ah_wait_list = pt->ah_wait_list;
-	pt->ah_wait_list = wsi;
-	pt->ah_wait_list_length++;
+	wsi->http.ah_wait_list = pt->http.ah_wait_list;
+	pt->http.ah_wait_list = wsi;
+	pt->http.ah_wait_list_length++;
 
 	/* we cannot accept input then */
 
@@ -166,20 +166,20 @@ static int
 __lws_remove_from_ah_waiting_list(struct lws *wsi)
 {
         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	struct lws **pwsi =&pt->ah_wait_list;
+	struct lws **pwsi =&pt->http.ah_wait_list;
 
 	while (*pwsi) {
 		if (*pwsi == wsi) {
 			lwsl_info("%s: wsi %p\n", __func__, wsi);
 			/* point prev guy to our next */
-			*pwsi = wsi->ah_wait_list;
+			*pwsi = wsi->http.ah_wait_list;
 			/* we shouldn't point anywhere now */
-			wsi->ah_wait_list = NULL;
-			pt->ah_wait_list_length--;
+			wsi->http.ah_wait_list = NULL;
+			pt->http.ah_wait_list_length--;
 
 			return 1;
 		}
-		pwsi = &(*pwsi)->ah_wait_list;
+		pwsi = &(*pwsi)->http.ah_wait_list;
 	}
 
 	return 0;
@@ -194,18 +194,18 @@ lws_header_table_attach(struct lws *wsi, int autoservice)
 	int n;
 
 	lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
-		  (void *)wsi, (void *)wsi->ah, wsi->tsi,
-		  pt->ah_count_in_use);
+		  (void *)wsi, (void *)wsi->http.ah, wsi->tsi,
+		  pt->http.ah_count_in_use);
 
 	lws_pt_lock(pt, __func__);
 
 	/* if we are already bound to one, just clear it down */
-	if (wsi->ah) {
+	if (wsi->http.ah) {
 		lwsl_info("%s: cleardown\n", __func__);
 		goto reset;
 	}
 
-	n = pt->ah_count_in_use == context->max_http_header_pool;
+	n = pt->http.ah_count_in_use == context->max_http_header_pool;
 #if defined(LWS_WITH_PEER_LIMITS)
 	if (!n) {
 		n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
@@ -229,26 +229,28 @@ lws_header_table_attach(struct lws *wsi, int autoservice)
 
 	__lws_remove_from_ah_waiting_list(wsi);
 
-	wsi->ah = _lws_create_ah(pt, context->max_http_header_data);
-	if (!wsi->ah) { /* we could not create an ah */
+	wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
+	if (!wsi->http.ah) { /* we could not create an ah */
 		_lws_header_ensure_we_are_on_waiting_list(wsi);
 
 		goto bail;
 	}
 
-	wsi->ah->in_use = 1;
-	wsi->ah->wsi = wsi; /* mark our owner */
-	pt->ah_count_in_use++;
+	wsi->http.ah->in_use = 1;
+	wsi->http.ah->wsi = wsi; /* mark our owner */
+	pt->http.ah_count_in_use++;
 
-#if defined(LWS_WITH_PEER_LIMITS)
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
+	lws_context_lock(context); /* <====================================== */
 	if (wsi->peer)
-		wsi->peer->count_ah++;
+		wsi->peer->http.count_ah++;
+	lws_context_unlock(context); /* ====================================> */
 #endif
 
 	_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
 
 	lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
-		  (void *)wsi, (void *)wsi->ah, pt->ah_count_in_use);
+		  (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use);
 
 reset:
 	__lws_header_table_reset(wsi, autoservice);
@@ -275,7 +277,7 @@ bail:
 int __lws_header_table_detach(struct lws *wsi, int autoservice)
 {
 	struct lws_context *context = wsi->context;
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_pollargs pa;
 	struct lws **pwsi, **pwsi_eligible;
@@ -288,7 +290,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 
 	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
 		  (void *)wsi, (void *)ah, wsi->tsi,
-		  pt->ah_count_in_use);
+		  pt->http.ah_count_in_use);
 
 	/* we did have an ah attached */
 	time(&now);
@@ -305,10 +307,10 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	ah->assigned = 0;
 
 	/* if we think we're detaching one, there should be one in use */
-	assert(pt->ah_count_in_use > 0);
+	assert(pt->http.ah_count_in_use > 0);
 	/* and this specific one should have been in use */
 	assert(ah->in_use);
-	memset(&wsi->ah, 0, sizeof(wsi->ah));
+	memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
 
 #if defined(LWS_WITH_PEER_LIMITS)
 	if (ah->wsi)
@@ -316,7 +318,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 #endif
 	ah->wsi = NULL; /* no owner */
 
-	pwsi = &pt->ah_wait_list;
+	pwsi = &pt->http.ah_wait_list;
 
 	/* oh there is nobody on the waiting list... leave the ah unattached */
 	if (!*pwsi)
@@ -341,11 +343,11 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 		}
 #if defined(LWS_WITH_PEER_LIMITS)
 		else
-			if (!(*pwsi)->ah_wait_list)
+			if (!(*pwsi)->http.ah_wait_list)
 				lws_stats_atomic_bump(context, pt,
 					LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
 #endif
-		pwsi = &(*pwsi)->ah_wait_list;
+		pwsi = &(*pwsi)->http.ah_wait_list;
 	}
 
 	if (!wsi) /* everybody waiting already has too many ah... */
@@ -353,14 +355,14 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 
 	lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi);
 
-	wsi->ah = ah;
+	wsi->http.ah = ah;
 	ah->wsi = wsi; /* new owner */
 
 	__lws_header_table_reset(wsi, autoservice);
-#if defined(LWS_WITH_PEER_LIMITS)
+#if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
 	lws_context_lock(context); /* <====================================== */
 	if (wsi->peer)
-		wsi->peer->count_ah++;
+		wsi->peer->http.count_ah++;
 	lws_context_unlock(context); /* ====================================> */
 #endif
 
@@ -375,10 +377,10 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	}
 
 	/* point prev guy to next guy in list instead */
-	*pwsi_eligible = wsi->ah_wait_list;
+	*pwsi_eligible = wsi->http.ah_wait_list;
 	/* the guy who got one is out of the list */
-	wsi->ah_wait_list = NULL;
-	pt->ah_wait_list_length--;
+	wsi->http.ah_wait_list = NULL;
+	pt->http.ah_wait_list_length--;
 
 #ifndef LWS_NO_CLIENT
 	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
@@ -395,17 +397,17 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	}
 #endif
 
-	assert(!!pt->ah_wait_list_length == !!(lws_intptr_t)pt->ah_wait_list);
+	assert(!!pt->http.ah_wait_list_length == !!(lws_intptr_t)pt->http.ah_wait_list);
 bail:
 	lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
-		  (void *)wsi, (void *)ah, pt->tid, pt->ah_count_in_use);
+		  (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use);
 
 	return 0;
 
 nobody_usable_waiting:
 	lwsl_info("%s: nobody usable waiting\n", __func__);
 	_lws_destroy_ah(pt, ah);
-	pt->ah_count_in_use--;
+	pt->http.ah_count_in_use--;
 
 	goto bail;
 }
@@ -428,16 +430,16 @@ lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
 {
 	int n;
 
-	if (!wsi->ah)
+	if (!wsi->http.ah)
 		return 0;
 
-	n = wsi->ah->frag_index[h];
+	n = wsi->http.ah->frag_index[h];
 	if (!n)
 		return 0;
 	do {
 		if (!frag_idx)
-			return wsi->ah->frags[n].len;
-		n = wsi->ah->frags[n].nfrag;
+			return wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
 	} while (frag_idx-- && n);
 
 	return 0;
@@ -448,15 +450,15 @@ LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
 	int n;
 	int len = 0;
 
-	if (!wsi->ah)
+	if (!wsi->http.ah)
 		return 0;
 
-	n = wsi->ah->frag_index[h];
+	n = wsi->http.ah->frag_index[h];
 	if (!n)
 		return 0;
 	do {
-		len += wsi->ah->frags[n].len;
-		n = wsi->ah->frags[n].nfrag;
+		len += wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
 	} while (n);
 
 	return len;
@@ -468,29 +470,29 @@ LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
 	int n = 0;
 	int f;
 
-	if (!wsi->ah)
+	if (!wsi->http.ah)
 		return -1;
 
-	f = wsi->ah->frag_index[h];
+	f = wsi->http.ah->frag_index[h];
 
 	if (!f)
 		return -1;
 
 	while (n < frag_idx) {
-		f = wsi->ah->frags[f].nfrag;
+		f = wsi->http.ah->frags[f].nfrag;
 		if (!f)
 			return -1;
 		n++;
 	}
 
-	if (wsi->ah->frags[f].len >= len)
+	if (wsi->http.ah->frags[f].len >= len)
 		return -1;
 
-	memcpy(dst, wsi->ah->data + wsi->ah->frags[f].offset,
-	       wsi->ah->frags[f].len);
-	dst[wsi->ah->frags[f].len] = '\0';
+	memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
+	       wsi->http.ah->frags[f].len);
+	dst[wsi->http.ah->frags[f].len] = '\0';
 
-	return wsi->ah->frags[f].len;
+	return wsi->http.ah->frags[f].len;
 }
 
 LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
@@ -502,21 +504,21 @@ LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
 	if (toklen >= len)
 		return -1;
 
-	if (!wsi->ah)
+	if (!wsi->http.ah)
 		return -1;
 
-	n = wsi->ah->frag_index[h];
+	n = wsi->http.ah->frag_index[h];
 	if (!n)
 		return 0;
 
 	do {
-		if (wsi->ah->frags[n].len >= len)
+		if (wsi->http.ah->frags[n].len >= len)
 			return -1;
-		strncpy(dst, &wsi->ah->data[wsi->ah->frags[n].offset],
-		        wsi->ah->frags[n].len);
-		dst += wsi->ah->frags[n].len;
-		len -= wsi->ah->frags[n].len;
-		n = wsi->ah->frags[n].nfrag;
+		strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
+		        wsi->http.ah->frags[n].len);
+		dst += wsi->http.ah->frags[n].len;
+		len -= wsi->http.ah->frags[n].len;
+		n = wsi->http.ah->frags[n].nfrag;
 	} while (n);
 	*dst = '\0';
 
@@ -527,21 +529,21 @@ char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
 {
 	int n;
 
-	n = wsi->ah->frag_index[h];
+	n = wsi->http.ah->frag_index[h];
 	if (!n)
 		return NULL;
 
-	return wsi->ah->data + wsi->ah->frags[n].offset;
+	return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
 }
 
 static int LWS_WARN_UNUSED_RESULT
 lws_pos_in_bounds(struct lws *wsi)
 {
-	if (wsi->ah->pos <
+	if (wsi->http.ah->pos <
 	    (unsigned int)wsi->context->max_http_header_data)
 		return 0;
 
-	if ((int)wsi->ah->pos == wsi->context->max_http_header_data) {
+	if ((int)wsi->http.ah->pos == wsi->context->max_http_header_data) {
 		lwsl_err("Ran out of header data space\n");
 		return 1;
 	}
@@ -550,7 +552,7 @@ lws_pos_in_bounds(struct lws *wsi)
 	 * with these tests everywhere, it should never be able to exceed
 	 * the limit, only meet it
 	 */
-	lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->ah->pos,
+	lwsl_err("%s: pos %d, limit %d\n", __func__, wsi->http.ah->pos,
 		 wsi->context->max_http_header_data);
 	assert(0);
 
@@ -560,25 +562,25 @@ lws_pos_in_bounds(struct lws *wsi)
 int LWS_WARN_UNUSED_RESULT
 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
 {
-	wsi->ah->nfrag++;
-	if (wsi->ah->nfrag == ARRAY_SIZE(wsi->ah->frags)) {
+	wsi->http.ah->nfrag++;
+	if (wsi->http.ah->nfrag == ARRAY_SIZE(wsi->http.ah->frags)) {
 		lwsl_warn("More hdr frags than we can deal with, dropping\n");
 		return -1;
 	}
 
-	wsi->ah->frag_index[h] = wsi->ah->nfrag;
+	wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
 
-	wsi->ah->frags[wsi->ah->nfrag].offset = wsi->ah->pos;
-	wsi->ah->frags[wsi->ah->nfrag].len = 0;
-	wsi->ah->frags[wsi->ah->nfrag].nfrag = 0;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
+	wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
 
 	do {
 		if (lws_pos_in_bounds(wsi))
 			return -1;
 
-		wsi->ah->data[wsi->ah->pos++] = *s;
+		wsi->http.ah->data[wsi->http.ah->pos++] = *s;
 		if (*s)
-			wsi->ah->frags[wsi->ah->nfrag].len++;
+			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
 	} while (*s++);
 
 	return 0;
@@ -606,27 +608,27 @@ issue_char(struct lws *wsi, unsigned char c)
 	if (lws_pos_in_bounds(wsi))
 		return -1;
 
-	frag_len = wsi->ah->frags[wsi->ah->nfrag].len;
+	frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
 	/*
 	 * If we haven't hit the token limit, just copy the character into
 	 * the header
 	 */
-	if (frag_len < wsi->ah->current_token_limit) {
-		wsi->ah->data[wsi->ah->pos++] = c;
+	if (frag_len < wsi->http.ah->current_token_limit) {
+		wsi->http.ah->data[wsi->http.ah->pos++] = c;
 		if (c)
-			wsi->ah->frags[wsi->ah->nfrag].len++;
+			wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
 		return 0;
 	}
 
 	/* Insert a null character when we *hit* the limit: */
-	if (frag_len == wsi->ah->current_token_limit) {
+	if (frag_len == wsi->http.ah->current_token_limit) {
 		if (lws_pos_in_bounds(wsi))
 			return -1;
 
-		wsi->ah->data[wsi->ah->pos++] = '\0';
+		wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
 		lwsl_warn("header %i exceeds limit %d\n",
-			  wsi->ah->parser_state,
-			  wsi->ah->current_token_limit);
+			  wsi->http.ah->parser_state,
+			  wsi->http.ah->current_token_limit);
 	}
 
 	return 1;
@@ -635,7 +637,7 @@ issue_char(struct lws *wsi, unsigned char c)
 int
 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	unsigned int enc = 0;
 	uint8_t c = *_c;
 
@@ -841,13 +843,13 @@ static const unsigned char methods[] = {
 int LWS_WARN_UNUSED_RESULT
 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
 {
-	struct allocated_headers *ah = wsi->ah;
+	struct allocated_headers *ah = wsi->http.ah;
 	struct lws_context *context = wsi->context;
 	unsigned int n, m;
 	unsigned char c;
 	int r, pos;
 
-	assert(wsi->ah);
+	assert(wsi->http.ah);
 
 	do {
 		(*len)--;
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index fac864e787a3bd22c133fcbd788f6ce0971ee888..5c819cd6696ce8527a85ed2e4fb6fb857e219a97 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -460,9 +460,11 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 	if (!wsi->vhost)
 		return -1;
 
-	if (wsi->vhost->error_document_404 &&
-	    !strcmp(uri, wsi->vhost->error_document_404))
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
+	if (wsi->vhost->http.error_document_404 &&
+	    !strcmp(uri, wsi->vhost->http.error_document_404))
 		wsi->handling_404 = 1;
+#endif
 
 	lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
 
@@ -546,7 +548,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
 		if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
 			/* differs - defeat Range: */
-			wsi->ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
+			wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
 
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
 		/*
@@ -653,13 +655,14 @@ bail:
 	return -1;
 }
 
+#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
 const struct lws_http_mount *
 lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
 {
 	const struct lws_http_mount *hm, *hit = NULL;
 	int best = 0;
 
-	hm = wsi->vhost->mount_list;
+	hm = wsi->vhost->http.mount_list;
 	while (hm) {
 		if (uri_len >= hm->mountpoint_len &&
 		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
@@ -684,6 +687,7 @@ lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
 
 	return hit;
 }
+#endif
 
 #if !defined(LWS_WITH_ESP32)
 static int
@@ -1383,7 +1387,7 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 		assert(0);
 	}
 
-	if (!wsi->ah) {
+	if (!wsi->http.ah) {
 		lwsl_err("%s: assert: NULL ah\n", __func__);
 		assert(0);
 	}
@@ -1435,7 +1439,7 @@ raw_transition:
 			goto bail_nuke_ah;
 		}
 
-		if (wsi->ah->parser_state != WSI_PARSING_COMPLETE)
+		if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
 			continue;
 
 		lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
@@ -1550,7 +1554,7 @@ raw_transition:
 		wsi->http.fop_fd = NULL;
 
 		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
-			   (void *)wsi->ah);
+			   (void *)wsi->http.ah);
 
 		n = lws_http_action(wsi);
 
@@ -1764,7 +1768,7 @@ lws_http_transaction_completed(struct lws *wsi)
 	 * that is already at least the start of another header set, simply
 	 * reset the existing header table and keep it.
 	 */
-	if (wsi->ah) {
+	if (wsi->http.ah) {
 		// lws_buflist_describe(&wsi->buflist, wsi);
 		if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
 			lwsl_debug("%s: nothing in buflist so detaching ah\n", __func__);
@@ -1798,8 +1802,8 @@ lws_http_transaction_completed(struct lws *wsi)
 					wsi->vhost->keepalive_timeout);
 		}
 		/* If we're (re)starting on headers, need other implied init */
-		if (wsi->ah)
-			wsi->ah->ues = URIES_IDLE;
+		if (wsi->http.ah)
+			wsi->http.ah->ues = URIES_IDLE;
 
 		lwsi_set_state(wsi, LRS_ESTABLISHED);
 	} else
@@ -2074,7 +2078,7 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
 	 * readbuf data to wsi or ah yet, and we will do it next if we get
 	 * the ah.
 	 */
-	if (wsi->ah || !lws_header_table_attach(wsi, 0)) {
+	if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) {
 
 		lwsl_notice("%s: calling service on readbuf ah\n", __func__);
 		pt = &wsi->context->pt[(int)wsi->tsi];
diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c
index 2fe5b4f9d5f36913ea57c6a882caf857b46e3193..10b69f01996247c7f50fa5b1759dc35ef46c77a6 100644
--- a/lib/roles/ws/client-ws.c
+++ b/lib/roles/ws/client-ws.c
@@ -194,8 +194,8 @@ lws_generate_client_ws_handshake(struct lws *wsi, char *p)
 	lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
 
 	lws_b64_encode_string(hash, 20,
-		  wsi->ah->initial_handshake_hash_base64,
-		  sizeof(wsi->ah->initial_handshake_hash_base64));
+		  wsi->http.ah->initial_handshake_hash_base64,
+		  sizeof(wsi->http.ah->initial_handshake_hash_base64));
 
 	return p;
 }
@@ -225,18 +225,18 @@ lws_client_ws_upgrade(struct lws *wsi, const char **cce)
 		goto bail3;
 	}
 
-	if (wsi->ah->http_response == 401) {
+	if (wsi->http.ah->http_response == 401) {
 		lwsl_warn(
 		       "lws_client_handshake: got bad HTTP response '%d'\n",
-		       wsi->ah->http_response);
+		       wsi->http.ah->http_response);
 		*cce = "HS: ws upgrade unauthorized";
 		goto bail3;
 	}
 
-	if (wsi->ah->http_response != 101) {
+	if (wsi->http.ah->http_response != 101) {
 		lwsl_warn(
 		       "lws_client_handshake: got bad HTTP response '%d'\n",
-		       wsi->ah->http_response);
+		       wsi->http.ah->http_response);
 		*cce = "HS: ws upgrade response not 101";
 		goto bail3;
 	}
@@ -544,9 +544,9 @@ check_accept:
 	 */
 
 	p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
-	if (strcmp(p, wsi->ah->initial_handshake_hash_base64)) {
+	if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
 		lwsl_warn("lws_client_int_s_hs: accept '%s' wrong vs '%s'\n", p,
-				  wsi->ah->initial_handshake_hash_base64);
+				  wsi->http.ah->initial_handshake_hash_base64);
 		*cce = "HS: Accept hash wrong";
 		goto bail2;
 	}
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index f94b0ed67c7d04effcf38feb0aa381c47806164f..13402b46edafefa1bd1c4148b3ebc169457a929f 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -1023,7 +1023,7 @@ read:
 		goto drain;
 	}
 
-	if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->ah)
+	if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->http.ah)
 		return LWS_HPI_RET_HANDLED;
 
 	if (lws_is_flowcontrolled(wsi)) {
@@ -1129,7 +1129,7 @@ drain:
 		ebuf.len = 0;
 	} while (m);
 
-	if (wsi->ah
+	if (wsi->http.ah
 #if !defined(LWS_NO_CLIENT)
 			&& !wsi->client_h2_alpn
 #endif
diff --git a/lib/service.c b/lib/service.c
index 226bf96b6eb989dfdae0f87b0b90721aa78daa51..cf2f2b44589d4ff8ec5c53c01b1aadebd3dd1ff5 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -240,8 +240,8 @@ __lws_service_timeout_check(struct lws *wsi, time_t sec)
 				  "(did hdr %d, ah %p, wl %d, pfd "
 				  "events %d) %llu vs %llu\n",
 				  (void *)wsi, wsi->pending_timeout,
-				  wsi->hdr_parsing_completed, wsi->ah,
-				  pt->ah_wait_list_length, n,
+				  wsi->hdr_parsing_completed, wsi->http.ah,
+				  pt->http.ah_wait_list_length, n,
 				  (unsigned long long)sec,
 				  (unsigned long long)wsi->pending_timeout_limit);
 #if defined(LWS_WITH_CGI)
@@ -613,7 +613,7 @@ lws_service_periodic_checks(struct lws_context *context,
 	 *	    timeout status
 	 */
 
-	ah = pt->ah_list;
+	ah = pt->http.ah_list;
 	while (ah) {
 		int len;
 		char buf[256];
@@ -686,7 +686,7 @@ lws_service_periodic_checks(struct lws_context *context,
 			__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
 					     "excessive ah");
 
-		ah = pt->ah_list;
+		ah = pt->http.ah_list;
 	}
 
 	lws_pt_unlock(pt);