Check extensions before performing directory redirects
Problem
When using the extensions option, send inconsistently handles path resolution depending on whether a directory exists at the requested path.
For example, given this file structure:
dist/
about.html
about/
team.html
contact.html
When requesting paths with extensions: ['html'] configured:
-
/contact(no directory exists) → servescontact.html -
/about(directory exists) → redirects to/about/instead of servingabout.html
This inconsistency occurs because:
- When a path doesn't exist,
sendreceives an ENOENT error and tries extensions, successfully servingcontact.html - When a path exists as a directory,
sendimmediately callsredirect(path)without trying extensions first - The extension checking logic only runs when the initial
stat()returns ENOENT
This behavior is problematic for static site generators and frameworks that want to serve files like about.html from the URL /about (no trailing slash) while also having subdirectories like about/team.html.
Solution
Modified sendFile() to attempt extension resolution before redirecting when:
- The path resolves to a directory
- Extensions are configured
- The path has no file extension
- The path doesn't end with a separator
The next() function now tracks whether the original path was a directory via an isDir parameter. If all configured extensions fail to
resolve to a file and the original path was a directory, it falls back to the standard directory redirect behavior.
This ensures consistent behavior:
-
/contactwithcontact.htmlpresent → servescontact.html -
/aboutwithabout.htmlpresent → servesabout.html -
/aboutwith onlyabout/directory → redirects to/about/ -
/aboutwith both present → servesabout.html(prioritizes file over directory)
References
Resolves https://github.com/pillarjs/send/issues/194 Related to https://github.com/expressjs/serve-static/issues/138