<feed xmlns='http://www.w3.org/2005/Atom'>
<title>uhttpd, branch master</title>
<subtitle>Tiny HTTP server</subtitle>
<id>https://git.openwrt.org/project/uhttpd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/uhttpd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/'/>
<updated>2026-08-24T12:19:03Z</updated>
<entry>
<title>Merge commit 'refs/pull/34/head' of https://github.com/openwrt/uhttpd</title>
<updated>2026-08-24T12:19:03Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-24T12:19:03Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=373145f72c884c36a2b16f7f47e74ffae06bd754'/>
<id>urn:sha1:373145f72c884c36a2b16f7f47e74ffae06bd754</id>
<content type='text'>
</content>
</entry>
<entry>
<title>file: enforce Basic Auth realms for URL-prefix handlers</title>
<updated>2026-08-24T12:07:20Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-08-24T12:03:22Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=3aa7e1c7678281220470e24784bcdcfb71bda6cf'/>
<id>urn:sha1:3aa7e1c7678281220470e24784bcdcfb71bda6cf</id>
<content type='text'>
uh_handle_request() selects check_url dispatch handlers (lua, ucode and
ubus) via dispatch_find(url, NULL) and invokes them through
uh_invoke_handler() before reaching __handle_file_request(), which holds
the only uh_auth_check() call. As a result a configured Basic Auth realm
was never evaluated for those handlers: a request to a lua/ucode/ubus
prefix was served without authentication, and even invalid credentials
were ignored. CGI scripts are not affected because they register a
check_path handler and fall through to the authenticated file path, so
the same realm protected /cgi-bin/foo but silently not /api/foo.

Evaluate the matching realm against the request URL before invoking a
check_url handler. uh_auth_check() already emits the 401 challenge and
tears the request down on failure, so handlers stay consistent with the
static file and CGI paths. Deployments without a realm covering the
prefix are unaffected.

Link: https://github.com/openwrt/uhttpd/security/advisories/GHSA-5cgm-8h9x-v28c
Reported-by: @aramosf
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt; [return style fix]
</content>
</entry>
<entry>
<title>client: set TCP_NODELAY on accepted sockets</title>
<updated>2026-08-05T15:30:11Z</updated>
<author>
<name>Ivan Kvashonkin</name>
</author>
<published>2026-07-26T14:00:18Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=82b4c79ec266942251f95010eae700ddc33899ca'/>
<id>urn:sha1:82b4c79ec266942251f95010eae700ddc33899ca</id>
<content type='text'>
uh_accept_client() leaves the accepted socket with Nagle enabled, and a
response is emitted as one write() per header line plus one for the body.
The kernel therefore holds the small final segment: tcp_nagle_check()
allows a partial segment only if TCP_NODELAY is set, TCP_CORK is set, or
every small packet sent so far has been acknowledged (Minshall's
modification, tcp_minshall_check()). On a connection that has just
answered a request, the previous response is still unacknowledged, so the
next small body waits for the peer's delayed ACK. Every request after the
first on a keep-alive connection pays that.

Reproduced without LuCI, ubus, a session or a browser - a 5-byte static
file and four sequential requests on one connection, on 25.12 (x86_64) and
24.10.7 (uhttpd 2025.07.06~7e64e8ba-r4):

  req1 0.4 ms / 1.0 ms      (first on the connection)
  req2 41.5 ms / 42.2 ms
  req3 44.6 ms / 48.8 ms
  req4 44.6 ms / -
  fresh connection each time: 0.3-0.4 ms / 0.24-0.33 ms

strace shows uhttpd is not the slow part: it wrote response 2 within 0.4 ms
of response 1, while the client's next request only arrived 42.5 ms after
that write. The stall window is the peer's delayed ACK timer, so it depends
on the client; the figures above are Linux peers.

Both builds come from the 25.12.5 release SDK, and the unpatched build is
byte-identical to the published uhttpd-2026.06.16~7b1bec45-r1.apk, so the
before column is what is shipping. Same box, http_keepalive at its default:

  unpatched  0.35 / 42.9  / 44.8  / 44.8  ms
  patched    0.32 / 0.087 / 0.075 / 0.075 ms

LuCI is where this is visible, since its ubus JSON-RPC replies run a few
hundred bytes and a page issues several: warm in-place navigation over five
config pages went from 540 ms to 284 ms with keep-alive untouched.

The trade-off is packet count, and it is measured rather than assumed.
Disabling Nagle lets each of the eight writes leave as its own segment: the
four-request reproducer goes from 13 to 32 server-to-client segments, and a
full LuCI page load from 752 to 1211 packets for the same payload. For an
admin interface that is a few dozen extra packets per page view against
40 ms stalls, but the real answer is to coalesce the response into fewer
writes as well - nginx does both, enabling TCP_NODELAY when a connection
enters the keep-alive state. Worth noting for that: ustream already carries
a `more` flag from ustream_write() down to the backend, and ustream_fd_write()
ignores it, using write() rather than send() with MSG_MORE - while
ustream_vprintf() hardcodes more=false, so no printf-based header emission
can express it today.

Signed-off-by: Ivan Kvashonkin &lt;vizzlef@gmail.com&gt;
</content>
</entry>
<entry>
<title>tls: add SNI-based redirect support</title>
<updated>2026-08-03T09:49:17Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:45:09Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=60f64bec40c8113cf09815ec377761b1f4f95f22'/>
<id>urn:sha1:60f64bec40c8113cf09815ec377761b1f4f95f22</id>
<content type='text'>
Add -Q option to configure SNI hostname to URL redirect mappings.
When a TLS client presents a matching SNI name, uhttpd responds
with 301 Moved Permanently and the configured Location URL.

Multiple -Q entries are supported for mapping different hostnames
to different targets.

Note: requires ustream-ssl backend populating ssl.server_name.

Closes: #19
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>lua, ucode: use buffered stdio for script output</title>
<updated>2026-08-03T09:38:25Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:38:25Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=c54ba119cf54427123087aed7955b05ca32d6ae7'/>
<id>urn:sha1:c54ba119cf54427123087aed7955b05ca32d6ae7</id>
<content type='text'>
Replace raw write() syscalls with fwrite() and enable full
buffering via setvbuf() to reduce the number of pipe writes from
script handlers to the parent process.

Expose uhttpd.flush() to both Lua and ucode so handlers can
explicitly flush the output buffer when needed.

Fixes: #17
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>file: dereference symlinks when looking up MIME type</title>
<updated>2026-08-03T09:28:20Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:28:20Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=067466662368cc13166301ccb4ecc8d0301a0db1'/>
<id>urn:sha1:067466662368cc13166301ccb4ecc8d0301a0db1</id>
<content type='text'>
uh_file_mime_lookup() determined Content-Type from the path string
extension, which for symlinks is the link name rather than the target.
Use lstat() and readlink() to resolve the target path before
inspecting the extension.

Also switch the serving path from pi-&gt;name (relative) to pi-&gt;phys
(absolute) so readlink() can resolve the link correctly.

Fixes: #14
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>utils: strip trailing whitespace from header field-values</title>
<updated>2026-08-03T09:17:14Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:17:14Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=d0b89eb7c70b0a048e1c8c6fb6d0c6e216159692'/>
<id>urn:sha1:d0b89eb7c70b0a048e1c8c6fb6d0c6e216159692</id>
<content type='text'>
RFC 9110 §5.5 defines received-field-value as OWS field-content OWS,
requiring both leading and trailing SP/HTAB to be stripped.

uh_split_header() already trimmed leading WSP; extend it to trim
trailing WSP as well by accepting the string length from callers,
avoiding an extra strlen() or memchr() scan.

Fixes: #11
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>ubus: release client refcount on successful /ubus/subscribe</title>
<updated>2026-08-03T08:13:52Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:13:52Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=3c48b9e17d2086f9756b97e5825c2ebdf3ed233a'/>
<id>urn:sha1:3c48b9e17d2086f9756b97e5825c2ebdf3ed233a</id>
<content type='text'>
uh_ubus_handle_get_subscribe() takes a client reference but never
releases it on the success path, so cl-&gt;refcount stays &gt;= 1 forever.
This prevents client_close() from freeing the client and unregistering
the ubus subscriber, eventually exhausting max_connections and wedging
the listener.

Fix by adding the missing uh_client_unref() before return.

Fixes: GHSA-wvgh-cm54-q6f6
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>client: move unread-body guard into uh_request_done()</title>
<updated>2026-08-03T08:11:20Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:11:20Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=d6205aad61763b5e5d86441164b3aeed83294e7f'/>
<id>urn:sha1:d6205aad61763b5e5d86441164b3aeed83294e7f</id>
<content type='text'>
uh_request_done() returns a keep-alive connection to CLIENT_STATE_INIT
without checking whether the request body was consumed. Handlers that
do not set d-&gt;data_send (static files, HEAD, GET /ubus, etc.) leave
the body in the ustream, causing it to be parsed as the next request
— classic CL.0 request smuggling.

Move the existing guard from uh_client_error() into uh_request_done()
so all success paths are covered, making the error-path copy redundant.

Fixes: GHSA-c2wg-hcff-hqrm
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>ubus: set d-&gt;free unconditionally to fix url_path leak</title>
<updated>2026-08-03T08:04:53Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:04:53Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=42e30caa704e4e6e28fc7412e0006959eb247c44'/>
<id>urn:sha1:42e30caa704e4e6e28fc7412e0006959eb247c44</id>
<content type='text'>
uh_ubus_handle_request() allocates du-&gt;url_path for every HTTP method,
but only the POST branch installed d-&gt;free = uh_ubus_request_free — the
sole code path that frees it. For all other methods, request_done()
zeroes the dispatch struct, NULLing url_path without freeing it.

Fix by setting d-&gt;free unconditionally; uh_ubus_request_free already
guards all other fields against uninitialized state.

Fixes: GHSA-83vv-qrc6-h3hx
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
</feed>
