How to Disable Directory Listing in Nginx

How to Disable Directory Listing in Nginx

How to Disable Directory Listing in Nginx

A few years ago I was doing a routine security check on a client’s server and stumbled onto something that made my stomach drop: navigating to /uploads/ on their site showed a full, browsable file listing — every filename, every file size, every upload timestamp, all sitting there in plain view for anyone who typed the right URL. Nothing had been “hacked.” Nginx was just doing exactly what it does by default when it can’t find an index file in a directory: it shows you what’s inside. This guide is about making sure that never happens to you.

What Is Directory Listing, and Why Does It Happen?

When a client requests a URL that maps to a directory rather than a specific file — say, https://example.com/uploads/ — a web server has to decide what to do. If there’s an index file (like index.html) in that directory, it serves that. If there isn’t, and directory listing (sometimes called “directory indexing” or “autoindex”) is enabled, the server generates an HTML page listing every file and subdirectory it finds there.

Nginx’s directory listing feature is controlled by the autoindex directive, and it’s off by default in a stock Nginx install. So how do sites end up with it exposed? In my experience, it’s almost always one of these:

Why This Matters

Directory listing isn’t a vulnerability in the traditional sense — Nginx isn’t misbehaving, it’s doing exactly what it’s configured to do. But it’s an information disclosure risk that can snowball into something worse:

Step 1: Check If Directory Listing Is Currently Enabled

Before making changes, confirm what you’re dealing with. Visit a directory URL on your site directly (e.g., https://example.com/uploads/) in a browser. If you see a plain HTML page with a list of files and folders (usually with an “Index of /uploads” heading), directory listing is on.

You can also check your Nginx config directly:

sudo grep -r "autoindex" /etc/nginx/

This searches every config file under /etc/nginx/ for any autoindex directive, so you can see exactly where it’s enabled.

Step 2: Disable Directory Listing

The fix is almost always this simple. Find the location block (or server block) where autoindex is set, and either remove the line or explicitly set it to off:

location /uploads/ {
    autoindex off;
}

Since autoindex off; is Nginx’s default behavior, you don’t strictly need this line at all unless it’s overriding an autoindex on; set at a broader scope (like the server or http block) — in that case, explicitly setting it to off at the more specific location level is the correct way to override it.

Step 3: Provide a Proper Response Instead

Simply turning off autoindex means Nginx will now return a 403 Forbidden for directory requests that have no index file. That’s usually fine, but I like to be a bit more deliberate about it. Here are a few approaches I use depending on the situation:

Option A: Return a clean 403 with a custom error page

location /uploads/ {
    autoindex off;
}

error_page 403 /403.html;
location = /403.html {
    root /var/www/errors;
    internal;
}

Option B: Redirect directory requests to your homepage

If a bare directory URL genuinely shouldn’t be reachable at all, I sometimes just redirect it:

location /uploads/ {
    autoindex off;
    try_files $uri $uri/ =404;
}

This serves individual files that are directly requested by name (e.g., /uploads/photo123.jpg) but returns a plain 404 for the bare directory path itself, which reveals nothing about the directory’s contents.

Option C: Block the directory entirely if it should never be public

For directories that store things like backups, logs, or internal config — things that should never be reachable over HTTP at all — I go further and deny access outright:

location /backups/ {
    deny all;
    return 404;
}

Returning 404 instead of 403 here is a deliberate choice — a 403 confirms to an attacker that something exists at that path and is just forbidden, while a 404 gives away nothing.

Step 4: Check Every Level of Your Config

This is the step people skip, and it’s the one that actually matters most. autoindex on; set inside the http block in /etc/nginx/nginx.conf applies to every single server and location on that Nginx instance unless something more specific overrides it. I always check:

sudo cat /etc/nginx/nginx.conf | grep -A2 -B2 autoindex

and every included config file:

sudo find /etc/nginx -name "*.conf" -exec grep -l "autoindex" {} \;

If you find autoindex on; at the http level, I’d strongly recommend removing it there and only enabling it explicitly, narrowly, in specific location blocks where you actually want it (for example, an internal-only file archive behind authentication).

Step 5: Test and Reload

sudo nginx -t
sudo systemctl reload nginx

Then re-check the directory URL in a browser or with curl:

curl -I https://example.com/uploads/

You should now see a 403 or 404 status code instead of a 200 with an HTML file listing.

A Note on Legitimate Uses of Directory Listing

I don’t want to make it sound like autoindex on; is always wrong — there are legitimate cases, like an internal file server, a package mirror, or a public downloads page for open-source release artifacts. If you do want directory listing enabled somewhere, I’d combine it with:

location /internal-files/ {
    autoindex on;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
}
location /internal-files/ {
    autoindex on;
    allow 203.0.113.0/24;
    deny all;
}

Testing Your Setup

  1. sudo nginx -t to validate syntax
  2. Visit every directory-style URL on your site you can think of — /uploads/, /images/, /assets/, /backups/, /logs/ — and confirm none of them return a file listing
  3. Run a quick automated check with a tool like nikto or dirb against a staging copy of your site to catch anything you missed manually
  4. Check that legitimate files within those directories (ones meant to be directly linked) still load correctly — you’re disabling the listing, not necessarily access to individual files

Troubleshooting Common Issues

Still seeing a directory listing after adding autoindex off; — You likely have a more specific location block matching the same path with autoindex on; still set, and Nginx’s location matching rules mean the more specific block wins. Search your whole config tree, not just the file you edited.

Getting a 403 when you actually wanted an index page served — Make sure you have an actual index.html (or your app’s default document) in that directory, and that index is set correctly in the relevant location or server block.

Custom error page not showing — Confirm the root path in your error_page location block is correct and that the file actually exists there, and that you’ve included internal; so the error page itself isn’t directly browsable.

Security Considerations

Performance Tips

Directory listing itself isn’t really a performance concern, but a couple of related notes:

Real-World Use Case

Going back to that client I mentioned at the start — the fix took about five minutes once we found it: one stray autoindex on; in an old config block for their /uploads/ directory, left over from when someone debugged a file upload issue two years earlier. We turned it off, added try_files $uri $uri/ =404; so directly-linked files still worked, and audited the rest of their config for anything similar. The scariest part wasn’t the fix — it was realizing that misconfiguration had probably been live for two years before anyone noticed.

Best Practices Recap

Directory listing exposure is one of those issues that’s completely silent until someone finds it — there’s no error, no crash, nothing in your logs that screams “this is a problem.” It just sits there quietly disclosing information until either you catch it in an audit, or someone else finds it first. A five-minute config check is cheap insurance against that.

Exit mobile version