diff --git a/lib/context.c b/lib/context.c
index 23b64ab00cb58f1a32281c83b8af7c550f14ff57..04c95a15bc4fb8a3dd1479c33d0d56c88624e736 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -196,7 +196,7 @@ lws_protocol_init(struct lws_context *context)
 	struct lws_vhost *vh = context->vhost_list;
 	const struct lws_protocol_vhost_options *pvo, *pvo1;
 	struct lws wsi;
-	int n;
+	int n, any = 0;
 
 	if (context->doing_protocol_init)
 		return 0;
@@ -212,7 +212,8 @@ lws_protocol_init(struct lws_context *context)
 		wsi.vhost = vh;
 
 		/* only do the protocol init once for a given vhost */
-		if (vh->created_vhost_protocols)
+		if (vh->created_vhost_protocols ||
+		    (vh->options & LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT))
 			goto next;
 
 		/* initialize supported protocols on this vhost */
@@ -260,6 +261,10 @@ lws_protocol_init(struct lws_context *context)
 				pvo = pvo1->options;
 			}
 
+#if defined(LWS_OPENSSL_SUPPORT)
+			any |= !!vh->ssl_ctx;
+#endif
+
 			/*
 			 * inform all the protocols that they are doing their
 			 * one-time initialization if they want to.
@@ -289,6 +294,9 @@ next:
 
 	context->protocol_init_done = 1;
 
+	if (any)
+		lws_tls_check_all_cert_lifetimes(context);
+
 	return 0;
 }
 
@@ -1270,6 +1278,8 @@ lws_create_context(struct lws_context_creation_info *info)
 			LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
 			goto bail;
 
+	time(&context->last_cert_check_s);
+
 #if defined(LWS_WITH_SELFTESTS)
 	lws_jws_selftest();
 #endif
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 7fb742d7c9957a53aba3961e4e6e273bd91ed661..1b980dc17353b36d3279c5b3d3d42b9431d9e673 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -1432,12 +1432,45 @@ lws_rx_flow_allow_all_protocol(const struct lws_context *context,
 	}
 }
 
+int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len)
+{
+	struct lws_vhost *v = context->vhost_list;
+	struct lws wsi;
+	int n, ret = 0;
+
+	memset(&wsi, 0, sizeof(wsi));
+	wsi.context = context;
+
+	while (v) {
+		const struct lws_protocols *p = v->protocols;
+		wsi.vhost = v;
+
+		for (n = 0; n < v->count_protocols; n++) {
+			wsi.protocol = p;
+			if (p->callback &&
+			    p->callback(&wsi, reason, NULL, in, len))
+				ret |= 1;
+			p++;
+		}
+		v = v->vhost_next;
+	}
+
+	return ret;
+}
+
 LWS_VISIBLE extern const char *
 lws_canonical_hostname(struct lws_context *context)
 {
 	return (const char *)context->canonical_hostname;
 }
 
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost)
+{
+	return vhost->name;
+}
+
 int user_callback_handle_rxflow(lws_callback_function callback_function,
 				struct lws *wsi,
 				enum lws_callback_reasons reason, void *user,
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 0aaa505a2edc532e4010abbd7118b8a230780b91..f6f57cb19d8d57b43e7eb9dfef834f82948d0d5f 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1422,6 +1422,12 @@ enum lws_callback_reasons {
 	 * callback is serialized in the lws event loop normally, even
 	 * if the lws_cancel_service[_pt]() call was from a different
 	 * thread. */
+	LWS_CALLBACK_VHOST_CERT_AGING				= 72,
+	/**< When a vhost TLS cert has its expiry checked, this callback
+	 * is broadcast to every protocol of every vhost in case the
+	 * protocol wants to take some action with this information.
+	 * \p in is the lws_vhost and \p len is the number of days left
+	 * before it expires, as a (ssize_t) */
 
 	/****** add new things just above ---^ ******/
 
@@ -2504,6 +2510,11 @@ enum lws_context_options {
 	 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which
 	 * provides the vhost SSL_CTX * in the user parameter.
 	 */
+	LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT			= (1 << 25),
+	/**< (VH) You probably don't want this.  It forces this vhost to not
+	 * call LWS_CALLBACK_PROTOCOL_INIT on its protocols.  It's used in the
+	 * special case of a temporary vhost bound to a single protocol.
+	 */
 
 	/****** add new things just above ---^ ******/
 };
@@ -3013,6 +3024,14 @@ lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED;
 LWS_VISIBLE LWS_EXTERN struct lws_vhost *
 lws_get_vhost(struct lws *wsi);
 
+/**
+ * lws_get_vhost_name() - returns the name of a vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost);
+
 /**
  * lws_json_dump_vhost() - describe vhost state and stats in JSON
  *
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 960c37dbc5c4ad6f5991e987b162bfe96e6d83ce..c623eee31692f72406aeeca35be1a860f2b93a65 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -1088,6 +1088,7 @@ struct lws_peer {
 struct lws_context {
 	time_t last_timeout_check_s;
 	time_t last_ws_ping_pong_check_s;
+	time_t last_cert_check_s;
 	time_t time_up;
 	const struct lws_plat_file_ops *fops;
 	struct lws_plat_file_ops fops_platform;
@@ -2368,6 +2369,7 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
 #define lws_ssl_remove_wsi_from_buffered_list(_a)
 #define lws_context_init_ssl_library(_a)
 #define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0)
+#define lws_tls_check_all_cert_lifetimes(_a)
 #else
 #define LWS_SSL_ENABLED(context) (context->use_ssl)
 LWS_EXTERN int openssl_websocket_private_data_index;
@@ -2408,6 +2410,8 @@ lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
 LWS_EXTERN int
 lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
 			  union lws_tls_cert_info_results *buf, size_t len);
+LWS_EXTERN int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context);
 #ifndef LWS_NO_SERVER
 LWS_EXTERN int
 lws_context_init_server_ssl(struct lws_context_creation_info *info,
@@ -2526,6 +2530,9 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_ssl_pending_no_ssl(struct lws *wsi);
 
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
+
 int lws_jws_selftest(void);
 
 #ifdef LWS_WITH_HTTP_PROXY
@@ -2704,6 +2711,9 @@ lws_same_vh_protocol_remove(struct lws *wsi);
 LWS_EXTERN void
 lws_same_vh_protocol_insert(struct lws *wsi, int n);
 
+LWS_EXTERN int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len);
+
 #if defined(LWS_WITH_STATS)
 void
 lws_stats_atomic_bump(struct lws_context * context,
diff --git a/lib/service.c b/lib/service.c
index e2efee887c7b0010712f6700dd3501074ba4ebda..62644f55e913eab93b9ea1202254f9f975548fbe 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -1166,6 +1166,14 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 		}
 	}
 
+	/*
+	 * check the remaining cert lifetime daily
+	 */
+	if (context->last_cert_check_s < now - (24 * 60 * 60)) {
+		context->last_cert_check_s = now;
+
+		lws_tls_check_all_cert_lifetimes(context);
+	}
 
 	/* the socket we came to service timed out, nothing to do */
 	if (timed_out)
@@ -1234,7 +1242,6 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 	switch (wsi->mode) {
 	case LWSCM_EVENT_PIPE:
 	{
-		struct lws_vhost *v = context->vhost_list;
 #if !defined(WIN32) && !defined(_WIN32)
 		char s[10];
 
@@ -1248,31 +1255,18 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 		if (n < 0)
 			goto close_and_handled;
 #endif
-
 		/*
 		 * the poll() wait, or the event loop for libuv etc is a
 		 * process-wide resource that we interrupted.  So let every
 		 * protocol that may be interested in the pipe event know that
 		 * it happened.
 		 */
-		while (v) {
-			const struct lws_protocols *p = v->protocols;
-			wsi->vhost = v;
-
-			for (n = 0; n < v->count_protocols; n++) {
-				wsi->protocol = p;
-				if (p->callback && p->callback(wsi,
-					  LWS_CALLBACK_EVENT_WAIT_CANCELLED,
-					  NULL, NULL, 0)) {
-					lwsl_info("closed in event cancel\n");
-					goto close_and_handled;
-				}
-				p++;
-			}
-			v = v->vhost_next;
+		if (lws_broadcast(context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
+				  NULL, 0)) {
+			lwsl_info("closed in event cancel\n");
+			goto close_and_handled;
 		}
-		wsi->vhost = NULL;
-		wsi->protocol = NULL;
+
 		goto handled;
 	}
 	case LWSCM_HTTP_SERVING:
diff --git a/lib/tls/tls.c b/lib/tls/tls.c
index 81dc0e231ad550999e1ad498ae85e3d40ebf5642..c900c44fc2df695939ecbda68de2dc48f8e8c586 100644
--- a/lib/tls/tls.c
+++ b/lib/tls/tls.c
@@ -21,8 +21,9 @@
 
 #include "private-libwebsockets.h"
 
-int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
-		lws_filepos_t *amount)
+int
+lws_alloc_vfs_file(struct lws_context *context, const char *filename,
+		   uint8_t **buf, lws_filepos_t *amount)
 {
 	lws_filepos_t len;
 	lws_fop_flags_t	flags = LWS_O_RDONLY;
@@ -97,6 +98,45 @@ lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
 	wsi->pending_read_list_next = NULL;
 }
 
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *v)
+{
+	union lws_tls_cert_info_results ir;
+	time_t now = (time_t)lws_now_secs(), life;
+	int n;
+
+	if (!v->ssl_ctx)
+		return -1;
+
+	if (now < 1464083026) /* May 2016 */
+		/* our clock is wrong and we can't judge the certs */
+		return -1;
+
+	n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0);
+	if (n)
+		return -1;
+
+	life = (ir.time - now) / (24 * 3600);
+	lwsl_notice("   vhost %s: cert expiry: %dd\n", v->name, (int)life);
+
+	lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, v,
+		      (size_t)(ssize_t)life);
+
+	return 0;
+}
+
+int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context)
+{
+	struct lws_vhost *v = context->vhost_list;
+
+	while (v) {
+		lws_tls_check_cert_lifetime(v);
+		v = v->vhost_next;
+	}
+
+	return 0;
+}
 
 int
 lws_gate_accepts(struct lws_context *context, int on)
diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c
index ca2fe3eb8397395b70d4b5acefe71967385639a4..b5826fcb6cd0abc6240819b81a0095515fc341ea 100644
--- a/plugins/protocol_lws_sshd_demo.c
+++ b/plugins/protocol_lws_sshd_demo.c
@@ -411,6 +411,9 @@ callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
 		close(vhd->privileged_fd);
 		break;
 
+	case LWS_CALLBACK_VHOST_CERT_AGING:
+		break;
+
 	default:
 		if (!vhd->ssh_base_protocol) {
 			vhd->ssh_base_protocol = lws_vhost_name_to_protocol(