20 Oct 2026
Page headers end at the right line in templates
- What was broken
- A page can start with a header block that ends with three dots. If the page also had a line of three dashes further down, Caddy ended the header there, and the text in between disappeared.
- What I changed
- I made it end the header at whichever closing line comes first.
- Why it matters
- Pages with a divider line in the body keep all their text.
see the code, #8259 ↗ (opens in a new tab)
19 Oct 2026
The log filter for query strings reads URLs the way the server does
- What was broken
- Caddy can hide secret values in the query string of logged URLs. The filter read the URL differently from the server, so anything after a hash sign slipped past it and was written to the log in plain text.
- What I changed
- I made the filter read the URL the same way the server does, so the whole query is filtered.
- Why it matters
- A token sent in a URL with a stray hash sign no longer leaks into the access logs.
see the code, #8255 ↗ (opens in a new tab)
19 Oct 2026
An impossible IP mask is caught when the config loads
- What was broken
- Caddy accepted IP masks in its log filter that cannot exist, and then logged a blank value instead of the client's address.
- What I changed
- I made Caddy reject an out-of-range mask when the config is loaded.
- Why it matters
- A typo in the log settings shows up at startup instead of quietly emptying the client addresses in the logs.
see the code, #8241 ↗ (opens in a new tab)
16 Oct 2026
Passwords hashed from a pipe match on Windows line endings
- What was broken
- Piping a password into the hashing command kept the Windows line-ending character as part of the password, so logins with the real password were refused. Input with no final newline failed outright.
- What I changed
- The command now drops a trailing line ending of either kind and accepts input without one.
- Why it matters
- A password hashed from a script or file works for logging in.
see the code, #8227 ↗ (opens in a new tab)
16 Oct 2026
Log folders get usable permissions
- What was broken
- With one log permission setting and no file mode, the server created missing log folders that even it could not open, then failed to start.
- What I changed
- Those folders now get permissions that match the log file's default, so the server can write into them.
- Why it matters
- The server starts and writes its logs with that setting.
see the code, #8225 ↗ (opens in a new tab)
16 Oct 2026
Each default header is checked on its own
- What was broken
- A settings block can add headers only when a response lacks them. If any one of those headers was already set, every default in the block was skipped, and a condition on the block was ignored for them.
- What I changed
- Each default header now checks only for itself, and the block's condition applies to it.
- Why it matters
- Security headers meant as defaults now get sent.
see the code, #8217 ↗ (opens in a new tab)
13 Oct 2026
Redirects from PHP-style apps now actually redirect
- What was broken
- Apps behind the server can answer with just a 'go here instead' address. The server sent that as a normal 'OK' page, so browsers stayed put instead of following the redirect.
- What I changed
- The server now sends it as a redirect, as the standard says and other servers already do. An app that sets its own status still wins.
- Why it matters
- Logins, form submits and other pages that redirect work the way the app meant.
see the code, #8209 ↗ (opens in a new tab)
13 Oct 2026
Uploads over the size limit are refused, not cut short
- What was broken
- With unlimited buffering turned on, the server ignored errors while reading a request. An upload over the size limit was cut off and passed on as if complete, and the user got 'OK' instead of 'too large'.
- What I changed
- Read errors now go through the same path as with a buffer limit, so the server reports them instead of hiding them.
- Why it matters
- Apps never receive a half upload that looks whole, and users are told when a file is too big.
see the code, #8207 ↗ (opens in a new tab)
12 Oct 2026
Custom error pages now follow the order you wrote them
- What was broken
- A site can set different error pages, like one for 'not found' and a general one for other mistakes. The server checked them in reverse order, so the general page showed up where the 'not found' page should have.
- What I changed
- The server now keeps the order written in the settings file.
- Why it matters
- Visitors see the error page the site owner meant for them.
see the code, #8195 ↗ (opens in a new tab)
10 Oct 2026
Pages that exist no longer show 'not found'
- What was broken
- One form of a setting that says 'try this file, then that one, else show not found' gave every page a 'not found' error, even pages that existed.
- What I changed
- Once one rule in a group has matched, the server now skips checking the rest of that group.
- Why it matters
- Sites using that setting serve their real pages again.
see the code, #8189 ↗ (opens in a new tab)
10 Oct 2026
A repeated rule line is now an error, not silently ignored
- What was broken
- In the server's settings file, a rule could list two custom conditions. The server quietly used only the first one, so the rule matched more requests than intended.
- What I changed
- The server now refuses that setup and says how to combine the conditions into one.
- Why it matters
- The mistake is caught when the settings load, instead of quietly changing which requests a rule applies to.
see the code, #8183 ↗ (opens in a new tab)
9 Oct 2026
Security settings stay on pages that send early hints
- What was broken
- Sites add extra instructions to every reply, like 'always use a secure connection'. If the backend first sent a quick early note (an 'early hint'), those instructions vanished from the final reply.
- What I changed
- I save the site's own instructions before handling the early note and put them back afterwards.
- Why it matters
- Security settings a site asks for now reach browsers every time.
see the code, #8169 ↗ (opens in a new tab)
9 Oct 2026
Successful requests are no longer retried into an error
- What was broken
- The server can pass requests to backend servers and retry on failure. A retry rule that checked only the request also retried replies that had succeeded, until the user got an error page.
- What I changed
- Retries based on the reply now happen only when the rule looks at the reply.
- Why it matters
- Requests that succeed the first time are sent once and reach the user, instead of failing.
see the code, #8168 ↗ (opens in a new tab)
9 Oct 2026
The server stopped lying about the size of compressed files
- What was broken
- When a program asked only for a file's details, not the file itself, the server reported a wrong size (20 bytes) for compressed files.
- What I changed
- The server still sends the right details, but no longer makes up that false size.
- Why it matters
- Tools that check a file's size before downloading, like download managers and caches, now get the truth.
see the code, #8150 ↗ (opens in a new tab)
8 Oct 2026
Page errors no longer send a broken, half-finished reply
- What was broken
- When a page the server fills in before sending (a template) had an error, the error reply still claimed the original file's size but had no content, so it looked cut off.
- What I changed
- On any template error, the server now drops the file's leftover details before sending the error.
- Why it matters
- Visitors and tools get a clean error message instead of a broken reply.
see the code, #8149 ↗ (opens in a new tab)
7 Oct 2026
Traffic now goes to the least busy server when all are busy
- What was broken
- One way of sharing traffic should send each request to the less busy of a few backends. Once all of them were busy, it picked at random, even the busiest one.
- What I changed
- I fixed the comparison so only the least busy backends stay in the running.
- Why it matters
- In a test with a slow and a fast backend, the fast one got 70 of 80 requests instead of 41.
see the code, #8160 ↗ (opens in a new tab)
7 Oct 2026
No more crash when a backend is down and weights are missing
- What was broken
- The server can share visitors between backends by weight (how much traffic each gets). With fewer weights than backends and one backend down, it crashed and visitors got empty replies.
- What I changed
- Backends without a weight are now treated as getting no traffic, instead of crashing the server.
- Why it matters
- A small gap in the setup no longer breaks the site when one backend goes down.
see the code, #8159 ↗ (opens in a new tab)
← every project