diff --git a/package/uhttpd/src/uhttpd-lua.c b/package/uhttpd/src/uhttpd-lua.c
index c2efe33849557a5e6f90b94c3afa221fdd223618..a140dc2f2de36eb8a2e88d35e3bed86be184b72f 100644
--- a/package/uhttpd/src/uhttpd-lua.c
+++ b/package/uhttpd/src/uhttpd-lua.c
@@ -108,19 +108,34 @@ static int uh_lua_sendc(lua_State *L)
 	return uh_lua_send_common(L, 1);
 }
 
-static int uh_lua_urldecode(lua_State *L)
+static int uh_lua_str2str(lua_State *L, int (*xlate_func) (char *, int, const char *, int))
 {
-	size_t inlen, outlen;
+	size_t inlen;
+	int outlen;
 	const char *inbuf;
 	char outbuf[UH_LIMIT_MSGHEAD];
 
 	inbuf = luaL_checklstring(L, 1, &inlen);
-	outlen = uh_urldecode(outbuf, sizeof(outbuf), inbuf, inlen);
+	outlen = (* xlate_func)(outbuf, sizeof(outbuf), inbuf, inlen);
+	if( outlen < 0 )
+		luaL_error( L, "%s on URL-encode codec",
+			(outlen==-1) ? "buffer overflow" : "malformed string" );
 
 	lua_pushlstring(L, outbuf, outlen);
 	return 1;
 }
 
+static int uh_lua_urldecode(lua_State *L)
+{
+	return uh_lua_str2str( L, uh_urldecode );
+}
+
+
+static int uh_lua_urlencode(lua_State *L)
+{
+	return uh_lua_str2str( L, uh_urlencode );
+}
+
 
 lua_State * uh_lua_init(const char *handler)
 {
@@ -146,6 +161,9 @@ lua_State * uh_lua_init(const char *handler)
 	lua_pushcfunction(L, uh_lua_urldecode);
 	lua_setfield(L, -2, "urldecode");
 
+	lua_pushcfunction(L, uh_lua_urlencode);
+	lua_setfield(L, -2, "urlencode");
+
 	/* _G.uhttpd = { ... } */
 	lua_setfield(L, LUA_GLOBALSINDEX, "uhttpd");
 
diff --git a/package/uhttpd/src/uhttpd-utils.c b/package/uhttpd/src/uhttpd-utils.c
index 1073f3bb34ecee0dea8374f42a685a6fa29280fd..1dac33db0ff6973fc421997b1afd8ca26a8dd20f 100644
--- a/package/uhttpd/src/uhttpd-utils.c
+++ b/package/uhttpd/src/uhttpd-utils.c
@@ -307,7 +307,7 @@ int uh_http_send(
 
 /* blen is the size of buf; slen is the length of src.  The input-string need
 ** not be, and the output string will not be, null-terminated.  Returns the
-** length of the decoded string. */
+** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
 int uh_urldecode(char *buf, int blen, const char *src, int slen)
 {
 	int i;
@@ -329,7 +329,15 @@ int uh_urldecode(char *buf, int blen, const char *src, int slen)
 			}
 			else
 			{
-				buf[len++] = '%';
+				/* Encoding error: it's hard to think of a
+				** scenario in which returning an incorrect
+				** 'decoding' of the malformed string is
+				** preferable to signaling an error condition. */
+				#if 0 /* WORSE_IS_BETTER */
+				    buf[len++] = '%';
+				#else
+				    return -2;
+				#endif
 			}
 		}
 		else
@@ -338,12 +346,12 @@ int uh_urldecode(char *buf, int blen, const char *src, int slen)
 		}
 	}
 
-	return len;
+	return (i == slen) ? len : -1;
 }
 
 /* blen is the size of buf; slen is the length of src.  The input-string need
 ** not be, and the output string will not be, null-terminated.  Returns the
-** length of the encoded string. */
+** length of the encoded string, or -1 on error (buffer overflow) */
 int uh_urlencode(char *buf, int blen, const char *src, int slen)
 {
 	int i;
@@ -365,11 +373,12 @@ int uh_urlencode(char *buf, int blen, const char *src, int slen)
 		}
 		else
 		{
+			len = -1;
 			break;
 		}
 	}
 
-	return len;
+	return (i == slen) ? len : -1;
 }
 
 int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen)
@@ -495,6 +504,9 @@ static char * canonpath(const char *path, char *path_resolved)
 	return NULL;
 }
 
+/* Returns NULL on error.
+** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning
+** NULL here causes 404 [Not Found], but that's not too unreasonable. */
 struct path_info * uh_path_lookup(struct client *cl, const char *url)
 {
 	static char path_phys[PATH_MAX];
@@ -530,21 +542,21 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
 
 		/* urldecode component w/o query */
 		if( pathptr > url )
-			uh_urldecode(
-				&buffer[strlen(docroot)],
-				sizeof(buffer) - strlen(docroot) - 1,
-				url, pathptr - url
-			);
+			if ( uh_urldecode(
+					&buffer[strlen(docroot)],
+					sizeof(buffer) - strlen(docroot) - 1,
+					url, pathptr - url ) < 0 )
+				return NULL; /* bad URL */
 	}
 
 	/* no query string, decode all of url */
 	else
 	{
-		uh_urldecode(
-			&buffer[strlen(docroot)],
-			sizeof(buffer) - strlen(docroot) - 1,
-			url, strlen(url)
-		);
+		if ( uh_urldecode(
+				&buffer[strlen(docroot)],
+				sizeof(buffer) - strlen(docroot) - 1,
+				url, strlen(url) ) < 0 )
+			return NULL; /* bad URL */
 	}
 
 	/* create canon path */
diff --git a/package/uhttpd/src/uhttpd.c b/package/uhttpd/src/uhttpd.c
index 9b9608628d4d393dc654bfbe966d5bdc17cb48a7..5d66e23a50b5e5590b848cde75f2a896f678d90f 100644
--- a/package/uhttpd/src/uhttpd.c
+++ b/package/uhttpd/src/uhttpd.c
@@ -948,9 +948,10 @@ int main (int argc, char **argv)
 					for (opt = 0; optarg[opt]; opt++)
 						if (optarg[opt] == '+')
 							optarg[opt] = ' ';
-
-					memset(port, 0, strlen(optarg)+1);
-					uh_urldecode(port, strlen(optarg), optarg, strlen(optarg));
+					/* opt now contains strlen(optarg) -- no need to re-scan */
+					memset(port, 0, opt+1);
+					if (uh_urldecode(port, opt, optarg, opt) < 0)
+					    fprintf( stderr, "uhttpd: invalid encoding\n" );
 
 					printf("%s", port);
 					free(port);