diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 112a390ca3046774d2ca0eeaf63be43c86d55e34..a8efcaf250ab0f7035a0422a9adc94ec1293811f 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -2515,6 +2515,12 @@ enum lws_context_options {
 	 * call LWS_CALLBACK_PROTOCOL_INIT on its protocols.  It's used in the
 	 * special case of a temporary vhost bound to a single protocol.
 	 */
+	LWS_SERVER_OPTION_IGNORE_MISSING_CERT			= (1 << 26),
+	/**< (VH) Don't fail if the vhost TLS cert or key are missing, just
+	 * continue.  The vhost won't be able to serve anything, but if for
+	 * example the ACME plugin was configured to fetch a cert, this lets
+	 * you bootstrap your vhost from having no cert to start with.
+	 */
 
 	/****** add new things just above ---^ ******/
 };
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 93a3ced3a722144da538cb6d64d87c6fec5eeec8..f3fb29b6798da5f1e069e66fa45f707f8689714f 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -1042,6 +1042,7 @@ struct lws_vhost {
 
 	unsigned int created_vhost_protocols:1;
 	unsigned int being_destroyed:1;
+	unsigned int skipped_certs:1;
 
 	unsigned char default_protocol_index;
 	unsigned char raw_protocol_index;
@@ -2377,6 +2378,14 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
 #define lws_tls_acme_sni_cert_destroy(_a)
 #else
 #define LWS_SSL_ENABLED(context) (context->use_ssl)
+
+enum lws_tls_extant {
+	LWS_TLS_EXTANT_NO,
+	LWS_TLS_EXTANT_YES,
+	LWS_TLS_EXTANT_ALTERNATIVE
+};
+LWS_EXTERN enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name);
 LWS_EXTERN int openssl_websocket_private_data_index;
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len);
diff --git a/lib/server/lejp-conf.c b/lib/server/lejp-conf.c
index e4191876dee10bbe8b24708c71ae61f8dc11204c..2a641784103dd026d96a6207ae839a792e3949b8 100644
--- a/lib/server/lejp-conf.c
+++ b/lib/server/lejp-conf.c
@@ -100,6 +100,7 @@ static const char * const paths_vhosts[] = {
 	"vhosts[].client-ssl-ciphers",
 	"vhosts[].onlyraw",
 	"vhosts[].client-cert-required",
+	"vhosts[].ignore-missing-cert",
 };
 
 enum lejp_vhost_paths {
@@ -148,6 +149,7 @@ enum lejp_vhost_paths {
 	LEJPVP_CLIENT_CIPHERS,
 	LEJPVP_FLAG_ONLYRAW,
 	LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
+	LEJPVP_IGNORE_MISSING_CERT,
 };
 
 static const char * const parser_errs[] = {
@@ -690,6 +692,14 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 			    LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
 		return 0;
 
+	case LEJPVP_IGNORE_MISSING_CERT:
+		if (arg_to_bool(ctx->buf))
+			a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
+		else
+			a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
+
+		return 0;
+
 	case LEJPVP_SSL_OPTION_SET:
 		a->info->ssl_options_set |= atol(ctx->buf);
 		return 0;
diff --git a/lib/tls/mbedtls/server.c b/lib/tls/mbedtls/server.c
index 69c08a59f16a968380f56dd0b1b26d857d244482..cb526e17898632e03657762f6d59431bd87a79e6 100644
--- a/lib/tls/mbedtls/server.c
+++ b/lib/tls/mbedtls/server.c
@@ -255,12 +255,13 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
 	 * parameter.
 	 */
 	n = lws_tls_use_any_upgrade_check_extant(info->ssl_cert_filepath);
-	if (n < 0)
+	if (n == LWS_TLS_EXTANT_ALTERNATIVE)
 		return 1;
 	m = lws_tls_use_any_upgrade_check_extant(info->ssl_private_key_filepath);
-	if (m < 0)
+	if (m == LWS_TLS_EXTANT_ALTERNATIVE)
 		return 1;
-	if ((n || m) && (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
+	if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
+	    (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
 		lwsl_notice("Ignoring missing %s or %s\n",
 				info->ssl_cert_filepath,
 				info->ssl_private_key_filepath);
diff --git a/lib/tls/openssl/client.c b/lib/tls/openssl/client.c
index 50a280d2ab7d1565909f3d1d2b74e80c91f69a2e..d51c4d403d6e24962337c19696b0ee338f0b544c 100644
--- a/lib/tls/openssl/client.c
+++ b/lib/tls/openssl/client.c
@@ -347,7 +347,11 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
 
 	/* support for client-side certificate authentication */
 	if (cert_filepath) {
-		lwsl_notice("%s: doing cert filepath\n", __func__);
+		if (lws_tls_use_any_upgrade_check_extant(cert_filepath) != LWS_TLS_EXTANT_YES &&
+		    (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT))
+			return 0;
+
+		lwsl_notice("%s: doing cert filepath %s\n", __func__, cert_filepath);
 		n = SSL_CTX_use_certificate_chain_file(vh->ssl_client_ctx,
 						       cert_filepath);
 		if (n < 1) {
diff --git a/lib/tls/openssl/server.c b/lib/tls/openssl/server.c
index 9fb4ee8b9f91724946ed9d3d3331a4ee5e3d4f41..964bbae54294f15338cfce473310f2dc69e1e80f 100644
--- a/lib/tls/openssl/server.c
+++ b/lib/tls/openssl/server.c
@@ -224,12 +224,13 @@ lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info,
 	 */
 
 	n = lws_tls_use_any_upgrade_check_extant(info->ssl_cert_filepath);
-	if (n < 0)
+	if (n == LWS_TLS_EXTANT_ALTERNATIVE)
 		return 1;
 	m = lws_tls_use_any_upgrade_check_extant(info->ssl_private_key_filepath);
-	if (m < 0)
+	if (m == LWS_TLS_EXTANT_ALTERNATIVE)
 		return 1;
-	if ((n || m) && (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
+	if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
+	    (info->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
 		lwsl_notice("Ignoring missing %s or %s\n",
 				info->ssl_cert_filepath,
 				info->ssl_private_key_filepath);
diff --git a/lib/tls/tls.c b/lib/tls/tls.c
index c900c44fc2df695939ecbda68de2dc48f8e8c586..e6e24b883865a5a04e612101079a64abe3a943aa 100644
--- a/lib/tls/tls.c
+++ b/lib/tls/tls.c
@@ -102,22 +102,23 @@ 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;
+	time_t now = (time_t)lws_now_secs(), life = 0;
 	int n;
 
-	if (!v->ssl_ctx)
-		return -1;
+	if (v->ssl_ctx && !v->skipped_certs) {
 
-	if (now < 1464083026) /* May 2016 */
-		/* our clock is wrong and we can't judge the certs */
-		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;
+		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);
+		life = (ir.time - now) / (24 * 3600);
+		lwsl_notice("   vhost %s: cert expiry: %dd\n", v->name, (int)life);
+	} else
+		lwsl_notice("   vhost %s: no cert\n", v->name);
 
 	lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, v,
 		      (size_t)(ssize_t)life);
@@ -138,6 +139,86 @@ lws_tls_check_all_cert_lifetimes(struct lws_context *context)
 	return 0;
 }
 
+static int
+lws_tls_extant(const char *name)
+{
+	/* it exists if we can open it... */
+	int fd = open(name, O_RDONLY), n;
+	char buf[1];
+
+	if (fd < 0)
+		return 1;
+
+	/* and we can read at least one byte out of it */
+	n = read(fd, buf, 1);
+	close(fd);
+
+	return n != 1;
+}
+
+/*
+ * Returns 0 if the filepath "name" exists and can be read from.
+ *
+ * In addition, if "name".upd exists, backup "name" to "name.old.1"
+ * and rename "name".upd to "name" before reporting its existence.
+ *
+ * There are four situations and three results possible:
+ *
+ * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
+ *    be provisioned)
+ *
+ * 2) There are provisioned certs written (xxx.upd) and we still have root
+ *    privs... in this case we rename any existing cert to have a backup name
+ *    and move the upd cert into place with the correct name.  This then becomes
+ *    situation 4 for the caller.
+ *
+ * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
+ *    but we no longer have the privs needed to read or rename them.  In this
+ *    case, indicate that the caller should use temp copies if any we do have
+ *    rights to access.  This is normal after we have updated the cert.
+ *
+ * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
+ *    have the rights to read them.
+ */
+
+enum lws_tls_extant
+lws_tls_use_any_upgrade_check_extant(const char *name)
+{
+	char buf[256];
+	int n;
+
+	lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+	if (!lws_tls_extant(buf)) {
+		/* ah there is an updated file... how about the desired file? */
+		if (!lws_tls_extant(name)) {
+			/* rename the desired file */
+			for (n = 0; n < 50; n++) {
+				lws_snprintf(buf, sizeof(buf) - 1,
+					     "%s.old.%d", name, n);
+				if (!rename(name, buf))
+					break;
+			}
+			if (n == 50) {
+				lwsl_notice("unable to rename %s\n", name);
+
+				return LWS_TLS_EXTANT_ALTERNATIVE;
+			}
+			lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
+		}
+		/* desired file is out of the way, rename the updated file */
+		if (rename(buf, name)) {
+			lwsl_notice("unable to rename %s to %s\n", buf, name);
+
+			return LWS_TLS_EXTANT_ALTERNATIVE;
+		}
+	}
+
+	if (lws_tls_extant(name))
+		return LWS_TLS_EXTANT_NO;
+
+	return LWS_TLS_EXTANT_YES;
+}
+
 int
 lws_gate_accepts(struct lws_context *context, int on)
 {