diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index 6892f5d19408b635c1e99b92839bd6121ce4105c..35e296a1f7979a7b59a3f0c116aa5134c872dfee 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -366,7 +366,7 @@ html_parser_cb(const hubbub_token *token, void *pw)
 	switch (token->type) {
 	case HUBBUB_TOKEN_DOCTYPE:
 
-		p += snprintf(p, end - p, "<!DOCTYPE %.*s %s ",
+		p += lws_snprintf(p, end - p, "<!DOCTYPE %.*s %s ",
 				(int) token->data.doctype.name.len,
 				token->data.doctype.name.ptr,
 				token->data.doctype.force_quirks ?
@@ -375,20 +375,20 @@ html_parser_cb(const hubbub_token *token, void *pw)
 		if (token->data.doctype.public_missing)
 			printf("\tpublic: missing\n");
 		else
-			p += snprintf(p, end - p, "PUBLIC \"%.*s\"\n",
+			p += lws_snprintf(p, end - p, "PUBLIC \"%.*s\"\n",
 				(int) token->data.doctype.public_id.len,
 				token->data.doctype.public_id.ptr);
 
 		if (token->data.doctype.system_missing)
 			printf("\tsystem: missing\n");
 		else
-			p += snprintf(p, end - p, " \"%.*s\">\n",
+			p += lws_snprintf(p, end - p, " \"%.*s\">\n",
 				(int) token->data.doctype.system_id.len,
 				token->data.doctype.system_id.ptr);
 
 		break;
 	case HUBBUB_TOKEN_START_TAG:
-		p += snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len,
+		p += lws_snprintf(p, end - p, "<%.*s", (int)token->data.tag.name.len,
 				token->data.tag.name.ptr);
 
 /*				(token->data.tag.self_closing) ?
@@ -408,23 +408,23 @@ html_parser_cb(const hubbub_token *token, void *pw)
 					pp += r->from_len;
 					plen -= r->from_len;
 				}
-				p += snprintf(p, end - p, " %.*s=\"%s/%.*s\"",
+				p += lws_snprintf(p, end - p, " %.*s=\"%s/%.*s\"",
 				       (int) token->data.tag.attributes[i].name.len,
 				       token->data.tag.attributes[i].name.ptr,
 				       r->to, plen, pp);
 
 			} else
 
-				p += snprintf(p, end - p, " %.*s=\"%.*s\"",
+				p += lws_snprintf(p, end - p, " %.*s=\"%.*s\"",
 					(int) token->data.tag.attributes[i].name.len,
 					token->data.tag.attributes[i].name.ptr,
 					(int) token->data.tag.attributes[i].value.len,
 					token->data.tag.attributes[i].value.ptr);
 		}
-		p += snprintf(p, end - p, ">\n");
+		p += lws_snprintf(p, end - p, ">\n");
 		break;
 	case HUBBUB_TOKEN_END_TAG:
-		p += snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len,
+		p += lws_snprintf(p, end - p, "</%.*s", (int) token->data.tag.name.len,
 				token->data.tag.name.ptr);
 /*
 				(token->data.tag.self_closing) ?
@@ -433,25 +433,25 @@ html_parser_cb(const hubbub_token *token, void *pw)
 						"attributes:" : "");
 */
 		for (i = 0; i < token->data.tag.n_attributes; i++) {
-			p += snprintf(p, end - p, " %.*s='%.*s'\n",
+			p += lws_snprintf(p, end - p, " %.*s='%.*s'\n",
 				(int) token->data.tag.attributes[i].name.len,
 				token->data.tag.attributes[i].name.ptr,
 				(int) token->data.tag.attributes[i].value.len,
 				token->data.tag.attributes[i].value.ptr);
 		}
-		p += snprintf(p, end - p, ">\n");
+		p += lws_snprintf(p, end - p, ">\n");
 		break;
 	case HUBBUB_TOKEN_COMMENT:
-		p += snprintf(p, end - p, "<!-- %.*s -->\n",
+		p += lws_snprintf(p, end - p, "<!-- %.*s -->\n",
 				(int) token->data.comment.len,
 				token->data.comment.ptr);
 		break;
 	case HUBBUB_TOKEN_CHARACTER:
-		p += snprintf(p, end - p, "%.*s", (int) token->data.character.len,
+		p += lws_snprintf(p, end - p, "%.*s", (int) token->data.character.len,
 				token->data.character.ptr);
 		break;
 	case HUBBUB_TOKEN_EOF:
-		p += snprintf(p, end - p, "\n");
+		p += lws_snprintf(p, end - p, "\n");
 		break;
 	}
 
diff --git a/lib/libuv.c b/lib/libuv.c
index 64e5a7ed4ac00f34ac07b0974be13f89299e855a..bb1bee306cb6e5a04f8e06348c9f27499f902877 100644
--- a/lib/libuv.c
+++ b/lib/libuv.c
@@ -484,14 +484,14 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
 
 			lwsl_notice("   %s\n", dent.name);
 
-			snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name);
+			lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name);
 			if (uv_dlopen(path, &lib)) {
 				uv_dlerror(&lib);
 				lwsl_err("Error loading DSO: %s\n", lib.errmsg);
 				goto bail;
 			}
 			/* we could open it, can we get his init function? */
-			m = snprintf(path, sizeof(path) - 1, "init_%s",
+			m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
 				     dent.name + 3 /* snip lib... */);
 			path[m - 3] = '\0'; /* snip the .so */
 			if (uv_dlsym(&lib, path, &v)) {
@@ -554,7 +554,7 @@ lws_plat_plugins_destroy(struct lws_context * context)
 
 	while (plugin) {
 		p = plugin;
-		m = snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
+		m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
 		path[m - 3] = '\0';
 
 		if (uv_dlsym(&plugin->lib, path, &v)) {
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index f62c156174cfe24eb150ca229f5d50d88bb9f712..8562290306888b66ba4e97820e132372cd2ff66b 100755
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -1165,7 +1165,7 @@ lwsl_timestamp(int level, char *p, int len)
 			continue;
 		now = time_in_microseconds() / 100;
 		if (ptm)
-			n = snprintf(p, len,
+			n = lws_snprintf(p, len,
 				"[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ",
 				ptm->tm_year + 1900,
 				ptm->tm_mon + 1,
@@ -1175,7 +1175,7 @@ lwsl_timestamp(int level, char *p, int len)
 				ptm->tm_sec,
 				(int)(now % 10000), log_level_names[n]);
 		else
-			n = snprintf(p, len, "[%llu:%04d] %s: ",
+			n = lws_snprintf(p, len, "[%llu:%04d] %s: ",
 					(unsigned long long) now / 10000,
 					(int)(now % 10000), log_level_names[n]);
 		return n;
@@ -1683,6 +1683,24 @@ lws_finalize_startup(struct lws_context *context)
 	return 0;
 }
 
+int
+lws_snprintf(char *str, size_t size, const char *format, ...)
+{
+	va_list ap;
+	int n;
+
+	if (!size)
+		return 0;
+
+	va_start(ap, format);
+	n = vsnprintf(str, size, format, ap);
+	va_end(ap);
+
+	if (n >= size)
+		return size;
+
+	return n;
+}
 
 LWS_VISIBLE LWS_EXTERN int
 lws_is_cgi(struct lws *wsi) {
@@ -1831,7 +1849,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	if (wsi->u.hdr.ah) {
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
 			uritok = WSI_TOKEN_POST_URI;
-		snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
+		lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
 			 lws_hdr_simple_ptr(wsi, uritok));
 		cgi_path[sizeof(cgi_path) - 1] = '\0';
 		env_array[n++] = cgi_path;
@@ -1841,7 +1859,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 			env_array[n++] = "REQUEST_METHOD=GET";
 
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "QUERY_STRING=");
+		p += lws_snprintf(p, end - p, "QUERY_STRING=");
 		/* dump the individual URI Arg parameters */
 		m = 0;
 		while (1) {
@@ -1866,55 +1884,55 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 		*p++ = '\0';
 
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "PATH_INFO=%s",
+		p += lws_snprintf(p, end - p, "PATH_INFO=%s",
 			      lws_hdr_simple_ptr(wsi, uritok) +
 			      script_uri_path_len);
 		p++;
 	}
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_REFERER)) {
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "HTTP_REFERER=%s",
+		p += lws_snprintf(p, end - p, "HTTP_REFERER=%s",
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_REFERER));
 		p++;
 	}
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "HTTP_HOST=%s",
+		p += lws_snprintf(p, end - p, "HTTP_HOST=%s",
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
 		p++;
 	}
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "HTTP_COOKIE=%s",
+		p += lws_snprintf(p, end - p, "HTTP_COOKIE=%s",
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE));
 		p++;
 	}
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "USER_AGENT=%s",
+		p += lws_snprintf(p, end - p, "USER_AGENT=%s",
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
 		p++;
 	}
 	if (uritok == WSI_TOKEN_POST_URI) {
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
 			env_array[n++] = p;
-			p += snprintf(p, end - p, "CONTENT_TYPE=%s",
+			p += lws_snprintf(p, end - p, "CONTENT_TYPE=%s",
 				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
 			p++;
 		}
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
 			env_array[n++] = p;
-			p += snprintf(p, end - p, "CONTENT_LENGTH=%s",
+			p += lws_snprintf(p, end - p, "CONTENT_LENGTH=%s",
 				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH));
 			p++;
 		}
 	}
 	env_array[n++] = p;
-	p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
+	p += lws_snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
 
 	while (mp_cgienv) {
 		env_array[n++] = p;
-		p += snprintf(p, end - p, "%s=%s", mp_cgienv->name,
+		p += lws_snprintf(p, end - p, "%s=%s", mp_cgienv->name,
 			      mp_cgienv->value);
 		lwsl_debug("   Applying mount-specific cgi env '%s'\n",
 			   env_array[n - 1]);
@@ -2360,7 +2378,7 @@ lws_access_log(struct lws *wsi)
 	if (!p)
 		p = "";
 
-	l = snprintf(ass, sizeof(ass) - 1, "%s %d %lu %s\n",
+	l = lws_snprintf(ass, sizeof(ass) - 1, "%s %d %lu %s\n",
 		     wsi->access_log.header_log,
 		     wsi->access_log.response, wsi->access_log.sent, p);
 
@@ -2403,7 +2421,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 	if (len < 100)
 		return 0;
 
-	buf += snprintf(buf, end - buf,
+	buf += lws_snprintf(buf, end - buf,
 			"{\n \"name\":\"%s\",\n"
 			" \"port\":\"%d\",\n"
 			" \"use_ssl\":\"%d\",\n"
@@ -2429,11 +2447,11 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 	if (vh->mount_list) {
 		const struct lws_http_mount *m = vh->mount_list;
 
-		buf += snprintf(buf, end - buf, ",\n \"mounts\":[");
+		buf += lws_snprintf(buf, end - buf, ",\n \"mounts\":[");
 		while (m) {
 			if (!first)
-				buf += snprintf(buf, end - buf, ",");
-			buf += snprintf(buf, end - buf,
+				buf += lws_snprintf(buf, end - buf, ",");
+			buf += lws_snprintf(buf, end - buf,
 					"\n  {\n   \"mountpoint\":\"%s\",\n"
 					"  \"origin\":\"%s%s\",\n"
 					"  \"cache_max_age\":\"%d\",\n"
@@ -2449,25 +2467,25 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 					m->cache_revalidate,
 					m->cache_intermediaries);
 			if (m->def)
-				buf += snprintf(buf, end - buf,
+				buf += lws_snprintf(buf, end - buf,
 						",\n  \"default\":\"%s\"",
 						m->def);
-			buf += snprintf(buf, end - buf, "\n  }");
+			buf += lws_snprintf(buf, end - buf, "\n  }");
 			first = 0;
 			m = m->mount_next;
 		}
-		buf += snprintf(buf, end - buf, "\n ]");
+		buf += lws_snprintf(buf, end - buf, "\n ]");
 	}
 
 	if (vh->protocols) {
 		n = 0;
 		first = 1;
 
-		buf += snprintf(buf, end - buf, ",\n \"ws-protocols\":[");
+		buf += lws_snprintf(buf, end - buf, ",\n \"ws-protocols\":[");
 		while (n < vh->count_protocols) {
 			if (!first)
-				buf += snprintf(buf, end - buf, ",");
-			buf += snprintf(buf, end - buf,
+				buf += lws_snprintf(buf, end - buf, ",");
+			buf += lws_snprintf(buf, end - buf,
 					"\n  {\n   \"%s\":\{\n"
 					"    \"status\":\"ok\"\n   }\n  }"
 					,
@@ -2475,10 +2493,10 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 			first = 0;
 			n++;
 		}
-		buf += snprintf(buf, end - buf, "\n ]");
+		buf += lws_snprintf(buf, end - buf, "\n ]");
 	}
 
-	buf += snprintf(buf, end - buf, "\n}");
+	buf += lws_snprintf(buf, end - buf, "\n}");
 
 	return buf - orig;
 }
@@ -2497,7 +2515,7 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len)
 	time_t t = time(NULL);
 	int listening = 0, cgi_count = 0, n;
 
-	buf += snprintf(buf, end - buf, "{ "
+	buf += lws_snprintf(buf, end - buf, "{ "
 					"\"version\":\"%s\",\n"
 					"\"uptime\":\"%ld\",\n"
 					"\"cgi_spawned\":\"%d\",\n"
@@ -2517,19 +2535,19 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len)
 
 		m = getloadavg(d, 3);
 		for (n = 0; n < m; n++) {
-			buf += snprintf(buf, end - buf,
+			buf += lws_snprintf(buf, end - buf,
 				"\"l%d\":\"%.2f\",\n",
 				n + 1, d[n]);
 		}
 	}
 #endif
 
-	buf += snprintf(buf, end - buf, "\"pt\":[\n ");
+	buf += lws_snprintf(buf, end - buf, "\"pt\":[\n ");
 	for (n = 0; n < context->count_threads; n++) {
 		pt = &context->pt[n];
 		if (n)
-			buf += snprintf(buf, end - buf, ",");
-		buf += snprintf(buf, end - buf,
+			buf += lws_snprintf(buf, end - buf, ",");
+		buf += lws_snprintf(buf, end - buf,
 				"\n  {\n"
 				"    \"fds_count\":\"%d\",\n"
 				"    \"ah_pool_inuse\":\"%d\",\n"
@@ -2540,7 +2558,7 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len)
 				pt->ah_wait_list_length);
 	}
 
-	buf += snprintf(buf, end - buf, "], \"vhosts\":[\n ");
+	buf += lws_snprintf(buf, end - buf, "], \"vhosts\":[\n ");
 
 	while (vh) {
 		if (!first)
@@ -2553,7 +2571,7 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len)
 		vh = vh->vhost_next;
 	}
 
-	buf += snprintf(buf, end - buf, "],\n\"listen_wsi\":\"%d\"",
+	buf += lws_snprintf(buf, end - buf, "],\n\"listen_wsi\":\"%d\"",
 			listening);
 
 #ifdef LWS_WITH_CGI
@@ -2568,10 +2586,10 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len)
 		}
 	}
 #endif
-	buf += snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ",
+	buf += lws_snprintf(buf, end - buf, ",\n \"cgi_alive\":\"%d\"\n ",
 			cgi_count);
 
-	buf += snprintf(buf, end - buf, "}\n ");
+	buf += lws_snprintf(buf, end - buf, "}\n ");
 
 	return buf - orig;
 }
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index ef9cd0f11f199592280f4cc1e422568eba2ab364..51830909bd4026fc5a94a4b44008a8294ea5ebf7 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -147,7 +147,7 @@ struct sockaddr_in;
 #define LWS_O_RDONLY _O_RDONLY
 
 #if !defined(_MSC_VER) || _MSC_VER < 1900 /* Visual Studio 2015 already defines this in <stdio.h> */
-#define snprintf _snprintf
+#define lws_snprintf _snprintf
 #endif
 
 #ifndef __func__
@@ -2190,6 +2190,20 @@ lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
 LWS_VISIBLE LWS_EXTERN void
 lws_set_allocator(void *(*realloc)(void *ptr, size_t size));
 
+/**
+ * lws_snprintf(): lws_snprintf that truncates the returned length too
+ *
+ * \param str: destination buffer
+ * \param size: bytes left in destination buffer
+ * \param format: format string
+ * \param ...: args for format
+ *
+ * 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 int
+lws_snprintf(char *str, size_t size, const char *format, ...);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index e26b041fa5ae97e6680848c32b8513df25cab7a7..b430a78c4e4f10396e82ba3fdcae463d6fbb1d23 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -331,7 +331,7 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
 
 			lwsl_notice("   %s\n", namelist[i]->d_name);
 
-			snprintf(path, sizeof(path) - 1, "%s/%s", *d,
+			lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d,
 				 namelist[i]->d_name);
 			l = dlopen(path, RTLD_NOW);
 			if (!l) {
@@ -341,7 +341,7 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
 				goto bail;
 			}
 			/* we could open it, can we get his init function? */
-			m = snprintf(path, sizeof(path) - 1, "init_%s",
+			m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
 				     namelist[i]->d_name + 3 /* snip lib... */);
 			path[m - 3] = '\0'; /* snip the .so */
 			initfunc = dlsym(l, path);
@@ -406,7 +406,7 @@ lws_plat_plugins_destroy(struct lws_context * context)
 
 	while (plugin) {
 		p = plugin;
-		m = snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
+		m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
 		path[m - 3] = '\0';
 		func = dlsym(plugin->l, path);
 		if (!func) {
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 1a0792a0c76ee7dd9ccbf9230f4b387779999574..8102a73069cd585f4272944607f088c93acf27f9 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -97,7 +97,7 @@
 #endif
 
 #ifdef LWS_HAVE__SNPRINTF
-#define snprintf _snprintf
+#define lws_snprintf _snprintf
 #endif
 
 #else /* not windows --> */
diff --git a/lib/server.c b/lib/server.c
index 1ac3651521c1f56bad31a408f2ee77151476697a..b7d5bf0b379249403190e11411551d28188d4098 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -301,7 +301,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 #endif
 	int n, spin = 0;
 
-	snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
+	lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
 
 #ifndef _WIN32_WCE
 	do {
@@ -322,12 +322,12 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 			}
 			sym[len] = '\0';
 			lwsl_debug("symlink %s -> %s\n", path, sym);
-			snprintf(path, sizeof(path) - 1, "%s", sym);
+			lws_snprintf(path, sizeof(path) - 1, "%s", sym);
 		}
 #endif
 		if ((S_IFMT & st.st_mode) == S_IFDIR) {
 			lwsl_debug("default filename append to dir\n");
-			snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
+			lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
 				 origin, uri);
 		}
 
@@ -601,7 +601,7 @@ lws_http_action(struct lws *wsi)
 			else
 				me = "unknown";
 
-			snprintf(wsi->access_log.header_log, l,
+			lws_snprintf(wsi->access_log.header_log, l,
 				 "%s - - [%s] \"%s %s %s\"",
 				 pa, da, me, uri_ptr,
 				 hver[wsi->u.http.request_version]);
@@ -676,11 +676,11 @@ lws_http_action(struct lws *wsi)
 
 			/* > at start indicates deal with by redirect */
 			if (hit->origin_protocol & 4)
-				n = snprintf((char *)end, 256, "%s%s",
+				n = lws_snprintf((char *)end, 256, "%s%s",
 					    oprot[hit->origin_protocol & 1],
 					    hit->origin);
 			else
-				n = snprintf((char *)end, 256,
+				n = lws_snprintf((char *)end, 256,
 				    "https://%s/%s/",
 				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
 				    uri_ptr);
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index ee1137127e9c3a49a6fc43dad4640d06c43cc405..a7b7197e4530e7659cbc04ed7a3652437570c7bd 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -1923,3 +1923,27 @@ text/html content-encoding, it's replaced with <tt><b>uri_replace_to</b></tt>
 <dd>Length of data in <tt><b>buf</b></tt> to send
 </dl>
 <hr>
+<h2>lws_snprintf - </h2>
+<i>LWS_EXTERN int</i>
+<b>lws_snprintf</b>
+(<i>char *</i> <b>str</b>,
+<i>size_t</i> <b>size</b>,
+<i>const char *</i> <b>format</b>,
+<i></i> <b>...</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>...</b>
+<dd>variable arguments
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+\param str: destination buffer
+\param size: bytes left in destination buffer
+\param format: format string
+\param ...: args for format
+<p>
+This lets you correctly truncate buffers by concatenating lengths, if you
+reach the limit the reported length doesn't exceed the limit.
+</blockquote>
+<hr>
diff --git a/lwsws/conf.c b/lwsws/conf.c
index fa00095db45aa2d08b6b3740ece4a5c1aae63822..e0059b4d9f7ca51d773bfa8e04c4e62bf15c75f8 100644
--- a/lwsws/conf.c
+++ b/lwsws/conf.c
@@ -181,7 +181,7 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
 		return 0;
 	}
 
-	a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+	a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
 
 	return 0;
 }
@@ -254,7 +254,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		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 += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
 		*(a->p)++ = '\0';
 	}
 
@@ -408,7 +408,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->p += n;
 		mp_cgienv->value = a->p;
 		mp_cgienv->options = NULL;
-		a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+		a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
 		*(a->p)++ = '\0';
 
 		lwsl_notice("    adding cgi-env '%s' = '%s'\n", mp_cgienv->name,
@@ -452,7 +452,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		return 0;
 	}
 
-	a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+	a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
 	*(a->p)++ = '\0';
 
 	return 0;
@@ -518,7 +518,7 @@ lwsws_get_config_d(void *user, const char *d, const char * const *paths,
 	}
 
 	while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
-		snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
+		lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
 		ret = lwsws_get_config(user, path, paths, count_paths, cb);
 		if (ret)
 			goto bail;
@@ -558,7 +558,7 @@ lwsws_get_config_d(void *user, const char *d, const char * const *paths,
 	}
 
 	for (i = 0; i < n; i++) {
-		snprintf(path, sizeof(path) - 1, "%s/%s", d,
+		lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
 			 namelist[i]->d_name);
 		ret = lwsws_get_config(user, path, paths, count_paths, cb);
 		if (ret) {
diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c
index 29e1d65fe2adfb74641703c8bfb652ac742d8441..c9d828fcd7fd076ac499dde4058b12b19745d9fe 100644
--- a/plugins/protocol_lws_status.c
+++ b/plugins/protocol_lws_status.c
@@ -58,7 +58,7 @@ update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
 	struct tm tm;
 #endif
 
-	p += snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
+	p += lws_snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
 		     server_info, live_wsi);
 
 	/* render the list */
@@ -79,7 +79,7 @@ update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
 		if (subsequent)
 			*p++ = ',';
 		subsequent = 1;
-		p += snprintf(p, sizeof(cache) - (p - start) - 1,
+		p += lws_snprintf(p, sizeof(cache) - (p - start) - 1,
 				"{\"peer\":\"%s\",\"time\":\"%s\","
 				"\"ua\":\"%s\"}",
 			     (*pp)->ip, date, (*pp)->user_agent);
diff --git a/test-server/test-fraggle.c b/test-server/test-fraggle.c
index 2137e7b52ea065c9dd336b0c693faf45f2065aaa..80aaea5835431fc82de58818ba081b68badde74f 100644
--- a/test-server/test-fraggle.c
+++ b/test-server/test-fraggle.c
@@ -349,7 +349,7 @@ int main(int argc, char **argv)
 		struct lws_client_connect_info i;
 
 		address = argv[optind];
-		snprintf(ads_port, sizeof(ads_port), "%s:%u",
+		lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
 			 address, port & 65535);
 		memset(&i, 0, sizeof(i));
 		i.context = context;
diff --git a/test-server/test-ping.c b/test-server/test-ping.c
index 3d073d81082cd35dbdca219d28bf272c1367a358..65ab942b22f594a76e8a5d9245a2b19a17ed4d00 100644
--- a/test-server/test-ping.c
+++ b/test-server/test-ping.c
@@ -453,7 +453,7 @@ int main(int argc, char **argv)
 	/* create client websockets using dumb increment protocol */
 
 	address = argv[optind];
-	snprintf(ads_port, sizeof(ads_port), "%s:%u",
+	lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
 		 address, port & 65535);
 	lwsl_notice("Connecting to %s...\n", ads_port);
 	memset(&i, 0, sizeof(i));
diff --git a/test-server/test-server-status.c b/test-server/test-server-status.c
index d85897b83bf6298b1e5658ed0fb59ae463995786..50fe5bf98552191615b24d4983ca4b9b1fe5d936 100644
--- a/test-server/test-server-status.c
+++ b/test-server/test-server-status.c
@@ -42,7 +42,7 @@ update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
 	struct tm tm;
 #endif
 
-	p += snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
+	p += lws_snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
 		     server_info, live_wsi);
 
 	/* render the list */
@@ -67,7 +67,7 @@ update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
 		if (subsequent)
 			*p++ = ',';
 		subsequent = 1;
-		p += snprintf(p, sizeof(cache) - (p - start) - 1,
+		p += lws_snprintf(p, sizeof(cache) - (p - start) - 1,
 				"{\"peer\":\"%s\",\"time\":\"%s\","
 				"\"ua\":\"%s\"}",
 			     (*pp)->ip, date, (*pp)->user_agent);