diff --git a/lib/core/context.c b/lib/core/context.c
index 35da6f50e47d238be7b2b6b37fad4a3a9c9266d7..91bfd17017e7c9192e461df93454df3f7855b944 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -971,7 +971,7 @@ lws_create_vhost(struct lws_context *context,
 
 #ifdef LWS_WITH_ACCESS_LOG
 	if (info->log_filepath) {
-		vh->log_fd = open(info->log_filepath,
+		vh->log_fd = lws_open(info->log_filepath,
 				  O_CREAT | O_APPEND | O_RDWR, 0600);
 		if (vh->log_fd == (int)LWS_INVALID_FILE) {
 			lwsl_err("unable to open log filepath %s\n",
diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c
index e5325895f21a83dc4b0912d6659112bee04d2831..c42b3eac3a4c67e92ec73156ff66280d74cdd578 100644
--- a/lib/core/libwebsockets.c
+++ b/lib/core/libwebsockets.c
@@ -88,6 +88,28 @@ signed char char_to_hex(const char c)
 	return -1;
 }
 
+int lws_open(const char *__file, int __oflag, ...)
+{
+	va_list ap;
+	int n;
+
+	va_start(ap, __oflag);
+	if (((__oflag & O_CREAT) == O_CREAT)
+#if defined(O_TMPFILE)
+		|| ((__oflag & O_TMPFILE) == O_TMPFILE)
+#endif
+	)
+		/* last arg is really a mode_t.  But windows... */
+		n = open(__file, __oflag, va_arg(ap, uint32_t));
+	else
+		n = open(__file, __oflag);
+	va_end(ap);
+
+	lws_plat_apply_FD_CLOEXEC(n);
+
+	return n;
+}
+
 void
 lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi)
 {
diff --git a/lib/core/private.h b/lib/core/private.h
index 7b94782a058a2b2e39b3b0b0e945de3efbded83d..0d0da2be20272bbf7034bd48f69727fb4bb42256 100644
--- a/lib/core/private.h
+++ b/lib/core/private.h
@@ -1382,6 +1382,9 @@ lws_plat_pipe_close(struct lws *wsi);
 int
 lws_create_event_pipes(struct lws_context *context);
 
+void
+lws_plat_apply_FD_CLOEXEC(int n);
+
 const struct lws_plat_file_ops *
 lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path,
 		    const char **vpath);
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 0f81d1027324d709aa1fb7e0cd8cb8110dc15f5b..6c4ebb3f4ecba2415d02fc38d804fcd17b3fcde8 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -2809,7 +2809,7 @@ struct lws_context_creation_info {
 	/**< VHOST: pointer to optional linked list of per-vhost
 	 * options made accessible to protocols */
 	int keepalive_timeout;
-	/**< VHOST: (default = 0 = 60s) seconds to allow remote
+	/**< VHOST: (default = 0 = 5s) seconds to allow remote
 	 * client to hold on to an idle HTTP/1.1 connection */
 	const char *log_filepath;
 	/**< VHOST: filepath to append logs to... this is opened before
@@ -6021,6 +6021,19 @@ lws_is_ssl(struct lws *wsi);
 LWS_VISIBLE LWS_EXTERN int
 lws_is_cgi(struct lws *wsi);
 
+/**
+ * lws_open() - platform-specific wrapper for open that prepares the fd
+ *
+ * \param file: the filepath to open
+ * \param oflag: option flags
+ * \param mode: optional mode of any created file
+ *
+ * This is a wrapper around platform open() that sets options on the fd
+ * according to lws policy.  Currently that is FD_CLOEXEC to stop the opened
+ * fd being available to any child process forked by user code.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_open(const char *__file, int __oflag, ...);
 
 struct lws_wifi_scan { /* generic wlan scan item */
 	struct lws_wifi_scan *next;
diff --git a/lib/misc/daemonize.c b/lib/misc/daemonize.c
index 457f2903b16027130283d451e7f68d45b813e6e7..807ca0f1eb00026bd3cf3ff2aba16b6ceeca6000 100644
--- a/lib/misc/daemonize.c
+++ b/lib/misc/daemonize.c
@@ -49,7 +49,7 @@ child_handler(int signum)
 		if (lock_path) {
 			/* Create the lock file as the current user */
 
-			fd = open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
+			fd = lws_open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
 			if (fd < 0) {
 				fprintf(stderr,
 				   "unable to create lock file %s, code=%d (%s)\n",
@@ -106,7 +106,7 @@ lws_daemonize(const char *_lock_path)
 //		return 1;
 
 	if (_lock_path) {
-		fd = open(_lock_path, O_RDONLY);
+		fd = lws_open(_lock_path, O_RDONLY);
 		if (fd >= 0) {
 			n = read(fd, buf, sizeof(buf));
 			close(fd);
diff --git a/lib/plat/esp32/esp32-file.c b/lib/plat/esp32/esp32-file.c
index 0b25a5530fb2c567d8beddfb86ba06f5bf4622ee..cfddd7acaf6b245b128b91c241a1834eebe77955 100644
--- a/lib/plat/esp32/esp32-file.c
+++ b/lib/plat/esp32/esp32-file.c
@@ -29,6 +29,10 @@
 #include <lwip/sockets.h>
 #include <esp_task_wdt.h>
 
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+}
+
 
 LWS_VISIBLE lws_fop_fd_t IRAM_ATTR
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
diff --git a/lib/plat/optee/lws-plat-optee.c b/lib/plat/optee/lws-plat-optee.c
index 79e3137a96655f4f050016088aabd73c0e0726fb..d5ba9017f4c62a0a5dceb37614fa04c665d74683 100644
--- a/lib/plat/optee/lws-plat-optee.c
+++ b/lib/plat/optee/lws-plat-optee.c
@@ -4,6 +4,10 @@
  * included from libwebsockets.c for OPTEE builds
  */
 
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+}
+
 int
 lws_plat_pipe_create(struct lws *wsi)
 {
diff --git a/lib/plat/unix/unix-file.c b/lib/plat/unix/unix-file.c
index 84dcc5bddc9498981bbbfb38ba4fffba59e62b54..32392970a69a07cd2c4623af8c0b5b575fdb453d 100644
--- a/lib/plat/unix/unix-file.c
+++ b/lib/plat/unix/unix-file.c
@@ -30,13 +30,18 @@
 #endif
 #include <dirent.h>
 
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+	if (n != -1)
+		fcntl(n, F_SETFD, FD_CLOEXEC );
+}
 
 int
 lws_plat_write_file(const char *filename, void *buf, int len)
 {
 	int m, fd;
 
-	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 
 	if (fd == -1)
 		return 1;
@@ -50,7 +55,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
 int
 lws_plat_read_file(const char *filename, void *buf, int len)
 {
-	int n, fd = open(filename, O_RDONLY);
+	int n, fd = lws_open(filename, O_RDONLY);
 	if (fd == -1)
 		return -1;
 
@@ -65,7 +70,7 @@ _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
 		    const char *vpath, lws_fop_flags_t *flags)
 {
 	struct stat stat_buf;
-	int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664);
+	int ret = lws_open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664);
 	lws_fop_fd_t fop_fd;
 
 	if (ret < 0)
diff --git a/lib/plat/unix/unix-init.c b/lib/plat/unix/unix-init.c
index 10a748a5de9f3af0744dc5840fd644755c6fc327..a71718e7cee64d5513f062cce4baae2a09734010 100644
--- a/lib/plat/unix/unix-init.c
+++ b/lib/plat/unix/unix-init.c
@@ -47,7 +47,7 @@ lws_plat_init(struct lws_context *context,
 
 	lwsl_info(" mem: platform fd map: %5lu bytes\n",
 		    (unsigned long)(sizeof(struct lws *) * context->max_fds));
-	fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
+	fd = lws_open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
 
 	context->fd_random = fd;
 	if (context->fd_random < 0) {
diff --git a/lib/plat/windows/windows-file.c b/lib/plat/windows/windows-file.c
index e798775f5fb19dc53dd769ff60040e762a366356..74920bd25f4ca534ad0520942b49fc4dfffca72b 100644
--- a/lib/plat/windows/windows-file.c
+++ b/lib/plat/windows/windows-file.c
@@ -24,6 +24,10 @@
 #endif
 #include "core/private.h"
 
+void lws_plat_apply_FD_CLOEXEC(int n)
+{
+}
+
 lws_fop_fd_t
 _lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
 		    const char *vpath, lws_fop_flags_t *flags)
@@ -142,7 +146,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
 {
 	int m, fd;
 
-	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	fd = lws_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 
 	if (fd == -1)
 		return -1;
@@ -156,7 +160,7 @@ lws_plat_write_file(const char *filename, void *buf, int len)
 int
 lws_plat_read_file(const char *filename, void *buf, int len)
 {
-	int n, fd = open(filename, O_RDONLY);
+	int n, fd = lws_open(filename, O_RDONLY);
 	if (fd == -1)
 		return -1;
 
diff --git a/lib/roles/cgi/cgi-server.c b/lib/roles/cgi/cgi-server.c
index 7f3e5b64ef21eacdfe5af2670f04b74adee582df..95121b4ddf957891da6c9212fff7e6619e412a9c 100644
--- a/lib/roles/cgi/cgi-server.c
+++ b/lib/roles/cgi/cgi-server.c
@@ -441,9 +441,14 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 		lwsl_info("%s: cgi %p spawned PID %d\n", __func__,
 			   cgi, cgi->pid);
 
-		/* close: stdin:r, stdout:w, stderr:w */
-		for (n = 0; n < 3; n++)
+		/*
+		 *  close:                stdin:r, stdout:w, stderr:w
+		 * hide from other forks: stdin:w, stdout:r, stderr:r
+		 */
+		for (n = 0; n < 3; n++) {
+			lws_plat_apply_FD_CLOEXEC(cgi->pipe_fds[n][!!(n == 0)]);
 			close(cgi->pipe_fds[n][!(n == 0)]);
+		}
 
 		/* inform cgi owner of the child PID */
 		n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c
index e9ce854cfc74352bab9a28acd54fc3b22eb4d020..f9ac68a5403797df6c3f0b9a6ad1f0d16d39a888 100644
--- a/lib/roles/http/server/lejp-conf.c
+++ b/lib/roles/http/server/lejp-conf.c
@@ -779,7 +779,7 @@ lwsws_get_config(void *user, const char *f, const char * const *paths,
 	struct lejp_ctx ctx;
 	int n, m, fd;
 
-	fd = open(f, O_RDONLY);
+	fd = lws_open(f, O_RDONLY);
 	if (fd < 0) {
 		lwsl_err("Cannot open %s\n", f);
 		return 2;
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 583656384199acea0ad60ba438395f825f8fbf63..730a4844f70db24c6576050c6664baaabc16d629 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -694,7 +694,7 @@ lws_find_string_in_file(const char *filename, const char *string, int stringlen)
 	char buf[128];
 	int fd, match = 0, pos = 0, n = 0, hit = 0;
 
-	fd = open(filename, O_RDONLY);
+	fd = lws_open(filename, O_RDONLY);
 	if (fd < 0) {
 		lwsl_err("can't open auth file: %s\n", filename);
 		return 0;
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c b/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c
index a819e3f98f081ce96d1d0a6bb0082212d93d88b1..486e9e643b088c673aacc78d3281753e0f51ea7b 100644
--- a/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c
+++ b/minimal-examples/http-server/minimal-http-server-form-post-file/minimal-http-server-form-post-file.c
@@ -58,7 +58,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 		/* remove any scary things like .. */
 		lws_filename_purify_inplace(pss->filename);
 		/* open a file of that name for write in the cwd */
-		pss->fd = open(pss->filename, O_CREAT | O_TRUNC | O_RDWR, 0600);
+		pss->fd = lws_open(pss->filename, O_CREAT | O_TRUNC | O_RDWR, 0600);
 		if (pss->fd == LWS_INVALID_FILE) {
 			lwsl_notice("Failed to open output file %s\n",
 				    pss->filename);
diff --git a/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c b/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
index 74b0c422e0b43f26407e4eb8e7b021741e4bc3ca..b350554079a263848624319ace03549c82f1d27d 100644
--- a/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
+++ b/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
@@ -36,7 +36,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 	case LWS_CALLBACK_PROTOCOL_INIT:
 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
 				lws_get_protocol(wsi), sizeof(struct raw_vhd));
-		vhd->u.filefd = open(filepath, O_RDWR);
+		vhd->u.filefd = lws_open(filepath, O_RDWR);
 		if (vhd->u.filefd == -1) {
 			lwsl_err("Unable to open %s\n", filepath);
 
diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c
index 4cb42e60a5317bfb582f23d024ec92271ebf1ec9..828ef95bf3e0a5315c5df5de14320371c3ed86b7 100644
--- a/plugins/acme-client/protocol_lws_acme_client.c
+++ b/plugins/acme-client/protocol_lws_acme_client.c
@@ -623,7 +623,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
 		 */
 		lws_snprintf(buf, sizeof(buf) - 1, "%s.upd",
 			     vhd->pvop[LWS_TLS_SET_CERT_PATH]);
-		vhd->fd_updated_cert = open(buf, LWS_O_WRONLY | LWS_O_CREAT |
+		vhd->fd_updated_cert = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT |
 						 LWS_O_TRUNC, 0600);
 		if (vhd->fd_updated_cert < 0) {
 			lwsl_err("unable to create update cert file %s\n", buf);
@@ -631,7 +631,7 @@ callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
 		}
 		lws_snprintf(buf, sizeof(buf) - 1, "%s.upd",
 			     vhd->pvop[LWS_TLS_SET_KEY_PATH]);
-		vhd->fd_updated_key = open(buf, LWS_O_WRONLY | LWS_O_CREAT |
+		vhd->fd_updated_key = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT |
 						LWS_O_TRUNC, 0600);
 		if (vhd->fd_updated_key < 0) {
 			lwsl_err("unable to create update key file %s\n", buf);
diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c
index 699d9c428dead3bde09ef733121c400884d605a1..abef4ca664481d63c623bf9a720a0fcbece6bf6d 100644
--- a/plugins/protocol_lws_raw_test.c
+++ b/plugins/protocol_lws_raw_test.c
@@ -128,7 +128,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 			lwsl_err("mkfifo failed\n");
 			return 1;
 		}
-		vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
+		vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
 		if (vhd->fifo == -1) {
 			lwsl_err("opening fifo failed\n");
 			unlink(vhd->fifo_path);
@@ -203,7 +203,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 			vhd->zero_length_read = 0;
 			close(vhd->fifo);
 			/* the wsi that adopted the fifo file is closing... reopen the fifo and readopt */
-			vhd->fifo = open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
+			vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
 			if (vhd->fifo == -1) {
 				lwsl_err("opening fifo failed\n");
 				return 1;
diff --git a/plugins/protocol_lws_server_status.c b/plugins/protocol_lws_server_status.c
index 4ee1bf4f4ee47d70dbdaac0b1ee3751c07e3a6e6..c37f05c3f896880da36e1b8e06c0af693bf15c33 100644
--- a/plugins/protocol_lws_server_status.c
+++ b/plugins/protocol_lws_server_status.c
@@ -90,7 +90,7 @@ update(struct per_vhost_data__lws_server_status *v)
 			p += n;
 			l -= n;
 		}
-		fd = open(fp->filepath, LWS_O_RDONLY);
+		fd = lws_open(fp->filepath, LWS_O_RDONLY);
 		if (fd >= 0) {
 			n = read(fd, contents, sizeof(contents) - 1);
 			if (n >= 0) {
diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c
index ad3d6ae0dac1e8e5c1b03a75da87bf7d9d82b568..1590416f43052899829c74159ad8ebb9fc0c2498 100644
--- a/plugins/protocol_lws_sshd_demo.c
+++ b/plugins/protocol_lws_sshd_demo.c
@@ -397,9 +397,9 @@ callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
 		 * deal with it down /etc/.. when just after this we will lose
 		 * the privileges needed to read / write /etc/...
 		 */
-		vhd->privileged_fd = open(TEST_SERVER_KEY_PATH, O_RDONLY);
+		vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY);
 		if (vhd->privileged_fd == -1)
-			vhd->privileged_fd = open(TEST_SERVER_KEY_PATH,
+			vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH,
 					O_CREAT | O_TRUNC | O_RDWR, 0600);
 		if (vhd->privileged_fd == -1) {
 			lwsl_err("%s: Can't open %s\n", __func__,
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index b7744838eecd9649de8c616d3abc403973a23099..8b209273be77a3aaa3a445fcc8ea5352933a0c1f 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -80,7 +80,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 		 * simple demo use a fixed name so we don't have to deal with
 		 * attacks  */
 #if !defined(LWS_WITH_ESP32)
-		pss->fd = (lws_filefd_type)(long long)open("/tmp/post-file",
+		pss->fd = (lws_filefd_type)(long long)lws_open("/tmp/post-file",
 			       O_CREAT | O_TRUNC | O_RDWR, 0600);
 #endif
 		break;
diff --git a/test-apps/test-sshd.c b/test-apps/test-sshd.c
index 2a5dada6df437b80cecf6855a03e363771521c3f..f673eb7ad11499d616365eb177849ec897449fd7 100644
--- a/test-apps/test-sshd.c
+++ b/test-apps/test-sshd.c
@@ -232,7 +232,7 @@ ssh_ops_rx(void *_priv, struct lws *wsi, const uint8_t *buf, uint32_t len)
 static size_t
 ssh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len)
 {
-	int fd = open(TEST_SERVER_KEY_PATH, O_RDONLY), n;
+	int fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY), n;
 
 	if (fd == -1) {
 		lwsl_err("%s: unable to open %s for read: %s\n", __func__,
@@ -255,7 +255,7 @@ ssh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len)
 static size_t
 ssh_ops_set_server_key(struct lws *wsi, uint8_t *buf, size_t len)
 {
-	int fd = open(TEST_SERVER_KEY_PATH, O_CREAT | O_TRUNC | O_RDWR, 0600);
+	int fd = lws_open(TEST_SERVER_KEY_PATH, O_CREAT | O_TRUNC | O_RDWR, 0600);
 	int n;
 
 	lwsl_notice("%s: %d\n", __func__, fd);