Open directory indexes: when your file server lists everything
When directory indexing is enabled, visiting a path like /uploads/ returns a browsable list of every file in that directory instead of a 403 or 404. Attackers look here first because backup archives, log files, and uploaded user content are regularly left world-readable inside web-served directories.
What it is
Directory listing (also called directory indexing) is a web server feature that generates an HTML file browser when a directory is requested and no index file is present. Enabled by default in some nginx configurations (autoindex on) and Apache (Options +Indexes), it's a convenience for static hosts that becomes a liability anywhere files shouldn't be public.
AppSafe checks a set of commonly misconfigured paths — /uploads/, /files/, /backup/, /backups/, /logs/, and /tmp/ — and flags any that return a recognisable directory listing response. Even one open directory can expose data that was never meant to be discoverable.
Why it matters
Exposed directories routinely contain user-uploaded files, compressed database dumps, application logs with credentials or session tokens, and source archives left over from deploys. Finding them requires no authentication and no exploitation — just typing the URL.
Automated scanners probe the same well-known paths AppSafe checks, continuously, against every reachable host. An open /backup/ directory containing a .sql.gz dump is effectively a full database export handed over without a password.
How to fix it
Remove or explicitly turn off the autoindex directive in any server or location block that serves user-controlled directories.
location /uploads/ {
autoindex off;
# Serve individual files but never list directory contents
}The safest fix is to store uploads, backups, and logs in a directory the web server doesn't serve at all. Your application reads and streams files through an authenticated route — the raw filesystem path is never reachable.
Never put user uploads or backups inside the public/ directory. Serve them through an API route that can enforce authentication and sanitise the requested path.
// app/api/uploads/[filename]/route.ts
import { getSession } from "@/lib/auth-session";
import { readFile } from "fs/promises";
import path from "path";
export async function GET(
_req: Request,
{ params }: { params: { filename: string } }
) {
const session = await getSession();
if (!session) return new Response(null, { status: 401 });
// path.basename prevents directory traversal
const safe = path.basename(params.filename);
const filePath = path.join(process.cwd(), "private-uploads", safe);
const data = await readFile(filePath);
return new Response(data);
}If you genuinely need a browsable file interface (a media library, an internal log viewer), protect the path with authentication rather than relying on obscurity.
location /logs/ {
autoindex on;
auth_basic "Internal only";
auth_basic_user_file /etc/nginx/.htpasswd;
}FAQ
Is directory listing always a problem, or only on certain paths?
It's a problem wherever the files contain anything sensitive. A public CDN serving static images with indexing on is usually harmless; /logs/, /backup/, and /uploads/ almost never are, because they accumulate files that weren't vetted for public access.
Can I just drop an empty index.html in the directory to block listing?
That hides the listing for that one directory but leaves every subdirectory open. It's also easy to miss when new directories are created. Turning off autoindex at the server level is a reliable, configuration-level fix that covers everything beneath it.
What severity does AppSafe report for an open directory index?
Medium. The listing itself isn't the compromise — it's the path to one. A browsable /backup/ directory containing a database dump or a /logs/ directory with authentication tokens in plain text quickly escalates beyond medium on its own.
Is your app affected?
AppSafe checks for this and dozens of other issues in one free scan.
Scan my app freeRelated guides
Exposed database backups: what happens when /backup.sql is reachable
A reachable .sql dump is a complete hand-off of your entire database.
Exposed .git directory: the source-code leak hiding in plain sight
A reachable /.git hands over your entire codebase and commit history.
Verbose error pages and stack traces in production
A stack trace tells attackers exactly where to aim.