diff --git a/lib/client/client-handshake.c b/lib/client/client-handshake.c
index d1b71cd7a98d33c7be0944458f6b90d013b6922b..5062b4dd797dc78ae0f6411e2f3abffbccb40d7e 100644
--- a/lib/client/client-handshake.c
+++ b/lib/client/client-handshake.c
@@ -463,19 +463,19 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN);
 	if (p)
-		strncpy(origin, p, sizeof(origin) - 1);
+		lws_strncpy(origin, p, sizeof(origin));
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
 	if (p)
-		strncpy(protocol, p, sizeof(protocol) - 1);
+		lws_strncpy(protocol, p, sizeof(protocol));
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
 	if (p)
-		strncpy(method, p, sizeof(method) - 1);
+		lws_strncpy(method, p, sizeof(method));
 
 	p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
 	if (p)
-		strncpy(method, p, sizeof(iface) - 1);
+		lws_strncpy(method, p, sizeof(iface));
 
 	lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n",
 		   address, port, path, ssl);
@@ -1040,14 +1040,14 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 		/* length of the user name */
 		pt->serv_buf[len++] = n;
 		/* user name */
-		strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
-			context->pt_serv_buf_size - len);
+		lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_user,
+			context->pt_serv_buf_size - len + 1);
 		len += n;
 		/* length of the password */
 		pt->serv_buf[len++] = passwd_len;
 		/* password */
-		strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
-			context->pt_serv_buf_size - len);
+		lws_strncpy((char *)&pt->serv_buf[len], wsi->vhost->socks_password,
+			context->pt_serv_buf_size - len + 1);
 		len += passwd_len;
 		break;
 
@@ -1066,8 +1066,8 @@ void socks_generate_msg(struct lws *wsi, enum socks_msg_type type,
 		n = len++;
 
 		/* the address we tell SOCKS proxy to connect to */
-		strncpy((char *)&(pt->serv_buf[len]), wsi->stash->address,
-			context->pt_serv_buf_size - len);
+		lws_strncpy((char *)&(pt->serv_buf[len]), wsi->stash->address,
+			context->pt_serv_buf_size - len + 1);
 		len += strlen(wsi->stash->address);
 		net_num = htons(wsi->c_port);
 
diff --git a/lib/client/client.c b/lib/client/client.c
index cf119c88e2fa0b9e37c5699a6a3e619f9983dae8..13fe80bd1732665cb888dfb3249245e5e5cc728b 100644
--- a/lib/client/client.c
+++ b/lib/client/client.c
@@ -644,16 +644,13 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 			port = wsi->c_port;
 			/* +1 as lws_client_reset expects leading / omitted */
 			path = new_path + 1;
-			strncpy(new_path, lws_hdr_simple_ptr(wsi,
-							 _WSI_TOKEN_CLIENT_URI),
-							 sizeof(new_path));
-			new_path[sizeof(new_path) - 1] = '\0';
+			lws_strncpy(new_path, lws_hdr_simple_ptr(wsi,
+				   _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
 			q = strrchr(new_path, '/');
-			if (q) {
-				strncpy(q + 1, p, sizeof(new_path) -
-							(q - new_path) - 1);
-				new_path[sizeof(new_path) - 1] = '\0';
-			} else
+			if (q)
+				lws_strncpy(q + 1, p, sizeof(new_path) -
+							(q - new_path));
+			else
 				path = p;
 		}
 
diff --git a/lib/context.c b/lib/context.c
index b97dc6d23f28ed718ac68d0fd04f3941d1ac6c2c..d309906ad5a58c8c652904de3d1b174fb74e77d2 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -626,7 +626,8 @@ lws_create_vhost(struct lws_context *context,
 
 #ifdef LWS_OPENSSL_SUPPORT
 	if (info->ecdh_curve)
-		strncpy(vh->ecdh_curve, info->ecdh_curve, sizeof(vh->ecdh_curve) - 1);
+		lws_strncpy(vh->ecdh_curve, info->ecdh_curve,
+			    sizeof(vh->ecdh_curve));
 #endif
 
 	/* carefully allocate and take a copy of cert + key paths if present */
diff --git a/lib/event-libs/libuv.c b/lib/event-libs/libuv.c
index 788943fffdf757986e4bd51e1efef3fce4af6042..1bfc465505ac5351f6308a712317f4d940de4cee 100644
--- a/lib/event-libs/libuv.c
+++ b/lib/event-libs/libuv.c
@@ -635,8 +635,7 @@ lws_plat_plugins_init(struct lws_context *context, const char * const *d)
 			}
 			plugin->list = context->plugin_list;
 			context->plugin_list = plugin;
-			strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1);
-			plugin->name[sizeof(plugin->name) - 1] = '\0';
+			lws_strncpy(plugin->name, dent.name, sizeof(plugin->name));
 			plugin->lib = lib;
 			plugin->caps = lcaps;
 			context->plugin_protocol_count += lcaps.count_protocols;
diff --git a/lib/http2/http2.c b/lib/http2/http2.c
index 00a5eb38fa91954f488411e308763b79051a3b26..104042c34f1d6e37a66b0d0dca3efe320f59ccdd 100644
--- a/lib/http2/http2.c
+++ b/lib/http2/http2.c
@@ -279,8 +279,7 @@ lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason)
 
 	pps->u.ga.err = err;
 	pps->u.ga.highest_sid = h2n->highest_sid;
-	strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str) - 1);
-	pps->u.ga.str[sizeof(pps->u.ga.str) - 1] = '\0';
+	lws_strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str));
 	lws_pps_schedule(wsi, pps);
 
 	h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
diff --git a/lib/jws/jwk.c b/lib/jws/jwk.c
index 2a93843ebdbcfa2cec8dda66369c3b1947699a84..000da84fae6c12d6594ddb870d1bfa25349adad8 100644
--- a/lib/jws/jwk.c
+++ b/lib/jws/jwk.c
@@ -67,8 +67,7 @@ cb_jwk(struct lejp_ctx *ctx, char reason)
 
 	switch (ctx->path_match - 1) {
 	case JWK_KTY:
-		strncpy(s->keytype, ctx->buf, sizeof(s->keytype) - 1);
-		s->keytype[sizeof(s->keytype) - 1] = '\0';
+		lws_strncpy(s->keytype, ctx->buf, sizeof(s->keytype));
 		if (!strcmp(ctx->buf, "oct")) {
 			break;
 		}
diff --git a/lib/jws/jws.c b/lib/jws/jws.c
index b14cf67156b85b3bdd46fac278a98a77842de1c3..feacfe0ca0a552ce03e264d6be53e3aea1606348 100644
--- a/lib/jws/jws.c
+++ b/lib/jws/jws.c
@@ -202,8 +202,7 @@ cb_hdr(struct lejp_ctx *ctx, char reason)
 			return -1;
 		break;
 	case JHP_ALG:
-		strncpy(s->alg, ctx->buf, sizeof(s->alg) - 1);
-		s->alg[sizeof(s->alg) - 1] = '\0';
+		lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
 		if (!strcmp(ctx->buf, "HS256")) {
 			s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
 			break;
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index c5a0b5465ffdf76b6483fd334a8e87d83b03f808..957d9b89ad2444cc2b2fcaed2094436ba0f798d9 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -1644,7 +1644,7 @@ lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
 		if ((unsigned int)(p - proxy) > sizeof(authstring) - 1)
 			goto auth_too_long;
 
-		strncpy(authstring, proxy, p - proxy);
+		lws_strncpy(authstring, proxy, p - proxy + 1);
 		// null termination not needed on input
 		if (lws_b64_encode_string(authstring, lws_ptr_diff(p, proxy),
 				vhost->proxy_basic_auth_token,
@@ -1657,10 +1657,8 @@ lws_set_proxy(struct lws_vhost *vhost, const char *proxy)
 	} else
 		vhost->proxy_basic_auth_token[0] = '\0';
 
-	strncpy(vhost->http_proxy_address, proxy,
-				sizeof(vhost->http_proxy_address) - 1);
-	vhost->http_proxy_address[
-				sizeof(vhost->http_proxy_address) - 1] = '\0';
+	lws_strncpy(vhost->http_proxy_address, proxy,
+		    sizeof(vhost->http_proxy_address));
 
 	p = strchr(vhost->http_proxy_address, ':');
 	if (!p && !vhost->http_proxy_port) {
@@ -1720,9 +1718,9 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
 				goto bail;
 			}
 
-			strncpy(vhost->socks_user, socks, p_colon - socks);
-			strncpy(vhost->socks_password, p_colon + 1,
-				p_at - (p_colon + 1));
+			lws_strncpy(vhost->socks_user, socks, p_colon - socks + 1);
+			lws_strncpy(vhost->socks_password, p_colon + 1,
+				p_at - (p_colon + 1) + 1);
 		}
 
 		lwsl_info(" Socks auth, user: %s, password: %s\n",
@@ -1731,10 +1729,8 @@ lws_set_socks(struct lws_vhost *vhost, const char *socks)
 		socks = p_at + 1;
 	}
 
-	strncpy(vhost->socks_proxy_address, socks,
-				sizeof(vhost->socks_proxy_address) - 1);
-	vhost->socks_proxy_address[sizeof(vhost->socks_proxy_address) - 1]
-		= '\0';
+	lws_strncpy(vhost->socks_proxy_address, socks,
+		    sizeof(vhost->socks_proxy_address));
 
 	p_colon = strchr(vhost->socks_proxy_address, ':');
 	if (!p_colon && !vhost->socks_proxy_port) {
@@ -2722,6 +2718,15 @@ lws_snprintf(char *str, size_t size, const char *format, ...)
 	return n;
 }
 
+char *
+lws_strncpy(char *dest, const char *src, size_t size)
+{
+	strncpy(dest, src, size - 1);
+	dest[size - 1] = '\0';
+
+	return dest;
+}
+
 
 LWS_VISIBLE LWS_EXTERN int
 lws_is_cgi(struct lws *wsi) {
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 2cb9bc6ecb6b6a305bec0861ffc487441a4b5ecf..00e6485a0c4a90a73ce593cabaa31be33a66642b 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -5315,6 +5315,19 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 LWS_VISIBLE LWS_EXTERN int
 lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
 
+/**
+ * lws_strncpy(): strncpy that guarantees NUL on truncated copy
+ *
+ * \param dest: destination buffer
+ * \param src: source buffer
+ * \param size: bytes left in destination buffer
+ *
+ * This lets you correctly truncate buffers by concatenating lengths, if you
+ * reach the limit the reported length doesn't exceed the limit.
+ */
+LWS_VISIBLE LWS_EXTERN char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
 /**
  * lws_get_random(): fill a buffer with platform random data
  *
diff --git a/lib/plat/lws-plat-esp32.c b/lib/plat/lws-plat-esp32.c
index 35006efd20193dbdf8009d36848dcb6f7132b994..7310bd0c6cca71c7565dd2d5b2f5952015d89e42 100644
--- a/lib/plat/lws-plat-esp32.c
+++ b/lib/plat/lws-plat-esp32.c
@@ -898,8 +898,7 @@ get_txt_param(const mdns_result_t *mr, const char *param, char *result, int len)
 		return 1;
 	}
 
-	strncpy(result, mr->txt->value, len);
-	result[len - 1] = '\0';
+	lws_strncpy(result, mr->txt->value, len);
 
 	return 0;
 }
@@ -938,8 +937,7 @@ next:
 			p = lws_malloc(sizeof(*p), "group");
 			if (!p)
 				continue;
-			strncpy(p->host, r->hostname, sizeof(p->host) - 1);
-			p->host[sizeof(p->host) - 1] = '\0';
+			lws_strncpy(p->host, r->hostname, sizeof(p->host));
 
 			get_txt_param(r, "model", p->model, sizeof(p->model));
 			get_txt_param(r, "role", p->role, sizeof(p->role));
@@ -1098,14 +1096,13 @@ hit:
 		lwsl_info("Attempting connection with slot %d: %s:\n", m,
 				lws_esp32.ssid[m]);
 		/* set the ssid we last tried to connect to */
-		strncpy(lws_esp32.active_ssid, lws_esp32.ssid[m],
-				sizeof(lws_esp32.active_ssid) - 1);
-		lws_esp32.active_ssid[sizeof(lws_esp32.active_ssid) - 1] = '\0';
-
-		strncpy((char *)sta_config.sta.ssid, lws_esp32.ssid[m],
-			sizeof(sta_config.sta.ssid) - 1);
-		strncpy((char *)sta_config.sta.password, lws_esp32.password[m],
-			sizeof(sta_config.sta.password) - 1);
+		lws_strncpy(lws_esp32.active_ssid, lws_esp32.ssid[m],
+				sizeof(lws_esp32.active_ssid));
+
+		lws_strncpy((char *)sta_config.sta.ssid, lws_esp32.ssid[m],
+			sizeof(sta_config.sta.ssid));
+		lws_strncpy((char *)sta_config.sta.password, lws_esp32.password[m],
+			sizeof(sta_config.sta.password));
 
 		tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA,
 					   (const char *)&config.ap.ssid[7]);
diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c
index b2868cb4fe2a3cd465ae8214ae62478b5f927a82..fe1fbfe6299a4e92a090d753b32097f801667192 100644
--- a/lib/plat/lws-plat-unix.c
+++ b/lib/plat/lws-plat-unix.c
@@ -513,8 +513,8 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
 			}
 			plugin->list = context->plugin_list;
 			context->plugin_list = plugin;
-			strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
-			plugin->name[sizeof(plugin->name) - 1] = '\0';
+			lws_strncpy(plugin->name, namelist[i]->d_name,
+				    sizeof(plugin->name));
 			plugin->l = l;
 			plugin->caps = lcaps;
 			context->plugin_protocol_count += lcaps.count_protocols;
diff --git a/lib/server/fops-zip.c b/lib/server/fops-zip.c
index 8b68ce210dbe8d9ee947c788f7788d6d400ace71..f8ede1f25fa93e300699f21862a5355242a784aa 100644
--- a/lib/server/fops-zip.c
+++ b/lib/server/fops-zip.c
@@ -349,8 +349,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
 	m = sizeof(rp) - 1;
 	if ((vpath - vfs_path - 1) < m)
 		m = lws_ptr_diff(vpath, vfs_path) - 1;
-	strncpy(rp, vfs_path, m);
-	rp[m] = '\0';
+	lws_strncpy(rp, vfs_path, m + 1);
 
 	/* open the zip file itself using the incoming fops, not fops_zip */
 
diff --git a/lib/server/lejp-conf.c b/lib/server/lejp-conf.c
index 488680cac3445658a196671eec81820859749062..5a21a5d03a87c2412329c2c4bd6c490323deffd5 100644
--- a/lib/server/lejp-conf.c
+++ b/lib/server/lejp-conf.c
@@ -732,7 +732,7 @@ dostring:
 		n = p1 - p;
 		if (n > a->end - a->p)
 			n = a->end - a->p;
-		strncpy(a->p, p, n);
+		lws_strncpy(a->p, p, n + 1);
 		a->p += n;
 		a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
 		p += n + strlen(ESC_INSTALL_DATADIR);
diff --git a/lib/server/server.c b/lib/server/server.c
index 605bfeac60ffedf1fa198f18f88fc696b2d904c8..4793d57c028b1c7de51533f2c74b5234ca8ee253 100644
--- a/lib/server/server.c
+++ b/lib/server/server.c
@@ -2816,10 +2816,9 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 
 #if defined(LWS_WITH_RANGES)
 	if (ranges >= 2) { /* multipart byteranges */
-		strncpy(wsi->http.multipart_content_type, content_type,
-			sizeof(wsi->http.multipart_content_type) - 1);
-		wsi->http.multipart_content_type[
-		         sizeof(wsi->http.multipart_content_type) - 1] = '\0';
+		lws_strncpy(wsi->http.multipart_content_type, content_type,
+			sizeof(wsi->http.multipart_content_type));
+
 		if (lws_add_http_header_by_token(wsi,
 						 WSI_TOKEN_HTTP_CONTENT_TYPE,
 						 (unsigned char *)
diff --git a/lib/tls/mbedtls/wrapper/library/ssl_lib.c b/lib/tls/mbedtls/wrapper/library/ssl_lib.c
index d8fdd06fadeb8e3f33ddc79e4d7f3741033ef02c..cf553e22f1fba333c6b92958f8c26d6e09b57003 100644
--- a/lib/tls/mbedtls/wrapper/library/ssl_lib.c
+++ b/lib/tls/mbedtls/wrapper/library/ssl_lib.c
@@ -19,6 +19,9 @@
 #include "ssl_dbg.h"
 #include "ssl_port.h"
 
+char *
+lws_strncpy(char *dest, const char *src, size_t size);
+
 #define SSL_SEND_DATA_MAX_LENGTH 1460
 
 /**
@@ -1582,7 +1585,7 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C
 
 void ERR_error_string_n(unsigned long e, char *buf, size_t len)
 {
-	strncpy(buf, "unknown", len);
+	lws_strncpy(buf, "unknown", len);
 }
 
 void ERR_free_strings(void)
diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c
index a6c47984a024db4f449ffa65a1aa63a3076f493c..9a45b3bee1a743e8e30cce97a5eeae2afddcd530 100644
--- a/lib/tls/openssl/ssl.c
+++ b/lib/tls/openssl/ssl.c
@@ -45,19 +45,19 @@ int lws_ssl_get_error(struct lws *wsi, int n)
 char* lws_ssl_get_error_string(int status, int ret, char *buf, size_t len) {
 	switch (status) {
 	case SSL_ERROR_NONE:
-		return strncpy(buf, "SSL_ERROR_NONE", len);
+		return lws_strncpy(buf, "SSL_ERROR_NONE", len);
 	case SSL_ERROR_ZERO_RETURN:
-		return strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
+		return lws_strncpy(buf, "SSL_ERROR_ZERO_RETURN", len);
 	case SSL_ERROR_WANT_READ:
-		return strncpy(buf, "SSL_ERROR_WANT_READ", len);
+		return lws_strncpy(buf, "SSL_ERROR_WANT_READ", len);
 	case SSL_ERROR_WANT_WRITE:
-		return strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
+		return lws_strncpy(buf, "SSL_ERROR_WANT_WRITE", len);
 	case SSL_ERROR_WANT_CONNECT:
-		return strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
+		return lws_strncpy(buf, "SSL_ERROR_WANT_CONNECT", len);
 	case SSL_ERROR_WANT_ACCEPT:
-		return strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
+		return lws_strncpy(buf, "SSL_ERROR_WANT_ACCEPT", len);
 	case SSL_ERROR_WANT_X509_LOOKUP:
-		return strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
+		return lws_strncpy(buf, "SSL_ERROR_WANT_X509_LOOKUP", len);
 	case SSL_ERROR_SYSCALL:
 		switch (ret) {
                 case 0:
diff --git a/lwsws/main.c b/lwsws/main.c
index dda9016ee320154678a8aaaa38210352e0eb51e9..4ba017f6e79d298058e31184b850bdb587c66e31 100644
--- a/lwsws/main.c
+++ b/lwsws/main.c
@@ -218,8 +218,7 @@ int main(int argc, char **argv)
 			debug_level = atoi(optarg);
 			break;
 		case 'c':
-			strncpy(config_dir, optarg, sizeof(config_dir) - 1);
-			config_dir[sizeof(config_dir) - 1] = '\0';
+			lws_strncpy(config_dir, optarg, sizeof(config_dir));
 			break;
 		case 'h':
 			fprintf(stderr, "Usage: lwsws [-c <config dir>] "
diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c
index e1f333405510792eca3c4915b226264d2a2fff46..6d2b304f6d5ab715340a5c28fc3012b011cd4432 100644
--- a/plugins/acme-client/protocol_lws_acme_client.c
+++ b/plugins/acme-client/protocol_lws_acme_client.c
@@ -241,20 +241,20 @@ cb_authz(struct lejp_ctx *ctx, char reason)
 		s->is_sni_02 = !strcmp(ctx->buf, "tls-sni-02");
 		break;
 	case JAAZ_CHALLENGES_STATUS:
-		strncpy(s->status, ctx->buf, sizeof(s->status) - 1);
+		lws_strncpy(s->status, ctx->buf, sizeof(s->status));
 		break;
 	case JAAZ_CHALLENGES_URI:
 		if (s->use) {
-			strncpy(s->challenge_uri, ctx->buf,
-				sizeof(s->challenge_uri) - 1);
+			lws_strncpy(s->challenge_uri, ctx->buf,
+				sizeof(s->challenge_uri));
 			s->yes |= 2;
 		}
 		break;
 	case JAAZ_CHALLENGES_TOKEN:
 		lwsl_notice("JAAZ_CHALLENGES_TOKEN: %s %d\n", ctx->buf, s->use);
 		if (s->use) {
-			strncpy(s->chall_token, ctx->buf,
-				sizeof(s->chall_token) - 1);
+			lws_strncpy(s->chall_token, ctx->buf,
+				sizeof(s->chall_token));
 			s->yes |= 1;
 		}
 		break;
@@ -299,14 +299,14 @@ cb_chac(struct lejp_ctx *ctx, char reason)
 			return 1;
 		break;
 	case JCAC_STATUS:
-		strncpy(s->status, ctx->buf, sizeof(s->status) - 1);
+		lws_strncpy(s->status, ctx->buf, sizeof(s->status));
 		break;
 	case JCAC_URI:
 		s->yes |= 2;
 		break;
 	case JCAC_TOKEN:
-		strncpy(s->chall_token, ctx->buf,
-				sizeof(s->chall_token) - 1);
+		lws_strncpy(s->chall_token, ctx->buf,
+				sizeof(s->chall_token));
 		s->yes |= 1;
 		break;
 	case JCAC_DETAIL:
@@ -363,8 +363,7 @@ lws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh,
 
 	memset(i, 0, sizeof(*i));
 	i->port = 443;
-	strncpy(_url, url, sizeof(_url) - 1);
-	_url[sizeof(_url) - 1] = '\0';
+	lws_strncpy(_url, url, sizeof(_url));
 	if (lws_parse_uri(_url, &prot, &i->address, &i->port, &p)) {
 		lwsl_err("unable to parse uri %s\n", url);
 
@@ -373,8 +372,7 @@ lws_acme_client_connect(struct lws_context *context, struct lws_vhost *vh,
 
 	/* add back the leading / on path */
 	path[0] = '/';
-	strncpy(path + 1, p, sizeof(path) - 2);
-	path[sizeof(path) - 1] = '\0';
+	lws_strncpy(path + 1, p, sizeof(path) - 1);
 	i->path = path;
 	i->context = context;
 	i->vhost = vh;
diff --git a/plugins/generic-sessions/handlers.c b/plugins/generic-sessions/handlers.c
index c3f0a0eda48513964ac1d420daf2bbd8067d4c0b..7daf257dca1341a3c427e899695eea4f6e669f99 100644
--- a/plugins/generic-sessions/handlers.c
+++ b/plugins/generic-sessions/handlers.c
@@ -313,8 +313,8 @@ lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi,
 
 		lwsl_debug("current pw checks out\n");
 
-		strncpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(u.username) - 1);
-		u.username[sizeof(u.username) - 1] = '\0';
+		lws_strncpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME),
+			    sizeof(u.username));
 	}
 
 	/* does he want to delete his account? */
diff --git a/plugins/generic-sessions/protocol_generic_sessions.c b/plugins/generic-sessions/protocol_generic_sessions.c
index 6f768e77a4fa7c11fd3a26f6e658451cdd34d342..eb2ab6759f5a04db8c2267dcdbed967196aaa5ce 100644
--- a/plugins/generic-sessions/protocol_generic_sessions.c
+++ b/plugins/generic-sessions/protocol_generic_sessions.c
@@ -60,8 +60,7 @@ lwsgs_lookup_callback_email(void *priv, int cols, char **col_val,
 
 	for (n = 0; n < cols; n++) {
 		if (!strcmp(col_name[n], "content")) {
-			strncpy(a->buf, col_val[n], a->len - 1);
-			a->buf[a->len - 1] = '\0';
+			lws_strncpy(a->buf, col_val[n], a->len);
 			continue;
 		}
 	}
@@ -82,7 +81,7 @@ lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len)
 		 "select content from email where username='%s';",
 		 lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
 
-	strncpy(buf, "failed", len);
+	lws_strncpy(buf, "failed", len);
 	if (sqlite3_exec(vhd->pdb, ss, lwsgs_lookup_callback_email, &a,
 			 NULL) != SQLITE_OK) {
 		lwsl_err("Unable to lookup email: %s\n",
@@ -178,7 +177,7 @@ lwsgs_email_cb_on_next(struct lws_email *email)
 		 */
 		return 1;
 
-	strncpy(email->email_to, vhd->u.email, sizeof(email->email_to) - 1);
+	lws_strncpy(email->email_to, vhd->u.email, sizeof(email->email_to));
 
 	return 0;
 }
@@ -221,7 +220,7 @@ lwsgs_subst(void *data, int index)
 	} else
 		lwsl_notice("no sid\n");
 
-	strncpy(a->pss->result + 32, u.email, 100);
+	lws_strncpy(a->pss->result + 32, u.email, 100);
 
 	switch (index) {
 	case 0:
@@ -285,38 +284,38 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
 		pvo = (const struct lws_protocol_vhost_options *)in;
 		while (pvo) {
 			if (!strcmp(pvo->name, "admin-user"))
-				strncpy(vhd->admin_user, pvo->value,
-					sizeof(vhd->admin_user) - 1);
+				lws_strncpy(vhd->admin_user, pvo->value,
+					sizeof(vhd->admin_user));
 			if (!strcmp(pvo->name, "admin-password-sha1"))
-				strncpy(vhd->admin_password_sha1.id, pvo->value,
-					sizeof(vhd->admin_password_sha1.id) - 1);
+				lws_strncpy(vhd->admin_password_sha1.id, pvo->value,
+					sizeof(vhd->admin_password_sha1.id));
 			if (!strcmp(pvo->name, "session-db"))
-				strncpy(vhd->session_db, pvo->value,
-					sizeof(vhd->session_db) - 1);
+				lws_strncpy(vhd->session_db, pvo->value,
+					sizeof(vhd->session_db));
 			if (!strcmp(pvo->name, "confounder"))
-				strncpy(vhd->confounder, pvo->value,
-					sizeof(vhd->confounder) - 1);
+				lws_strncpy(vhd->confounder, pvo->value,
+					sizeof(vhd->confounder));
 			if (!strcmp(pvo->name, "email-from"))
-				strncpy(vhd->email.email_from, pvo->value,
-					sizeof(vhd->email.email_from) - 1);
+				lws_strncpy(vhd->email.email_from, pvo->value,
+					sizeof(vhd->email.email_from));
 			if (!strcmp(pvo->name, "email-helo"))
-				strncpy(vhd->email.email_helo, pvo->value,
-					sizeof(vhd->email.email_helo) - 1);
+				lws_strncpy(vhd->email.email_helo, pvo->value,
+					sizeof(vhd->email.email_helo));
 			if (!strcmp(pvo->name, "email-template"))
-				strncpy(vhd->email_template, pvo->value,
-					sizeof(vhd->email_template) - 1);
+				lws_strncpy(vhd->email_template, pvo->value,
+					sizeof(vhd->email_template));
 			if (!strcmp(pvo->name, "email-title"))
-				strncpy(vhd->email_title, pvo->value,
-					sizeof(vhd->email_title) - 1);
+				lws_strncpy(vhd->email_title, pvo->value,
+					sizeof(vhd->email_title));
 			if (!strcmp(pvo->name, "email-contact-person"))
-				strncpy(vhd->email_contact_person, pvo->value,
-					sizeof(vhd->email_contact_person) - 1);
+				lws_strncpy(vhd->email_contact_person, pvo->value,
+					sizeof(vhd->email_contact_person));
 			if (!strcmp(pvo->name, "email-confirm-url-base"))
-				strncpy(vhd->email_confirm_url, pvo->value,
-					sizeof(vhd->email_confirm_url) - 1);
+				lws_strncpy(vhd->email_confirm_url, pvo->value,
+					sizeof(vhd->email_confirm_url));
 			if (!strcmp(pvo->name, "email-server-ip"))
-				strncpy(vhd->email.email_smtp_ip, pvo->value,
-					sizeof(vhd->email.email_smtp_ip) - 1);
+				lws_strncpy(vhd->email.email_smtp_ip, pvo->value,
+					sizeof(vhd->email.email_smtp_ip));
 
 			if (!strcmp(pvo->name, "timeout-idle-secs"))
 				vhd->timeout_idle_secs = atoi(pvo->value);
@@ -433,8 +432,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
 
 		pss->login_session.id[0] = '\0';
 		pss->phs.pos = 0;
-		strncpy(pss->onward, (char *)in, sizeof(pss->onward) - 1);
-		pss->onward[sizeof(pss->onward) - 1] = '\0';
+		lws_strncpy(pss->onward, (char *)in, sizeof(pss->onward));
 
 		if (!strcmp((const char *)in, "/lwsgs-forgot")) {
 			lwsgs_handler_forgot(vhd, wsi, pss);
@@ -530,10 +528,9 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
 				 sqlite3_errmsg(vhd->pdb));
 			break;
 		}
-		strncpy(sinfo->username, u.username, sizeof(sinfo->username) - 1);
-		sinfo->username[sizeof(sinfo->username) - 1] = '\0';
-		strncpy(sinfo->email, u.email, sizeof(sinfo->email) - 1);
-		strncpy(sinfo->session, sid.id, sizeof(sinfo->session) - 1);
+		lws_strncpy(sinfo->username, u.username, sizeof(sinfo->username));
+		lws_strncpy(sinfo->email, u.email, sizeof(sinfo->email));
+		lws_strncpy(sinfo->session, sid.id, sizeof(sinfo->session));
 		sinfo->mask = lwsgs_get_auth_level(vhd, username);
 		lws_get_peer_simple(wsi, sinfo->ip, sizeof(sinfo->ip));
 	}
@@ -600,7 +597,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
 			cp = lws_spa_get_string(pss->spa, FGS_BAD);
 			lwsl_notice("user/password no good %s\n",
 				lws_spa_get_string(pss->spa, FGS_USERNAME));
-			strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
+			lws_strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
 			pss->onward[sizeof(pss->onward) - 1] = '\0';
 			goto completion_flow;
 		}
@@ -638,9 +635,8 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
 					lws_email_check(&vhd->email);
 				}
 reg_done:
-				strncpy(pss->onward, lws_spa_get_string(pss->spa, n),
-					sizeof(pss->onward) - 1);
-				pss->onward[sizeof(pss->onward) - 1] = '\0';
+				lws_strncpy(pss->onward, lws_spa_get_string(pss->spa, n),
+					    sizeof(pss->onward));
 				pss->login_expires = 0;
 				pss->logging_out = 1;
 				goto completion_flow;
@@ -678,9 +674,8 @@ reg_done:
 				return -1;
 			}
 
-			strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_BAD),
-				sizeof(pss->onward) - 1);
-			pss->onward[sizeof(pss->onward) - 1] = '\0';
+			lws_strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_BAD),
+				    sizeof(pss->onward));
 			lwsl_debug("failed\n");
 
 			goto completion_flow;
@@ -702,8 +697,8 @@ reg_done:
 				return -1;
 			}
 
-			strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_GOOD), sizeof(pss->onward) - 1);
-			pss->onward[sizeof(pss->onward) - 1] = '\0';
+			lws_strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_GOOD),
+				    sizeof(pss->onward));
 
 			pss->login_expires = 0;
 			pss->logging_out = 1;
@@ -714,8 +709,7 @@ reg_done:
 		break;
 
 pass:
-		strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
-		pss->onward[sizeof(pss->onward) - 1] = '\0';
+		lws_strncpy(pss->onward, cp, sizeof(pss->onward));
 
 		if (lwsgs_get_sid_from_wsi(wsi, &sid))
 			sid.id[0] = '\0';
diff --git a/plugins/generic-sessions/protocol_lws_messageboard.c b/plugins/generic-sessions/protocol_lws_messageboard.c
index d462f1e4b9f16e9f3a55658a22a7472cf86424e5..3c20c90f1b5bc3db99149b5d940a103473e8b7e2 100644
--- a/plugins/generic-sessions/protocol_lws_messageboard.c
+++ b/plugins/generic-sessions/protocol_lws_messageboard.c
@@ -85,23 +85,19 @@ lookup_cb(void *priv, int cols, char **col_val, char **col_name)
 			continue;
 		}
 		if (!strcmp(col_name[n], "username")) {
-			strncpy(m->username, col_val[n], sizeof(m->username) - 1);
-			m->username[sizeof(m->username) - 1] = '\0';
+			lws_strncpy(m->username, col_val[n], sizeof(m->username));
 			continue;
 		}
 		if (!strcmp(col_name[n], "email")) {
-			strncpy(m->email, col_val[n], sizeof(m->email) - 1);
-			m->email[sizeof(m->email) - 1] = '\0';
+			lws_strncpy(m->email, col_val[n], sizeof(m->email));
 			continue;
 		}
 		if (!strcmp(col_name[n], "ip")) {
-			strncpy(m->ip, col_val[n], sizeof(m->ip) - 1);
-			m->ip[sizeof(m->ip) - 1] = '\0';
+			lws_strncpy(m->ip, col_val[n], sizeof(m->ip));
 			continue;
 		}
 		if (!strcmp(col_name[n], "content")) {
-			strncpy(m->content, col_val[n], sizeof(m->content) - 1);
-			m->content[sizeof(m->content) - 1] = '\0';
+			lws_strncpy(m->content, col_val[n], sizeof(m->content));
 			continue;
 		}
 	}
diff --git a/plugins/generic-sessions/utils.c b/plugins/generic-sessions/utils.c
index 7db4369e6c14fae573ebce033bffb75553f62107..e897ed1aa7e874afcdd33451943c47af1a0fddb3 100644
--- a/plugins/generic-sessions/utils.c
+++ b/plugins/generic-sessions/utils.c
@@ -206,8 +206,7 @@ lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
 	if (cols)
 		lla->results = 0;
 	if (col_val && col_val[0]) {
-		strncpy(lla->username, col_val[0], lla->len);
-		lla->username[lla->len - 1] = '\0';
+		lws_strncpy(lla->username, col_val[0], lla->len + 1);
 		lwsl_info("%s: %s\n", __func__, lla->username);
 	}
 
@@ -246,13 +245,11 @@ lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name
 
 	for (n = 0; n < cols; n++) {
 		if (!strcmp(col_name[n], "username")) {
-			strncpy(u->username, col_val[n], sizeof(u->username) - 1);
-			u->username[sizeof(u->username) - 1] = '\0';
+			lws_strncpy(u->username, col_val[n], sizeof(u->username));
 			continue;
 		}
 		if (!strcmp(col_name[n], "ip")) {
-			strncpy(u->ip, col_val[n], sizeof(u->ip) - 1);
-			u->ip[sizeof(u->ip) - 1] = '\0';
+			lws_strncpy(u->ip, col_val[n], sizeof(u->ip));
 			continue;
 		}
 		if (!strcmp(col_name[n], "creation_time")) {
@@ -267,8 +264,7 @@ lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name
 			continue;
 		}
 		if (!strcmp(col_name[n], "email")) {
-			strncpy(u->email, col_val[n], sizeof(u->email) - 1);
-			u->email[sizeof(u->email) - 1] = '\0';
+			lws_strncpy(u->email, col_val[n], sizeof(u->email));
 			continue;
 		}
 		if (!strcmp(col_name[n], "verified")) {
@@ -276,18 +272,15 @@ lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name
 			continue;
 		}
 		if (!strcmp(col_name[n], "pwhash")) {
-			strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id) - 1);
-			u->pwhash.id[sizeof(u->pwhash.id) - 1] = '\0';
+			lws_strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id));
 			continue;
 		}
 		if (!strcmp(col_name[n], "pwsalt")) {
-			strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id) - 1);
-			u->pwsalt.id[sizeof(u->pwsalt.id) - 1] = '\0';
+			lws_strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id));
 			continue;
 		}
 		if (!strcmp(col_name[n], "token")) {
-			strncpy(u->token.id, col_val[n], sizeof(u->token.id) - 1);
-			u->token.id[sizeof(u->token.id) - 1] = '\0';
+			lws_strncpy(u->token.id, col_val[n], sizeof(u->token.id));
 			continue;
 		}
 	}
diff --git a/plugins/generic-table/protocol_table_dirlisting.c b/plugins/generic-table/protocol_table_dirlisting.c
index 90872668436ba60863ed0d9d224d07cd21436858..9cbb2155901e51c95247469c64ee1b6dccd96f67 100644
--- a/plugins/generic-table/protocol_table_dirlisting.c
+++ b/plugins/generic-table/protocol_table_dirlisting.c
@@ -230,7 +230,7 @@ callback_lws_table_dirlisting(struct lws *wsi, enum lws_callback_reasons reason,
 		if (len > sizeof(pss->reldir) - 1)
 			len = sizeof(pss->reldir) - 1;
 		if (!strstr(in, "..") && !strchr(in, '~'))
-			strncpy(pss->reldir, in, len);
+			lws_strncpy(pss->reldir, in, len + 1);
 		else
 			len = 0;
 		pss->reldir[len] = '\0';
@@ -269,14 +269,12 @@ callback_lws_table_dirlisting(struct lws *wsi, enum lws_callback_reasons reason,
 					strcpy(s1, "/");
 					strcpy(s, "top");
 				} else {
-					strncpy(s, q, n);
-					s[n] = '\0';
+					lws_strncpy(s, q, n + 1);
 
 					n = lws_ptr_diff(q1, pss->reldir);
 					if (n > (int)sizeof(s1) - 1)
 						n = sizeof(s1) - 1;
-					strncpy(s1, pss->reldir, n);
-					s1[n] = '\0';
+					lws_strncpy(s1, pss->reldir, n + 1);
 				}
 				q = q1 + 1;
 			}
diff --git a/plugins/protocol_client_loopback_test.c b/plugins/protocol_client_loopback_test.c
index 687a4cd8ea9dbca0af9a140435221c687fb3b4bc..5ec33f6f27819301f5427ec1f54a8a681ba146ea 100644
--- a/plugins/protocol_client_loopback_test.c
+++ b/plugins/protocol_client_loopback_test.c
@@ -150,8 +150,9 @@ callback_client_loopback_test(struct lws *wsi, enum lws_callback_reasons reason,
 		break;
 
 	case LWS_CALLBACK_CLIENT_RECEIVE:
-		strncpy(buf, in, sizeof(buf) - 1);
-		lwsl_notice("Client connection received %ld from server '%s'\n", (long)len, buf);
+		lws_strncpy(buf, in, sizeof(buf));
+		lwsl_notice("Client connection received %ld from server '%s'\n",
+			    (long)len, buf);
 
 		/* OK we are done with the client connection */
 		return -1;
diff --git a/plugins/protocol_esp32_lws_ota.c b/plugins/protocol_esp32_lws_ota.c
index e91144cc28a32aae98436700896c2e68e8318e11..4fff45086df6ba298240a22f2b2b2bbad7c132dc 100644
--- a/plugins/protocol_esp32_lws_ota.c
+++ b/plugins/protocol_esp32_lws_ota.c
@@ -104,7 +104,7 @@ ota_file_upload_cb(void *data, const char *name, const char *filename,
 	switch (state) {
 	case LWS_UFS_OPEN:
 		lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
-		strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+		lws_strncpy(pss->filename, filename, sizeof(pss->filename));
 		if (strcmp(name, "ota"))
 			return 1;
 
diff --git a/plugins/protocol_esp32_lws_scan.c b/plugins/protocol_esp32_lws_scan.c
index 79377ec128badd9e58b5d45e9f2674f41dc8204f..ddcbec29ce90f2c26642c21c050abd665ca00427 100644
--- a/plugins/protocol_esp32_lws_scan.c
+++ b/plugins/protocol_esp32_lws_scan.c
@@ -374,8 +374,7 @@ scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v)
 		lws.rssi = r->rssi;
 		lws.count = 1;
 		memcpy(&lws.bssid, r->bssid, 6);
-		strncpy(lws.ssid, (const char *)r->ssid, sizeof(lws.ssid) - 1);
-		lws.ssid[sizeof(lws.ssid) - 1] = '\0';
+		lws_strncpy(lws.ssid, (const char *)r->ssid, sizeof(lws.ssid));
 
 		lws_wifi_scan_insert_trim(&vhd->known_aps_list, &lws);
 	}
@@ -408,7 +407,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 			return -1;
 
 		lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
-		strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+		lws_strncpy(pss->filename, filename, sizeof(pss->filename));
 		if (!strcmp(name, "pub") || !strcmp(name, "pri")) {
 			if (nvs_open("lws-station", NVS_READWRITE, &pss->nvh))
 				return 1;
@@ -567,7 +566,7 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
 				lws_tls_vhost_cert_info(vhd->vhost,
 					LWS_TLS_CERT_INFO_COMMON_NAME, &ir,
 						sizeof(ir.ns.name));
-				strncpy(subject, ir.ns.name, sizeof(subject) - 1);
+				lws_strncpy(subject, ir.ns.name, sizeof(subject));
 
 				ir.ns.name[0] = '\0';
 				lws_tls_vhost_cert_info(vhd->vhost,
@@ -785,8 +784,7 @@ issue:
 		lwsl_notice("LWS_CALLBACK_VHOST_CERT_UPDATE: %d\n", (int)len);
 		vhd->acme_state = (int)len;
 		if (in) {
-			strncpy(vhd->acme_msg, in, sizeof(vhd->acme_msg) - 1);
-			vhd->acme_msg[sizeof(vhd->acme_msg) - 1] = '\0';
+			lws_strncpy(vhd->acme_msg, in, sizeof(vhd->acme_msg));
 			lwsl_notice("acme_msg: %s\n", (char *)in);
 		}
 		lws_callback_on_writable_all_protocol_vhost(vhd->vhost, vhd->protocol);
@@ -843,20 +841,15 @@ issue:
 
 				if (si != -1 && n < 8) {
 					if (!(n & 1)) {
-						strncpy(lws_esp32.ssid[(n >> 1) & 3], p,
+						lws_strncpy(lws_esp32.ssid[(n >> 1) & 3], p,
 								sizeof(lws_esp32.ssid[0]));
-						lws_esp32.ssid[(n >> 1) & 3]
-							[sizeof(lws_esp32.ssid[0]) - 1] = '\0';
 						lws_snprintf(use, sizeof(use) - 1, "%duse", si);
 						lwsl_notice("resetting %s to 0\n", use);
 						nvs_set_u32(nvh, use, 0);
 
-					} else {
-						strncpy(lws_esp32.password[(n >> 1) & 3], p,
+					} else
+						lws_strncpy(lws_esp32.password[(n >> 1) & 3], p,
 								sizeof(lws_esp32.password[0]));
-						lws_esp32.password[(n >> 1) & 3]
-							[sizeof(lws_esp32.password[0]) - 1] = '\0';
-					}
 				}
 
 			}
diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c
index 35867e64e017d1140f6167228d919eda2ace87ee..0f99141879a0ac5336867e4d64c595c1862598ff 100644
--- a/plugins/protocol_lws_raw_test.c
+++ b/plugins/protocol_lws_raw_test.c
@@ -114,7 +114,8 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 					(const struct lws_protocol_vhost_options *)in;
 			while (pvo) {
 				if (!strcmp(pvo->name, "fifo-path"))
-					strncpy(vhd->fifo_path, pvo->value, sizeof(vhd->fifo_path) - 1);
+					lws_strncpy(vhd->fifo_path, pvo->value,
+							sizeof(vhd->fifo_path));
 				pvo = pvo->next;
 			}
 			if (vhd->fifo_path[0] == '\0') {
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index 4bff99b89d508df29b2a2fe7b6cfcc5cdb67681f..4fab8e2db62c91963db28a91312f385e8b25a4e1 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -72,7 +72,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 
 	switch (state) {
 	case LWS_UFS_OPEN:
-		strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+		lws_strncpy(pss->filename, filename, sizeof(pss->filename));
 		/* we get the original filename in @filename arg, but for
 		 * simple demo use a fixed name so we don't have to deal with
 		 * attacks  */
diff --git a/plugins/ssh-base/kex-25519.c b/plugins/ssh-base/kex-25519.c
index 526125f3f07aea1acaed8f179f2cf76d15998b67..f4ee5acb0f2263ed72864140c3b934667bc64769 100644
--- a/plugins/ssh-base/kex-25519.c
+++ b/plugins/ssh-base/kex-25519.c
@@ -153,10 +153,9 @@ ed25519_key_parse(uint8_t *p, size_t len, char *type, size_t type_len,
 	if (l > 31)
 		return 8;
 	m = l;
-	if (m > type_len)
+	if (m >= type_len)
 		m = (uint32_t)type_len -1 ;
-	strncpy(type, (const char *)p, m);
-	type[m] = '\0';
+	lws_strncpy(type, (const char *)p, m + 1);
 
 	p += l;
 	l = lws_g32(&p); /* pub key length */
diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c
index d479e31db02702633491db475294d27be79e9f1b..374fe09fa7d03a5b494a976e8a8dce1f4d238728 100644
--- a/plugins/ssh-base/sshd.c
+++ b/plugins/ssh-base/sshd.c
@@ -1066,14 +1066,10 @@ again:
 			}
 
 			pss->seen_auth_req_before = 1;
-			strncpy(pss->last_auth_req_username, pss->ua->username,
-				sizeof(pss->last_auth_req_username) - 1);
-			pss->last_auth_req_username[
-			        sizeof(pss->last_auth_req_username) - 1] = '\0';
-			strncpy(pss->last_auth_req_service, pss->ua->service,
-				sizeof(pss->last_auth_req_service) - 1);
-			pss->last_auth_req_service[
-			        sizeof(pss->last_auth_req_service) - 1] = '\0';
+			lws_strncpy(pss->last_auth_req_username, pss->ua->username,
+				sizeof(pss->last_auth_req_username));
+			lws_strncpy(pss->last_auth_req_service, pss->ua->service,
+				sizeof(pss->last_auth_req_service));
 
 			if (strcmp(pss->ua->service, "ssh-connection"))
 				goto ua_fail;
diff --git a/test-apps/fuzxy.c b/test-apps/fuzxy.c
index 888ce62d30910ecfc5bacd70246da428e14caf8c..51203c4df170a74b20b29987d7b17e2f75b09428 100644
--- a/test-apps/fuzxy.c
+++ b/test-apps/fuzxy.c
@@ -794,8 +794,7 @@ main(int argc, char **argv)
 			port_local = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			break;
 		case 'h':
 			fprintf(stderr, "Usage: libwebsockets-test-fuzxy "
diff --git a/test-apps/test-client.c b/test-apps/test-client.c
index 888223bb70c87f1c308a04ec1c37f404d784fdf4..c5acd604f27a776e06ff1325681b50ffae99e611 100644
--- a/test-apps/test-client.c
+++ b/test-apps/test-client.c
@@ -641,22 +641,18 @@ int main(int argc, char **argv)
 			lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
 			break;
 		case 'C':
-			strncpy(cert_path, optarg, sizeof(cert_path) - 1);
-			cert_path[sizeof(cert_path) - 1] = '\0';
+			lws_strncpy(cert_path, optarg, sizeof(cert_path));
 			break;
 		case 'K':
-			strncpy(key_path, optarg, sizeof(key_path) - 1);
-			key_path[sizeof(key_path) - 1] = '\0';
+			lws_strncpy(key_path, optarg, sizeof(key_path));
 			break;
 		case 'A':
-			strncpy(ca_path, optarg, sizeof(ca_path) - 1);
-			ca_path[sizeof(ca_path) - 1] = '\0';
+			lws_strncpy(ca_path, optarg, sizeof(ca_path));
 			break;
 
 #if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
 		case 'R':
-			strncpy(crl_path, optarg, sizeof(crl_path) - 1);
-			crl_path[sizeof(crl_path) - 1] = '\0';
+			lws_strncpy(crl_path, optarg, sizeof(crl_path));
 			break;
 #endif
 		case 'h':
@@ -677,8 +673,7 @@ int main(int argc, char **argv)
 
 	/* add back the leading / on path */
 	path[0] = '/';
-	strncpy(path + 1, p, sizeof(path) - 2);
-	path[sizeof(path) - 1] = '\0';
+	lws_strncpy(path + 1, p, sizeof(path) - 1);
 	i.path = path;
 
 	if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
diff --git a/test-apps/test-echo.c b/test-apps/test-echo.c
index ab75e19ba7460d131475597ff99b6d63d1c18317..4ae26bf4fe38c502026fbc2cfaa1cfcfeb0a301f 100644
--- a/test-apps/test-echo.c
+++ b/test-apps/test-echo.c
@@ -290,22 +290,18 @@ int main(int argc, char **argv)
 			continue;
 		switch (n) {
 		case 'P':
-			strncpy(passphrase, optarg, sizeof(passphrase));
-			passphrase[sizeof(passphrase) - 1] = '\0';
+			lws_strncpy(passphrase, optarg, sizeof(passphrase));
 			info.ssl_private_key_password = passphrase;
 			break;
 		case 'C':
-			strncpy(ssl_cert, optarg, sizeof(ssl_cert));
-			ssl_cert[sizeof(ssl_cert) - 1] = '\0';
+			lws_strncpy(ssl_cert, optarg, sizeof(ssl_cert));
 			disallow_selfsigned = 1;
 			break;
 		case 'k':
-			strncpy(ssl_key, optarg, sizeof(ssl_key));
-			ssl_key[sizeof(ssl_key) - 1] = '\0';
+			lws_strncpy(ssl_key, optarg, sizeof(ssl_key));
 			break;
 		case 'u':
-			strncpy(uri, optarg, sizeof(uri));
-			uri[sizeof(uri) - 1] = '\0';
+			lws_strncpy(uri, optarg, sizeof(uri));
 			break;
 
 #ifndef LWS_NO_DAEMONIZE
@@ -319,8 +315,7 @@ int main(int argc, char **argv)
 #ifndef LWS_NO_CLIENT
 		case 'c':
 			client = 1;
-			strncpy(address, optarg, sizeof(address) - 1);
-			address[sizeof(address) - 1] = '\0';
+			lws_strncpy(address, optarg, sizeof(address));
 			port = 80;
 			break;
 		case 'r':
@@ -345,8 +340,7 @@ int main(int argc, char **argv)
 			lwsl_err("using lws-echogen\n");
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			_interface = interface_name;
 			break;
 		case 'n':
diff --git a/test-apps/test-fraggle.c b/test-apps/test-fraggle.c
index dd1e84e1d4d0a9665ab7ac7f3aa3927e4b35c865..d98e8b32afccfe8b1fb3349db721ea7a5adc4040 100644
--- a/test-apps/test-fraggle.c
+++ b/test-apps/test-fraggle.c
@@ -296,8 +296,7 @@ int main(int argc, char **argv)
 			server_port = port;
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'c':
diff --git a/test-apps/test-ping.c b/test-apps/test-ping.c
index 87307ec652249f49296e1945c2acd0db801ba359..e9f8f18aecf22c3efa2cd90179d4f212f50c35cb 100644
--- a/test-apps/test-ping.c
+++ b/test-apps/test-ping.c
@@ -384,8 +384,7 @@ int main(int argc, char **argv)
 			port = atoi(optarg);
 			break;
 		case 'n':
-			strncpy(protocol_name, optarg, sizeof protocol_name);
-			protocol_name[(sizeof protocol_name) - 1] = '\0';
+			lws_strncpy(protocol_name, optarg, sizeof protocol_name);
 			protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
 			break;
 		case 'i':
diff --git a/test-apps/test-server-http.c b/test-apps/test-server-http.c
index 1166d15f89cc42d89ace81c73ca6d04a8e28a363..d895d267f121ed98170f1bc1395c51e33b3740fc 100644
--- a/test-apps/test-server-http.c
+++ b/test-apps/test-server-http.c
@@ -145,7 +145,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 
 	switch (state) {
 	case LWS_UFS_OPEN:
-		strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
+		lws_strncpy(pss->filename, filename, sizeof(pss->filename));
 		/* we get the original filename in @filename arg, but for
 		 * simple demo use a fixed name so we don't have to deal with
 		 * attacks  */
diff --git a/test-apps/test-server-libev.c b/test-apps/test-server-libev.c
index 6ba3e8da914d453d58633ec5f1262b2460e5e427..5766c0c725e3c627f04f37d16e47f8bb457c5b6e 100644
--- a/test-apps/test-server-libev.c
+++ b/test-apps/test-server-libev.c
@@ -224,8 +224,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'c':
diff --git a/test-apps/test-server-libevent.c b/test-apps/test-server-libevent.c
index cedbc5c92c5671d9743dde3a94195400e313aee5..932701e772df9cd91045903991a070788116ada8 100644
--- a/test-apps/test-server-libevent.c
+++ b/test-apps/test-server-libevent.c
@@ -222,8 +222,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'c':
diff --git a/test-apps/test-server-libuv.c b/test-apps/test-server-libuv.c
index de9c11e550273bc9e808b1bc29caa7c50ce57c2d..e229c03222d9a5c9749fe924f66478fcea2ad89f 100644
--- a/test-apps/test-server-libuv.c
+++ b/test-apps/test-server-libuv.c
@@ -271,8 +271,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'c':
diff --git a/test-apps/test-server-pthreads.c b/test-apps/test-server-pthreads.c
index 6a81bbd891ca7b5d27d68bd33ed176484ef7e70a..bb090a23af70a604851e80654b35e69cc68a5eb7 100644
--- a/test-apps/test-server-pthreads.c
+++ b/test-apps/test-server-pthreads.c
@@ -256,8 +256,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'c':
diff --git a/test-apps/test-server-v2.0.c b/test-apps/test-server-v2.0.c
index af629f5c352a459382a0fa75d7a86ea62c37bd26..ae81b4741a590f12c9dc5cf73a397ad7b2afd388 100644
--- a/test-apps/test-server-v2.0.c
+++ b/test-apps/test-server-v2.0.c
@@ -373,8 +373,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'r':
@@ -382,16 +381,13 @@ int main(int argc, char **argv)
 			printf("Setting resource path to \"%s\"\n", resource_path);
 			break;
 		case 'C':
-			strncpy(cert_path, optarg, sizeof(cert_path) - 1);
-			cert_path[sizeof(cert_path) - 1] = '\0';
+			lws_strncpy(cert_path, optarg, sizeof(cert_path));
 			break;
 		case 'K':
-			strncpy(key_path, optarg, sizeof(key_path) - 1);
-			key_path[sizeof(key_path) - 1] = '\0';
+			lws_strncpy(key_path, optarg, sizeof(key_path));
 			break;
 		case 'A':
-			strncpy(ca_path, optarg, sizeof(ca_path) - 1);
-			ca_path[sizeof(ca_path) - 1] = '\0';
+			lws_strncpy(ca_path, optarg, sizeof(ca_path));
 			break;
 #if defined(LWS_OPENSSL_SUPPORT)
 		case 'v':
@@ -401,8 +397,7 @@ int main(int argc, char **argv)
 
 #if defined(LWS_HAVE_SSL_CTX_set1_param)
 		case 'R':
-			strncpy(crl_path, optarg, sizeof(crl_path) - 1);
-			crl_path[sizeof(crl_path) - 1] = '\0';
+			lws_strncpy(crl_path, optarg, sizeof(crl_path));
 			break;
 #endif
 #endif
diff --git a/test-apps/test-server.c b/test-apps/test-server.c
index 105d4881c0fc1e61294f9ea3460e12e7b035e375..0350007007270608f3411ed29ad3d72a9fe87e57 100644
--- a/test-apps/test-server.c
+++ b/test-apps/test-server.c
@@ -296,8 +296,7 @@ int main(int argc, char **argv)
 			info.port = atoi(optarg);
 			break;
 		case 'i':
-			strncpy(interface_name, optarg, sizeof interface_name);
-			interface_name[(sizeof interface_name) - 1] = '\0';
+			lws_strncpy(interface_name, optarg, sizeof interface_name);
 			iface = interface_name;
 			break;
 		case 'k':
@@ -318,16 +317,13 @@ int main(int argc, char **argv)
 			printf("Setting resource path to \"%s\"\n", resource_path);
 			break;
 		case 'C':
-			strncpy(cert_path, optarg, sizeof(cert_path) - 1);
-			cert_path[sizeof(cert_path) - 1] = '\0';
+			lws_strncpy(cert_path, optarg, sizeof(cert_path));
 			break;
 		case 'K':
-			strncpy(key_path, optarg, sizeof(key_path) - 1);
-			key_path[sizeof(key_path) - 1] = '\0';
+			lws_strncpy(key_path, optarg, sizeof(key_path));
 			break;
 		case 'A':
-			strncpy(ca_path, optarg, sizeof(ca_path) - 1);
-			ca_path[sizeof(ca_path) - 1] = '\0';
+			lws_strncpy(ca_path, optarg, sizeof(ca_path));
 			break;
 		case 'P':
 			pp_secs = atoi(optarg);
@@ -341,8 +337,7 @@ int main(int argc, char **argv)
 
 #if defined(LWS_HAVE_SSL_CTX_set1_param)
 		case 'R':
-			strncpy(crl_path, optarg, sizeof(crl_path) - 1);
-			crl_path[sizeof(crl_path) - 1] = '\0';
+			lws_strncpy(crl_path, optarg, sizeof(crl_path));
 			break;
 #endif
 #endif