Skip to content

Redirect a directory to its trailing slash only when something is served there - #810

Merged
sdogruyol merged 2 commits into
masterfrom
fix/dir-redirect-discloses-directory
Sep 8, 2026
Merged

Redirect a directory to its trailing slash only when something is served there#810
sdogruyol merged 2 commits into
masterfrom
fix/dir-redirect-discloses-directory

Conversation

@sdogruyol

Copy link
Copy Markdown
Member

Description of the Change

HTTP::StaticFileHandler#normalize_request_path redirects /dir/dir/ whenever its @directory_listing flag is on. Kemal constructs the handler with the stdlib default (true) and never passes its own dir_listing setting down — it reads serve_static per request in directory_index instead. Result, with dir_listing and dir_index both off (the default):

GET /dir   → 302  Location: /dir/
GET /nope  → 404

Nothing is ever served at /dir/ in that configuration, so the redirect only tells the client that the directory is there. Directory enumeration under public/ for free.

The redirect now depends on serve_static: a directory URL gets its trailing slash when a listing or an index.html would answer at the slashed URL, and falls through to the same 404 as a missing path otherwise. The ≥ 1.17 branch overrides normalize_request_path; the legacy branch's is_dir check uses the same predicate. dir_index keeps the redirect on purpose — relative links in the index have to resolve against /dir/, not /.

Alternate Designs

  • Pass directory_listing: into the constructor from Config#setup_static_file_handler. Simpler, but the flag would be frozen at boot while directory_index reads the config at request time; serve_static can be called after the handler exists (the specs do). Reading it in one place per request keeps the two decisions consistent.

Verification

Specs: neither on → /dir is 404 with no Location (fails on master: 302); listing on → 302 /dir/; index on → 302 /dir/. Full suite green (481), ameba and format clean.

…ved there

The stdlib adds the trailing slash to a directory URL whenever its own
directory_listing flag is on, and Kemal never sets that flag - it decides
per request from serve_static. With dir_listing and dir_index both off,
/admin answered 302 /admin/ while /nope answered 404: the redirect was
the only difference, and it told a scanner which directories exist.

Both Crystal-version branches now consult serve_static: a directory URL
gets its canonical slash when a listing or an index.html would answer at
it, and falls through to the missing-path 404 otherwise.
@sdogruyol
sdogruyol merged commit f368532 into master Sep 8, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant