diff --git a/README.lwsws.md b/README.lwsws.md
index ff559b9e049ee3a2873ee283be0125af6266e9e3..4692a770b598c23fd5b58b3e41bdb8de66256268 100644
--- a/README.lwsws.md
+++ b/README.lwsws.md
@@ -68,43 +68,67 @@ Listing multiple vhosts looks something like this
 
 ```
 {
-        "vhosts": [{
-                "name": "warmcat.com",
-                "port": "443",
-                "host-ssl-key": "/etc/pki/tls/private/warmcat.com.key",
-                "host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
-                "host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
-                "mounts": [{
-                        "mountpoint": "/",
-                        "origin": "file:///var/www/warmcat.com",
-                        "default": "index.html"
-                }]
-        }, {
-                "name": "warmcat2.com",
-                "port": "443",
-                "host-ssl-key": "/etc/pki/tls/private/warmcat.com.key",
-                "host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
-                "host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
-                "mounts": [{
-                        "mountpoint": "/",
-                        "origin": "file:///var/www/warmcat2.com",
-                        "default": "index.html"
-                }]
-        }
-]
+ "vhosts": [ {
+     "name": "localhost",
+     "port": "443",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": "file:///var/www/libwebsockets.org",
+       "default": "index.html"
+       }, {
+        "mountpoint": "/testserver",
+        "origin": "file:///usr/local/share/libwebsockets-test-server",
+        "default": "test.html"
+       }],
+     # which protocols are enabled for this vhost, and optional
+     # vhost-specific config options for the protocol
+     #
+     "ws-protocols": [{
+       "warmcat,timezoom": {
+         "status": "ok"
+       }
+     }]
+    },
+    {
+    "name": "localhost",
+    "port": "7681",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://localhost"
+     }]
+   },
+    {
+    "name": "localhost",
+    "port": "80",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://localhost"
+     }]
+   }
+
+  ]
 }
 ```
 
+That sets up three vhosts all called "localhost" on ports 443 and 7681 with SSL, and port 80 without SSL but with a forced redirect to https://localhost
+
+
 Vhost name and port
 -------------------
 
 The vhost name field is used to match on incoming SNI or Host: header, so it
 must always be the host name used to reach the vhost externally.
 
-Vhosts may have the same name and different ports, these will each create a
+ - Vhosts may have the same name and different ports, these will each create a
 listening socket on the appropriate port.
 
-They may also have the same port and different name: these will be treated as
+ - Vhosts may also have the same port and different name: these will be treated as
 true vhosts on one listening socket and the active vhost decided at SSL
 negotiation time (via SNI) or if no SSL, then after the Host: header from
 the client has been parsed.
@@ -148,3 +172,20 @@ To help that happen conveniently, there are some new apis
 dumb increment, mirror and status protocol plugins are provided as examples.
 
 
+Protocols
+---------
+
+Vhosts by default have available the union of any initial protocols from context creation time, and
+any protocols exposed by plugins.
+
+Vhosts can select which plugins they want to offer and give them per-vhost settings using this syntax
+
+```	
+     "ws-protocols": [{
+       "warmcat,timezoom": {
+         "status": "ok"
+       }
+     }]
+
+```
+
diff --git a/lib/context.c b/lib/context.c
index 8a96518995f8cc1bfb2ebf3de5a28e247d7eb571..790b0dd2334ceded77298bd801c121f89bdc397d 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -129,10 +129,26 @@ lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *pr
 	return vhost->protocol_vh_privs[n];
 }
 
+static struct lws_protocol_vhost_options *
+lws_vhost_protocol_options(struct lws_vhost *vh, const char *name)
+{
+	struct lws_protocol_vhost_options *pvo = vh->pvo;
+
+	while (pvo) {
+		// lwsl_notice("%s: '%s' '%s'\n", __func__, pvo->name, name);
+		if (!strcmp(pvo->name, name))
+			return pvo;
+		pvo = pvo->next;
+	}
+
+	return NULL;
+}
+
 int
 lws_protocol_init(struct lws_context *context)
 {
 	struct lws_vhost *vh = context->vhost_list;
+	struct lws_protocol_vhost_options *pvo;
 	struct lws wsi;
 	int n;
 
@@ -147,6 +163,15 @@ lws_protocol_init(struct lws_context *context)
 		for (n = 0; n < vh->count_protocols; n++) {
 			wsi.protocol = &vh->protocols[n];
 
+			pvo = lws_vhost_protocol_options(vh,
+							 vh->protocols[n].name);
+			if (pvo)
+				/*
+				 * linked list of options specific to
+				 * vh + protocol
+				 */
+				pvo = pvo->options;
+
 			/*
 			 * inform all the protocols that they are doing their one-time
 			 * initialization if they want to.
@@ -155,7 +180,7 @@ lws_protocol_init(struct lws_context *context)
 			 * protocol ptrs so lws_get_context(wsi) etc can work
 			 */
 			vh->protocols[n].callback(&wsi,
-				LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
+				LWS_CALLBACK_PROTOCOL_INIT, NULL, pvo, 0);
 		}
 
 		vh = vh->vhost_next;
@@ -176,7 +201,7 @@ lws_create_vhost(struct lws_context *context,
 #ifdef LWS_WITH_PLUGINS
 	struct lws_plugin *plugin = context->plugin_list;
 	struct lws_protocols *lwsp;
-	int m;
+	int m, n;
 #endif
 	char *p;
 
@@ -194,6 +219,9 @@ lws_create_vhost(struct lws_context *context,
 	     info->protocols[vh->count_protocols].callback;
 	     vh->count_protocols++)
 		;
+
+	vh->pvo = info->pvo;
+
 #ifdef LWS_WITH_PLUGINS
 	if (plugin) {
 		/*
@@ -209,12 +237,22 @@ lws_create_vhost(struct lws_context *context,
 		m = vh->count_protocols;
 		memcpy(lwsp, info->protocols,
 		       sizeof(struct lws_protocols) * m);
+
 		while (plugin) {
-			memcpy(&lwsp[m], plugin->caps.protocols,
-			       sizeof(struct lws_protocols) *
-			       plugin->caps.count_protocols);
-			m += plugin->caps.count_protocols;
-			vh->count_protocols += plugin->caps.count_protocols;
+			for (n = 0; n < plugin->caps.count_protocols; n++) {
+				/*
+				 * for compatibility's sake, no pvo implies
+				 * allow all protocols
+				 */
+				if (!info->pvo || lws_vhost_protocol_options(vh,
+				    plugin->caps.protocols[n].name)) {
+					memcpy(&lwsp[m],
+					       &plugin->caps.protocols[n],
+					       sizeof(struct lws_protocols));
+					m++;
+					vh->count_protocols++;
+				}
+			}
 			plugin = plugin->list;
 		}
 		vh->protocols = lwsp;
@@ -222,7 +260,6 @@ lws_create_vhost(struct lws_context *context,
 #endif
 		vh->protocols = info->protocols;
 
-
 	vh->mount_list = mounts;
 
 	lwsl_notice("Creating Vhost '%s' port %d, %d protocols\n",
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index de485b3eec9e3b33592f66848bd9bfbcb721f6b9..36a1d40cb2d4f4f9e870491b256a086314678f0a 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1339,6 +1339,12 @@ LWS_EXTERN int
 lws_set_extension_option(struct lws *wsi, const char *ext_name,
 			 const char *opt_name, const char *opt_val);
 
+struct lws_protocol_vhost_options {
+	struct lws_protocol_vhost_options *next;
+	struct lws_protocol_vhost_options *options;
+	const char *name;
+	const char *value;
+};
 
 /**
  * struct lws_context_creation_info - parameters to create context with
@@ -1451,6 +1457,7 @@ struct lws_context_creation_info {
 	const char *ecdh_curve;				/* VH */
 	const char *vhost_name;				/* VH */
 	const char *plugins_dir;			/* context */
+	struct lws_protocol_vhost_options *pvo;		/* VH */
 
 	/* Add new things just above here ---^
 	 * This is part of the ABI, don't needlessly break compatibility
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 839eab541c5640915090dd8c3df4e4a7feb5b276..4ab471fb08018b7ddf6ff6fa569eb5dfc6f62af1 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -654,6 +654,7 @@ struct lws_vhost {
 	const char *iface;
 	const struct lws_protocols *protocols;
 	void **protocol_vh_privs;
+	struct lws_protocol_vhost_options *pvo;
 #ifdef LWS_OPENSSL_SUPPORT
 	SSL_CTX *ssl_ctx;
 	SSL_CTX *ssl_client_ctx;
diff --git a/lib/server.c b/lib/server.c
index 5a3ec799b1a093e72ad1fa6bc9f31a52fa7f5efc..93c98ab717479281231b5b412270a763560482f0 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -239,13 +239,13 @@ lws_http_action(struct lws *wsi)
 	enum http_connection_type connection_type;
 	enum http_version request_version;
 	char content_length_str[32];
-	struct lws_http_mount *hm;
+	struct lws_http_mount *hm, *hit = NULL;
 	unsigned int n, count = 0;
 	char http_version_str[10];
 	char http_conn_str[20];
 	int http_version_len;
 	char *uri_ptr = NULL;
-	int uri_len = 0;
+	int uri_len = 0, best = 0;
 
 	static const unsigned char methods[] = {
 		WSI_TOKEN_GET_URI,
@@ -395,25 +395,33 @@ lws_http_action(struct lws *wsi)
 
 	hm = wsi->vhost->mount_list;
 	while (hm) {
-		char *s = uri_ptr + hm->mountpoint_len;
-
-		if (s[0] == '\0')
-			s = (char *)hm->def;
-
-		if (!s)
-			s = "index.html";
-
+		lwsl_err("a %p\n", hm);
 		if (uri_len >= hm->mountpoint_len &&
 		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) {
-			n = lws_http_serve(wsi, s, hm->origin);
-			break;
+			if (hm->mountpoint_len > best) {
+				best = hm->mountpoint_len;
+				hit = hm;
+			}
 		}
 		hm = hm->mount_next;
 	}
+lwsl_err("x\n");
+	if (hit) {
+		char *s = uri_ptr + hit->mountpoint_len;
+
+		if (s[0] == '\0')
+			s = (char *)hit->def;
+
+		if (!s)
+			s = "index.html";
+lwsl_err("b\n");
+		n = lws_http_serve(wsi, s, hit->origin);
+	} else {
+		lwsl_err("c\n");
 
-	if (!hm)
 		n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
 				    wsi->user_space, uri_ptr, uri_len);
+	}
 	if (n) {
 		lwsl_info("LWS_CALLBACK_HTTP closing\n");
 
diff --git a/lwsws/conf.c b/lwsws/conf.c
index 4c94c5df448dcfc7204efa08804e6d2e062ba43e..57eafd7806d4d2e5f9822c417540186614808169 100644
--- a/lwsws/conf.c
+++ b/lwsws/conf.c
@@ -47,7 +47,10 @@ static const char * const paths_vhosts[] = {
 	"vhosts[].host-ssl-ca",
 	"vhosts[].mounts[].mountpoint",
 	"vhosts[].mounts[].origin",
-	"vhosts[].mounts[].default"
+	"vhosts[].mounts[].default",
+	"vhosts[].ws-protocols[].*.*",
+	"vhosts[].ws-protocols[].*",
+	"vhosts[].ws-protocols[]"
 };
 
 enum lejp_vhost_paths {
@@ -61,6 +64,9 @@ enum lejp_vhost_paths {
 	LEJPVP_MOUNTPOINT,
 	LEJPVP_ORIGIN,
 	LEJPVP_DEFAULT,
+	LEJPVP_PROTOCOL_NAME_OPT,
+	LEJPVP_PROTOCOL_NAME,
+	LEJPVP_PROTOCOL,
 };
 
 struct jpargs {
@@ -71,8 +77,18 @@ struct jpargs {
 	char *p, *end, valid;
 	struct lws_http_mount *head, *last;
 	char *mountpoint, *origin, *def;
+	struct lws_protocol_vhost_options *pvo;
 };
 
+static void *
+lwsws_align(struct jpargs *a)
+{
+	if ((unsigned long)(a->p) & 15)
+		a->p += 16 - ((unsigned long)(a->p) & 15);
+
+	return a->p;
+}
+
 static int arg_to_bool(const char *s)
 {
 	static const char * const on[] = { "on", "yes", "true" };
@@ -128,10 +144,16 @@ static char
 lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 {
 	struct jpargs *a = (struct jpargs *)ctx->user;
+	struct lws_protocol_vhost_options *pvo;
 	struct lws_http_mount *m;
 	int n;
 
+//	lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
+//	for (n = 0; n < ctx->wildcount; n++)
+//		lwsl_notice("    %d\n", ctx->wild[n]);
+
 	if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
+		/* set the defaults for this vhost */
 		a->valid = 1;
 		a->head = NULL;
 		a->last = NULL;
@@ -156,6 +178,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 				       "!DHE-RSA-AES256-SHA256:"
 				       "!AES256-GCM-SHA384:"
 				       "!AES256-SHA256";
+		a->info->pvo = NULL;
 	}
 
 	if (reason == LEJPCB_OBJECT_START &&
@@ -165,6 +188,25 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->def = NULL;
 	}
 
+	/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
+	if (reason == LEJPCB_OBJECT_START &&
+	    ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
+		a->pvo = lwsws_align(a);
+		a->p += sizeof(*a->pvo);
+
+		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
+		/* ie, enable this protocol, no options yet */
+		a->pvo->next = a->info->pvo;
+		a->info->pvo = a->pvo;
+		a->pvo->name = a->p;
+		lwsl_err("adding %s\n", a->p);
+		a->p += n;
+		a->pvo->value = a->p;
+		a->pvo->options = NULL;
+		a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+		*(a->p)++ = '\0';
+	}
+
 	if (reason == LEJPCB_OBJECT_END &&
 	    (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
 	    a->valid) {
@@ -232,6 +274,27 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_DEFAULT:
 		a->def = a->p;
 		break;
+
+	case LEJPVP_PROTOCOL_NAME_OPT:
+		/* this catches, eg,
+		 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
+		 * ie, these are options attached to a protocol with { }
+		 */
+		pvo = lwsws_align(a);
+		a->p += sizeof(*a->pvo);
+
+		n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
+		/* ie, enable this protocol, no options yet */
+		pvo->next = a->pvo->options;
+		a->pvo->options = pvo;
+		pvo->name = a->p;
+		a->p += n;
+		pvo->value = a->p;
+		pvo->options = NULL;
+		a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+		*(a->p)++ = '\0';
+		break;
+
 	default:
 		return 0;
 	}
diff --git a/lwsws/lejp.c b/lwsws/lejp.c
index 9eff615296c3a6fc199e58affcec962cc9116d5f..20a07340bdc5cc37ca11e5ecca9bd53d37c275b7 100644
--- a/lwsws/lejp.c
+++ b/lwsws/lejp.c
@@ -97,16 +97,56 @@ lejp_change_callback(struct lejp_ctx *ctx,
 static void
 lejp_check_path_match(struct lejp_ctx *ctx)
 {
+	const char *p, *q;
 	int n;
 
 	/* we only need to check if a match is not active */
 	for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) {
-		if (strcmp(ctx->path, ctx->paths[n]))
+		ctx->wildcount = 0;
+		p = ctx->path;
+		q = ctx->paths[n];
+		while (*p && *q) {
+			if (*q != '*') {
+				if (*p != *q)
+					break;
+				p++;
+				q++;
+				continue;
+			}
+			ctx->wild[ctx->wildcount++] = p - ctx->path;
+			q++;
+			while (*p && *p != '.')
+				p++;
+		}
+		if (*p || *q)
 			continue;
+
 		ctx->path_match = n + 1;
 		ctx->path_match_len = ctx->ppos;
 		return;
 	}
+
+	if (!ctx->path_match)
+		ctx->wildcount = 0;
+}
+
+int
+lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
+{
+	int n;
+
+	if (wildcard >= ctx->wildcount || !len)
+		return 0;
+
+	n = ctx->wild[wildcard];
+
+	while (--len && n < ctx->ppos && ctx->path[n] != '.')
+		*dest++ = ctx->path[n++];
+
+	*dest = '\0';
+	n++;
+
+	return n - ctx->wild[wildcard];
 }
 
 /**
diff --git a/lwsws/lejp.h b/lwsws/lejp.h
index 455c203e067f121e1f83176d2444f45fd8cbc1a8..5832e998e1a15c1e908bc282150857e02d373612 100644
--- a/lwsws/lejp.h
+++ b/lwsws/lejp.h
@@ -187,6 +187,7 @@ struct lejp_ctx {
 
 	struct _lejp_stack st[LEJP_MAX_DEPTH];
 	unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
+	unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
 	char path[LEJP_MAX_PATH];
 	char buf[LEJP_STRING_CHUNK];
 
@@ -209,6 +210,7 @@ struct lejp_ctx {
 	unsigned char count_paths;
 	unsigned char path_match;
 	unsigned char path_match_len;
+	unsigned char wildcount;
 };
 
 extern void
@@ -225,3 +227,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
 extern void
 lejp_change_callback(struct lejp_ctx *ctx,
 		       char (*callback)(struct lejp_ctx *ctx, char reason));
+
+extern int
+lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c
index 499073dd27f014fe213ee535e602d475931bdd22..f26e5936f8c11d3551e7446d59bad10b5f5acbc3 100644
--- a/plugins/protocol_dumb_increment.c
+++ b/plugins/protocol_dumb_increment.c
@@ -58,6 +58,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 
 	switch (reason) {
 	case LWS_CALLBACK_PROTOCOL_INIT:
+		lwsl_notice("%s: pvo %p\n", __func__, in);
 		vhd = lws_protocol_vh_priv_zalloc(lws_vhost_get(wsi),
 				lws_protocol_get(wsi),
 				sizeof(struct per_vhost_data__dumb_increment));