Currently handle_errors seems to be able to cause unexpected behaviour, at least behaviour that is not immediately obvious.
As an example:
Caddyfile (using Bob & hiccup Basic Auth from docs)
root * /usr/share/caddy/
file_server
basicauth /* {
Bob JDJhJDEwJEVCNmdaNEg2Ti5iejRMYkF3MFZhZ3VtV3E1SzBWZEZ5Q3VWc0tzOEJwZE9TaFlZdEVkZDhX
}
handle_errors {
@404 {
expression {http.error.status_code} == 404
}
rewrite @404 /404.html
file_server
}
404.html
my 404 page
index.html
hello world
Request index.html with correct auth:
$ curl -L -k -D - https://Bob:hiccup@localhost
HTTP/2 200
accept-ranges: bytes
content-type: text/html; charset=utf-8
etag: ..
last-modified: ...
server: Caddy
content-length: 11
date: ...
hello world
OK
Request non existing URL with correct auth:
$ curl -L -k -D - https://Bob:hiccup@localhost/abc
HTTP/2 404
content-type: text/html; charset=utf-8
etag: ...
server: Caddy
content-length: 11
date: ...
my 404 page
OK
Request index.html with incorrect auth:
$ curl -L -k -D - https://Bob:hiccu@localhost/
HTTP/2 401
content-type: text/html; charset=utf-8
etag: ...
server: Caddy
www-authenticate: Basic realm="restricted"
content-length: 11
date: ...
hello world
Oops - the secret content was revealed.
Request non existing URL with incorrect auth:
$ curl -L -k -D - https://Bob:hiccu@localhost/abc
HTTP/2 200
server: Caddy
www-authenticate: Basic realm="restricted"
content-length: 0
date: ...
Oops - a 200 response was returned instead of 401.
By adding w.WriteHeader(errStatus) to caddyhttp/server.go at the end of the // well... this is awkward block, the behaviour becomes as I would anticipate. The index.html is not rendered in the case of invalid auth and a 401 status is returned not 200 in the case of both invalid auth and a non existing URL.
I would like to suggest that the response behaviour when the handle_error routes return an error is as if there were no handle_error routes defined.
This is working as expected -- you're only changing the request's URI on 404 errors, so the 401 doesn't have any rewrites done, so it just flows back into another file server handler, this time without any auth protections.

Instead of limiting the rewrite to just 404s, consider rewriting all requests: rewrite * /{http.error.status_code}.html
yes sure - I stepped through the code and understand what and why is going on.
But does it make sense to be how it is ?
Over and out
@richardjennings Yes, it is very flexible and powerful! AFAIK almost all the feature requests from v1 related to error handling are resolved with this, and it can even do things other web servers aren't capable of (without extra scripting or whatever).
Most helpful comment
This is working as expected -- you're only changing the request's URI on 404 errors, so the 401 doesn't have any rewrites done, so it just flows back into another file server handler, this time without any auth protections.
Instead of limiting the rewrite to just 404s, consider rewriting all requests:
rewrite * /{http.error.status_code}.html