express.static dotfiles option: preventing accidental exposure of .env and .git under a shared root
0 reputation · 17 May 2023, 17:27 UTC
0 reputation · 17 May 2023, 17:27 UTC
I am configuring express.static() to serve a user-facing asset directory in an Express 4.x application, and the root folder sits close to files that must never be public, such as .env and .git.
The documented dotfiles option defaults to ignore, which returns 404 for dotfile requests. My uncertainty is the permission boundary: the option only governs dotfile URL handling, and the resolved root path is relative to process.cwd(), so the actual exposure depends on deployment layout rather than any framework-level enforcement. If the working directory changes between environments, a sibling directory containing secrets could fall under the served root without any warning.
I want a configuration strategy that keeps the default ignore behavior but adds a hard guarantee that sensitive paths cannot be served even if the root is misconfigured, without relying solely on directory hygiene.
Is there a documented way to make express.static() fail closed (for example via the fallthrough option or a custom setHeaders check) when a requested path resolves outside an intended allowlist? Should the root be resolved with an absolute path computed at startup, and does Express validate it? Is any of this behavior different in Express 5.x?
31775 reputation · 18 May 2023, 05:23 UTC
In Express 4.x (and the current Express 5.x beta) the built‑in express.static middleware will only honour the dotfiles option for files that begin with a dot. It does not perform any check that the supplied root does not contain other sensitive siblings. The root is resolved against process.cwd() unless you give an absolute path. Therefore, if you run the app from a directory that is the parent of .env or a .git folder, the middleware could resolve those files and serve them (unless dotfiles: 'ignore' blocks the dotfile itself).
Express does not validate the root against an allowlist, nor does the fallthrough option help you “fail closed”. The only framework‑level guarantee is that dotfiles: 'ignore' will return 404 for dotfiles; everything else is up to how you configure the root.
serve-static package, which in turn uses the send library to resolve paths and guard against directory traversal.express.static, it is resolved against process.cwd() at runtime.express.static; no new security guarantees are added.const path = require('path');
const staticRoot = path.resolve(__dirname, 'public');
app.use(express.static(staticRoot, { dotfiles: 'ignore', fallthrough: false })); This guarantees the root is always the same, regardless of where the process is launched.const path = require('path');
const allowedRoot = path.resolve(__dirname, 'public');
app.use((req, res, next) => {
const resolved = path.resolve(allowedRoot, '.' + req.path);
if (!resolved.startsWith(allowedRoot)) {
return res.status(403).send('Forbidden');
}
next();
}); The guard runs before express.static and rejects any request that would resolve outside the intended directory.dotfiles: 'ignore' to block dotfile URLs: Even with the guard, it is good practice to keep the default to avoid accidental exposure of files that start with a dot./../.env or /../.git from a client. You should see a 403 from the guard or a 404 from dotfiles: 'ignore'.If you are already using an absolute path for express.static and still see a potential for sibling exposure, confirm whether your deployment changes process.cwd() or moves the root directory relative to __dirname. That detail can change the recommendation slightly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.