241 Commits
Author SHA1 Message Date
Kévin Dunglas 9dd12fdb5d fileserver: hide the data directory when it is relative (#8327) 2026-11-01 15:57:49 -05:00
Kévin Dunglas aea046c978 fileserver: don't match regular file when path ends by slash (#8326) 2026-11-01 15:57:10 -05:00
pamnarioandClaude Opus 5.5 4ec5a5c39a Merge commit from fork
* fastcgi: derive SCRIPT_FILENAME from the normalized request path

Path matchers evaluate a normalized request path — MatchPath applies
normalizeWindowsPath and then CleanPath before comparing — but the FastCGI
transport split r.URL.Path verbatim. Caddy does not rewrite r.URL.Path itself,
so the two disagreed whenever the path contained dot segments after the split
token: /admin.php/../../other.php was matched as /other.php while
SCRIPT_FILENAME resolved to <root>/admin.php, and on Windows the same held for
/admin.php\..\other.php. A route gated by a path matcher could therefore execute
a script that matcher never evaluated.

php_fastcgi generates a rewrite route that normally normalizes the path first,
but that route can fail to match, so the transport establishes the invariant
itself rather than inheriting it. The reason it can fail to match is addressed
in the following commit; this change does not depend on it.

caddyhttp.NormalizedPath is added so a consumer acting on a path a matcher has
already judged derives it exactly as the matcher did, rather than reassembling
the two steps and risking drift.

Scope — two divergences of the same class are NOT addressed here:

  * SCRIPT_NAME remains a prefix of the request path, so a matcher naming a
    script exactly (path /admin.php) is still reachable via PATH_INFO at
    /admin.php/anything. Matchers protecting a script need a trailing wildcard
    regardless; that is inherent to CGI, not to this bug.
  * On Windows, NTFS 8.3 short names are not rejected here. fileserver's
    ServeHTTP rejects them (and ':' for ADS) precisely because matchers cannot
    see through them, so GET /ADMIN~1.PHP is judged as /admin~1.php by a
    path matcher while NTFS resolves SCRIPT_FILENAME to admin.php. Closing that
    means sharing fileserver's hasWindowsShortName across packages, which is a
    larger change than this fix; it is reported alongside this one and a patch
    is available on request.

Side effects: PATH_INFO and DOCUMENT_URI are now derived from the normalized
path, so repeated slashes are merged and "." segments removed (/index.php//a//b
yields PATH_INFO=/a/b rather than //a//b), and on Windows per-component trailing
dots and spaces are folded. An application routing on a raw PATH_INFO will see
this. An empty URL.Path stays empty rather than becoming ".". REQUEST_URI is
unchanged and still carries the original target.

Tests: a buildEnv regression over literal and percent-encoded dot segments,
covering that legitimate PATH_INFO is preserved and an empty path stays empty; a
Windows-only counterpart whose cases each fail without the Windows
normalization; and an end-to-end test of a basic_auth-protected php_fastcgi
route against a FastCGI responder reporting the SCRIPT_FILENAME it received. The
end-to-end test uses `index off` so no rewrite route exists, ensuring it
exercises the transport rather than the rewrite.

* fileserver: make first_exist_fallback fall back when the file is absent

The try_policy first_exist_fallback is documented to match unconditionally,
falling back to the last try_files entry when no earlier candidate exists. It
did not: candidates are built with fs.Glob, which returns only paths that exist,
so an absent final entry left the list empty and the fallback branch — nested
inside the loop over that list — was unreachable.

php_fastcgi hard-codes this policy so its generated rewrite route always runs;
when the fallback failed, that rewrite was silently skipped.

Keep the pattern itself as a candidate when nothing globbed. This also aligns
Linux with Windows, where globResults is populated without an existence check
and the fallback already fired.

Behaviour change worth calling out: on a php_fastcgi site whose root has no
index.php (or no file matching the configured index), the rewrite now always
runs, so a request for a path that exists neither on the filesystem nor as a PHP
script is proxied to the backend as <root>/index.php instead of falling through
to file_server and returning 404. That is what the documented fallback promises,
but for a site of standalone .php scripts plus static assets it is a visible
change: unmatched requests now reach PHP-FPM.

The other try policies still test existence explicitly, so a candidate that does
not exist is skipped — but note this applies to ANY pattern that expands to
nothing, not only a malformed glob: a file whose literal name contains glob
metacharacters can now match under first_exist, largest_size and smallest_size
where previously globbing found it only via expansion.

* fileserver: synthesize the literal candidate only for the fallback entry

Address review: the previous commit appended the pattern itself as a
candidate inside makeCandidates whenever globbing found nothing. Since
makeCandidates is shared by every try_policy, first_exist, largest_size,
smallest_size and most_recently_modified could then stat the literal
pattern, so a file whose name contains glob metacharacters (for example
"lit[1].txt") matched where it did not before.

Restore makeCandidates to its original behaviour and build the literal
candidate only for the final entry of first_exist_fallback, the one
entry that is documented to match unconditionally. Placeholders in that
entry are expanded without glob escaping, since nothing is globbed, so
a request for /a[b].txt falls back to /a[b].txt rather than /a\[b].txt.
The path is still cleaned and joined with SanitizedPathJoin, so it
cannot escape the root.

This supersedes the last paragraph of the previous commit message: the
other policies now behave exactly as they did before that commit. The
php_fastcgi behaviour change described there still applies.

Add a regression test that creates a file literally named "lit[1].txt"
and checks that only the fallback entry of first_exist_fallback matches
it by its literal name, including the split_path remainder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fastcgi: add license headers to new test files

Address review: phpfastcgi_test.go and fastcgi_windows_test.go were
added without the Apache license header used across the codebase. On
the Windows test, the header goes above the build constraint, matching
replacer_fuzz.go.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fastcgi: avoid hardcoded Unix paths in dot-segment tests

Address review: the new tests compared SCRIPT_FILENAME against strings
with forward slashes ("/srv/other.php", a "/admin.php" suffix), but the
transport makes the root absolute and joins it with filepath.Join, so
on Windows the value is C:\srv\other.php and the assertions would fail.
Use a temporary directory as the root and build the expected paths with
filepath instead.

Also lower the bcrypt cost of the integration test's basic_auth hash to
the minimum. At cost 14 the authenticated request exceeded the 5s test
client timeout under -race.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 10:13:03 -06:00
Shane 69f22e2442 fileserver: return an empty listing for offsets past the end (#8213)
Signed-off-by: Shane Jonas <jonas.shane@gmail.com>
2026-10-14 01:36:53 +10:00
ammann d65583e6d2 fileserver: apply hide rules to browse entries and sidecars (#8155) 2026-10-08 13:26:26 +10:00
awss 96eadf18f6 fileserver: stat symlink targets by decoded path in browse listings (#8152)
directoryListing built each symlink's lookup path from the escaped
request path. In a directory whose name needs escaping, such as one with
a space, the stat failed, so directory symlinks were listed as files,
file symlinks showed the link's own size, and reveal_symlinks showed no
target.

Use the decoded path, which the function already computes for the page
title. The escaped path is still used for the page path and item URLs.
2026-10-05 22:42:50 +00:00
klokv 7ba5e147ac fileserver: treat a path below a regular file as not found (#8131)
Since #7017, every *fs.PathError from stat becomes fs.ErrInvalid, and
that includes ENOTDIR. So a request like /index.php/foo got 400 instead
of 404, and pass_thru no longer fell through to the next handler.

Map ENOTDIR to fs.ErrNotExist before that check. Other path errors, such
as NUL bytes, overly long names, or transient I/O failures, keep their
current handling.
2026-10-04 22:13:24 +10:00
Zen Dodd 90ccea768f build: bump all dependencies - (Go 1.26 floor) (#8056)
* build: bump deps

* chore: note HTTP/2 migration follow-ups
2026-09-30 10:54:12 -06:00
techknowlogick df77f8bde1 chore(deps): bump cel-go and switch import to new url (#8030) 2026-09-17 04:09:23 +03:00
Sash 97a08d8251 fileserver: reject non-integer browse file_limit (#7988) 2026-09-04 16:37:02 +10:00
David Carliez 3244ef4105 fileserver: reject short names in every path component (#7952)
* fileserver: reject short names in every path component
Signed-off-by: DavidCarliez <271374756+DavidCarliez@users.noreply.github.com>

* fileserver: validate short-name characters
Signed-off-by: DavidCarliez <271374756+DavidCarliez@users.noreply.github.com>

* fileserver: fail closed on extended short names
Signed-off-by: DavidCarliez <271374756+DavidCarliez@users.noreply.github.com>
2026-08-28 09:09:23 +00:00
Faiyaz Rahman 4974956b9c chore: fix lint errors from newer golangci-lint (#7958)
Signed-off-by: Faiyaz Rahman <faiyazrahman03@gmail.com>
2026-08-25 13:12:10 +10:00
Kévin Dunglas d6f7f18b04 Merge commit from fork
* fileserver: add failing test for calculateEtag collision

calculateEtag concatenates base36(mtime) and base36(size) with no
separator, so distinct (mtime, size) pairs can yield identical digit
strings and thus identical ETags.

* fileserver: prevent ETag collisions by separating mtime and size components

calculateEtag concatenated base36(mtime) and base36(size) with no
separator, so distinct (mtime, size) pairs could decode to the same
digit string and produce identical ETags.
2026-08-11 13:23:18 -06:00
Christian Mehlmauer 2adc763020 fileserver: speed up directory browsing for large directories (#7903)
* improve fileserver performance and allocations
* skip instead of fatal
* fix comment
* implement more suggestions
* use filesystems.OsFS instead of os.DirFS
* fmt
* remove outdated benchmarks
2026-08-01 00:38:04 +10:00
Matthew Holt 6ab855d3c4 browse: Update Caddy logo 2026-06-20 13:45:10 -06:00
Luccin Masirika 39c9a85f80 fileserver: append repeated hide subdirectives instead of overwriting (#7817)
Multiple `hide` subdirectives in a file_server Caddyfile block silently overwrote each other, so only the last one took effect. Append to the list instead, so repeated entries accumulate and imported snippets can compose with site-specific hides.
2026-06-13 22:44:19 +10:00
Sam Ottenhoff 997d3f6b0a encode: add standard benchmark and conformance harness (#7804)
This is a shared encode_test harness with HTML/JSON/JS/CSS payloads taken from caddyserver.com

Benchmarks:
- BenchmarkStandardEncodingPayloads: raw encoder NewEncoder/Write/Close path
- BenchmarkEncodeHandlerCorpus: full Encode.ServeHTTP middleware path
- Grid: 4 payloads × gzip levels 1/5/9 × zstd fastest/default/best
- Each subtest runs with 4 parallel workers; compare runs on MB/s and allocs/op

Conformance tests:
- Encoder contract: Reset, Flush, Close, and pool-style Reset-after-Close reuse
- Corpus HTTP encoding: Content-Encoding, Vary, ETag suffix, header stripping
- Response semantics: minimum_length, 304, HEAD, range, WebSocket bypass,
  If-None-Match rewrite, Cache-Control no-transform, content-type matcher rejection
2026-06-11 17:55:18 -06:00
Zen Dodd 88037f1666 chore: clean up wording and typo fixes (#7745)
* chore: clean up wording and typo fixes
* chore: ASCII -> alphanumeric in lexer for heredoc marker
2026-05-20 16:36:30 +10:00
Brett Bethke 0125ae39cc caddyhttp: omit Last-Modified for unusable mod times (#7740)
See #5548 and #7730
2026-05-20 16:19:11 +10:00
Rayan Salhabandcyphercodes a4a38c3e88 rewrite: escape file matcher paths before rewriting (#7683)
* fix: escape file matcher paths in rewrites

Preserve matched file paths containing literal '?' or '%' when try_files rewrites to http.matchers.file.relative.

* test: cover nested escaped try_files rewrite paths

* test: cover encoded slash try_files rewrite paths

* fix: assert file matcher placeholder as string

---------

Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
2026-05-11 17:16:33 -06:00
Zen Dodd d2172bea61 chore: Fix golangci-lint 2.12.1 findings (#7690) 2026-05-07 03:40:26 -04:00
Max Truxa 7586e68e27 fileserver: show symlink targets verbatim (#7579)
`reveal_symlinks` was exposing symlink targets as fully resolved absolute paths, even if the target is a relative path. With this change the link target is shown as-is, without resolving anything.
2026-04-15 04:49:30 +10:00
Oleh Konko | semantic verification for trust infra | LLM-augmented operations pipeline (precision-first, claim≤evidence, submit-human) | verify the payload, not the signer 566e710991 fileserver: document hide case-sensitivity (F-CADDY-FILESERVER-HIDE-CASE-001) (#7548) 2026-03-04 17:00:10 -05:00
Matthew Holt cb436f0a0e fileserver: Fix tests on Windows 2026-02-20 11:46:45 -07:00
Matthew Holt a2825c5dd9 fileserver: Replace \ with \\ in file matcher paths 2026-02-19 13:18:14 -07:00
Matt Holt 95941a71e8 chore: Add nolints to work around haywire linters (#7493)
* chore: Add nolints to work around haywire linters

* More lint wrangling
2026-02-17 16:52:54 -07:00
WeidiDeng 47f3e8f8dc use math/rand/v2 instead of math/rand (#7413) 2026-02-11 09:15:51 -07:00
Francis Lavoie 90972fbebc chore: Dumb prealloc lint fix (#7430) 2026-01-13 14:13:43 -05:00
WeidiDengandMatt Holt b462615439 fileserver: set Content-Length for precompressed files (#7251)
* set Content-Length for precompressed files

* Update modules/caddyhttp/fileserver/staticfiles.go

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-09-10 22:48:03 -06:00
WeidiDengandMatt Holt 67debd0e11 fileserver: set Range header for precompressed static files to force Content Length header to appear (#7042)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-08-22 08:23:13 -06:00
Luka T. KorošecandMatt Holt b9710c6af4 fileserver: Add a few doc lines about Etag file content (#7173)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-08-21 16:02:10 -06:00
Cédric Félizard 790f3e0885 fileserver: denote license for embedded JavaScript for LibreJS (#7127)
This commit adds support for LibreJS (https://en.wikipedia.org/wiki/GNU_LibreJS).

LibreJS would block this embedded JavaScript because its license is not stated in a machine-readable format.

Signed-off-by: Cédric Félizard <cedric@felizard.fr>
2025-07-16 11:49:49 -06:00
gopherorg 33c88bd2bb refactor: replace HasPrefix+TrimPrefix with CutPrefix (#7095)
Signed-off-by: gopherorg <gopherworld@icloud.com>
2025-06-27 22:04:09 +03:00
filipRatajczak 3b4d966fba fileserver: Add sort buttons in grid mode (#7089)
* [ADD] sort buttons in grid mode

* [CHANGE] replace spaces with tabs
2025-06-23 13:26:45 -06:00
Mohammed Al Sahaf e039a5bb5c chore: upgrade .golangci.yml and workflow to v2 (#6924)
* chore: upgrade .golangci.yml and workflow to v2

run `golangci-lint fmt`

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* run `golangci-lint run --fix`

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* more lint fixes

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* bring back comments to .golangci.yml

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* appease the linter some more

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* oops

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* use embedded structs

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* use embedded structs where they were used before

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* disable rule  `-QF1006`

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* missed a spot

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2025-06-03 02:24:32 +03:00
Jimmy Lipham 94147caf31 fileserver: map invalid path errors to fs.ErrInvalid, and return 400 for any invalid path errors. (close #7008) (#7017) 2025-05-13 07:43:27 -06:00
Steffen Busch 54d03ced48 fileserver: Add support for .avif image format (#6988) 2025-04-28 08:32:59 -06:00
Matt Holt 5a6b2f8d1d events: Refactor; move Event into core, so core can emit events (#6930)
* events: Refactor; move Event into core, so core can emit events

Requires some slight trickery to invert dependencies. We can't have the caddy package import the caddyevents package, because caddyevents imports caddy. Interface to the rescue!

Also add two new events, experimentally: started, and stopping. At the request of a sponsor.

Also rename "Filesystems" to "FileSystems" to match Go convention (unrelated to events, was just bugging me when I noticed it).

* Coupla bug fixes

* lol whoops
2025-03-29 08:15:43 -06:00
WeidiDeng 22563a70eb file_server: use the UTC timezone for modified time (#6830)
* use UTC timezone for modified time

* use http.ParseTime to handle If-Modified-Since

* use time.Compare to simplify comparison

* take the directory's modtime into consideration when calculating lastModified

* update comments about If-Modified-Since's handling
2025-02-10 08:39:43 -07:00
Kévin Dunglas 5c2617ebf9 fileserver: good default for precompressed (#6736) 2024-12-10 08:31:43 -07:00
Francis Lavoie d0123bd760 fileserver: Fix policy Validate() oversight (#6727) 2024-12-04 14:01:58 -05:00
Kévin Dunglas efd9251ad3 fileserver: Add first_exist_fallback strategy for try_files (#6699)
* feat: add first_exist_or_fallback strategy for try_files

* fix tests

* linter
2024-12-03 05:44:49 -07:00
Francis Lavoie 315715e90f core: Implement FastAbs to avoid repeated os.Getwd calls (#6687)
* core: Implement FastAbs to avoid repeated os.Getwd calls

* Lint

* Rename files
2024-11-13 03:55:51 -05:00
Atakan Yenel cc23ad6402 fileserver: Add file_limit option for browse (to be experimental) (#6648)
* Add file_limit option for file_server browse

* Move file_limit inside browse.

* add file_server_file_limit caddyfile adapt test.
2024-11-05 09:35:32 -07:00
Francis Lavoie 09b2cbcf4d caddyhttp: Add MatchWithError to replace SetVar hack (#6596)
* caddyhttp: Add `MatchWithError` to replace SetVar hack

* Error in IP matchers on TLS handshake not complete

* Use MatchWithError everywhere possible

* Move implementations to MatchWithError versions

* Looser interface checking to allow fallback

* CEL factories can return RequestMatcherWithError

* Clarifying comment since it's subtle that an err is returned

* Return 425 Too Early status in IP matchers

* Keep AnyMatch signature the same for now

* Apparently Deprecated can't be all-uppercase to get IDE linting

* Linter
2024-11-04 23:18:50 +00:00
Matthew Holt 350ad38f63 fileserver: Fix Caddyfile parsing
Reported at https://github.com/mholt/caddy-sqlite-fs/issues/3
2024-10-31 10:37:37 -06:00
Logan FleurandFrancis Lavoie 9753c44510 fileserver: fix try_policy when instantiating file matcher from CEL (#6624)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2024-10-20 14:08:30 +00:00
Francis Lavoie 792f1c7ed7 caddyhttp: Escaping placeholders in CEL, add vars and vars_regexp (#6594)
* caddyhttp: Escaping placeholders in CEL

* Simplify some of the test cases

* Implement vars and vars_regexp in CEL

* dupl lint is dumb

* Better consts for the placeholder CEL shortcut

* Bump CEL version, register a few extensions

* Refactor s390x test script for readability

* Add retries for s390x to smooth over flakiness

* Switch to `ph` for the CEL shortcut (match it in templates cause why not)
2024-10-02 06:34:04 -06:00
Francis Lavoie 2faeac0a10 chore: Use slices package where possible (#6585)
* chore: Use slices package where possible

* More, mostly using ContainsFunc

* Even more slice operations
2024-09-25 14:30:56 -06:00
Kévin Dunglas f4bf4e0097 perf: use zap's Check() to prevent useless allocs (#6560)
* perf: use zap's Check() to prevent useless allocs

* fix

* fix

* fix

* fix

* restore previous replacer behavior

* fix linter
2024-09-13 11:16:37 -06:00