A secure, lightweight, and modern single-file PHP directory indexer with glassmorphic UI, real-time search, atomic hash caching, bcrypt folder protection, strict Content Security Policy, and zero external dependencies.
Designed and maintained by
HARRY DERTIN SUTISNA ALSYUNDAWY (@alsyundawy)โ
Modern drop-in replacement for Apachemod_autoindexand Nginxautoindexwith zero database or package dependencies.๐ฆ
GitHub Releases (v4.1.0)ย |ย โกQuick Startย |ย ๐Security Architectureย |ย ๐Full Changelogย |ย ๐Support via PayPalย |ย ๐ฎ๐ฉQRIS Donation
File & Directory Browser is a security-hardened, high-performance, single-file PHP directory indexer and file explorer. Designed as a modern, elegant replacement for standard web server auto-indexing modules (such as Apache mod_autoindex or Nginx autoindex), it delivers a responsive web experience without requiring database servers, heavy backend frameworks, or NPM/Composer package dependencies.
Everything runs self-contained from a single index.php file, featuring instant client-side search filtering, deterministic multi-attribute sorting, on-demand checksum generation (CRC32, MD5, SHA-1) backed by an atomic local caching system, bcrypt folder protection, enterprise-grade defense-in-depth security headers (CSP nonce, HSTS, anti-indexing), and a Glassmorphic UI fully compliant with WCAG 2.2 AAA accessibility standards.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload on HTTPS connections to mandate end-to-end encrypted transport.bin2hex(random_bytes(16))) enforcing strict script-src and style-src policies without 'unsafe-inline'.realpath) preventing traversal attacks.X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy, and X-XSS-Protection: 0.X-Robots-Tag: noindex, nofollow, noarchive headers and <meta name="robots" content="noindex,nofollow"> tags across all pages to block crawler indexing.HttpOnly, SameSite=Strict, and Secure flags, complemented by session fixation defenses (session_regenerate_id(true))..cache/ folder hardening generating both .htaccess (Apache) and an empty index.html (Nginx, Caddy, Lighttpd) to deny unauthorized cache browsing.password_hash / password_verify) with zero plaintext storage.bin2hex(random_bytes(32)) CSRF tokens with post-login regeneration, plus configurable brute-force lockout rules ($loginMaxAttempts and $loginLockSeconds) with live countdown timers..env, .php, .git, .htaccess, .sql, etc.) are excluded from directory listings and checksum checks by default.mtime), and schema version to eliminate redundant I/O operations. Cache writes use atomic temporary file creation and POSIX rename(2) syscalls.strnatcasecmp) in directory sorting to guarantee consistent row order when timestamps or file sizes are identical.<button type="button" class="sort-btn"> controls with dynamic aria-sort indicators and nonced JavaScript navigation./ or Ctrl+K (Cmd+K on macOS) to instantly focus the search bar; press Escape to clear search filters and dismiss.fa-folder-open), clean URL structure (/?berkas=folder/subfolder), and aria-current="page" semantics.viewport-fit=cover, min-height: 100svh (with 100vh fallback) to eliminate mobile URL bar jumping, and env(safe-area-inset-bottom) padding for bezel-less screens (iPhone, iPad, Android).<section class="table-wrap" aria-label="File listing"> landmark, native scope="col" on headers, high-visibility :focus-visible keyboard rings, and semantic <output class="empty-state"> live regions for screen readers.@media (prefers-reduced-motion: reduce) system accessibility preferences.phpcs --standard=PSR12).| Requirement | Minimum Version | Recommended | Notes |
|---|---|---|---|
| PHP | 8.0 |
8.2 โ 8.4+ |
Full compatibility across PHP 8.0, 8.1, 8.2, 8.3, and 8.4+ |
| PHP Extensions | session, hash, json, pcre, spl |
Standard | All are standard built-in PHP core extensions |
| Web Server | Any standard web server | Apache 2.4+ / Nginx 1.20+ | Works with Apache, Nginx, Caddy, Lighttpd, or PHP CLI built-in server |
| HTTPS / SSL | Strongly Recommended | Letโs Encrypt / TLS 1.3 | Required for secure cookies, HSTS enforcement, and CSRF protection |
| Database | None | None | Zero database required โ operates completely file-based |
Download: Grab the latest index.php from the Releases page or clone the repository:
git clone https://github.com/alsyundawy/File-Directory-Browser.git
index.php into the directory on your web server that you want to browse and share.index.php in any text editor to customize optional settings (page title, password-protected folders, date format, etc.).http://localhost/files/ or https://yourdomain.com/).Below are production-ready installation guides for Ubuntu (20.04 / 22.04 / 24.04 LTS) and Debian (11 / 12) using either Apache or Nginx + PHP-FPM.
Update your system package repositories and deploy the project files to your target web directory:
# Update package repositories
sudo apt update && sudo apt upgrade -y
# Install Git and essential utilities
sudo apt install -y git unzip curl
# Create target web root directory
sudo mkdir -p /var/www/file-browser
# Clone repository into web directory
sudo git clone https://github.com/alsyundawy/File-Directory-Browser.git /var/www/file-browser
# Set ownership to the web server user (www-data)
sudo chown -R www-data:www-data /var/www/file-browser
sudo chmod -R 755 /var/www/file-browser
# Grant write permissions for the .cache directory (required for hash caching)
sudo chmod -R 775 /var/www/file-browser
If you are using the Apache HTTP Server:
# Install Apache and PHP runtime with required extensions
sudo apt install -y apache2 php php-cli libapache2-mod-php php-json php-mbstring
# Enable required Apache modules (rewrite & headers)
sudo a2enmod rewrite headers
Create a new Virtual Host configuration:
sudo nano /etc/apache2/sites-available/file-browser.conf
Add the following configuration (replace files.example.com with your domain or server IP):
<VirtualHost *:80>
ServerName files.example.com
ServerAdmin webmaster@example.com
DocumentRoot /var/www/file-browser
<Directory /var/www/file-browser>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Block direct access to hidden files and directories (.env, .git, etc.)
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Logging
ErrorLog ${APACHE_LOG_DIR}/file-browser_error.log
CustomLog ${APACHE_LOG_DIR}/file-browser_access.log combined
</VirtualHost>
# Enable the Virtual Host
sudo a2ensite file-browser.conf
# Optional: Disable the default Apache welcome page
sudo a2dissite 000-default.conf
# Verify Apache configuration syntax
sudo apache2ctl configtest
# Restart Apache service
sudo systemctl restart apache2
If you are using Nginx for lightweight, high-performance web delivery:
# Install Nginx and PHP-FPM with essential extensions
sudo apt install -y nginx php-fpm php-cli php-json php-mbstring
[!NOTE] Check your installed PHP-FPM socket version (e.g.
/run/php/php8.3-fpm.sockor/run/php/php8.2-fpm.sock) by running:ls -la /run/php/php*-fpm.sock.
Create a new Nginx server configuration:
sudo nano /etc/nginx/sites-available/file-browser
Add the following configuration (adjust server_name and the fastcgi_pass socket path to match your environment):
server {
listen 80;
listen [::]:80;
server_name files.example.com;
root /var/www/file-browser;
index index.php;
charset utf-8;
client_max_body_size 100M;
# Primary routing: route through index.php if not a direct static file
location / {
try_files $uri $uri/ /index.php?$args;
}
# Pass PHP scripts to FastCGI server (PHP-FPM)
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Adjust PHP-FPM socket path to match your installed PHP version:
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
# For PHP 8.2 use: fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny direct access to .cache directory and hidden dotfiles
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Deny direct access to sensitive file extensions
location ~* \.(env|git|sql|htaccess|htpasswd)$ {
deny all;
return 404;
}
# Logging
error_log /var/log/nginx/file-browser_error.log;
access_log /var/log/nginx/file-browser_access.log;
}
# Create symbolic link to enable site
sudo ln -s /etc/nginx/sites-available/file-browser /etc/nginx/sites-enabled/
# Optional: Remove default Nginx welcome site
sudo rm -f /etc/nginx/sites-enabled/default
# Verify Nginx configuration syntax
sudo nginx -t
# Restart Nginx and PHP-FPM services
sudo systemctl restart nginx
sudo systemctl restart php*-fpm
To protect session cookies, authentication tokens, and enable automatic HSTS enforcement in transit, secure your installation with automated SSL certificates via Certbot:
# For Apache:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d files.example.com
# For Nginx:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d files.example.com
Certbot will automatically install the certificate, configure HTTPS redirects, and establish an automated background renewal timer.
Open index.php in any text editor to adjust the configuration parameters located at the top of the file:
// =================== GENERAL SETTINGS ===================
$browseDirectories = true; // Allow browsing sub-directories
$title = 'Index of '; // Page title format ( is dynamic)
$subtitle = ' files, total'; // Subtitle format
$showParent = true; // Show parent directory link (..)
$showDirectories = true; // Display directories in file table
$showDirectoriesFirst = true; // Group directories at the top of listing
$showHiddenFiles = false; // Hide/show dotfiles (.example)
$alignment = 'left'; // Text alignment ('left' or 'center')
$showIcons = true; // Display Font Awesome file/folder icons
$dateFormat = 'd-M-Y H:i'; // Date format for file modified time
$sizeDecimals = 1; // Decimal places for file sizes
$browseDefault = ''; // Default folder path on initial load
$allowExternalSymlinks = false; // Allow symlinks outside base directory
$enableHashCache = true; // Enable local cache for file checksums
$hashCacheVersion = '2026-07-08-v2'; // Internal cache schema version
$timezone = 'Asia/Jakarta'; // Default application timezone
To password-protect specific folders, generate a bcrypt hash and map folder names to their hashes in $protectedFolders:
# Generate a secure bcrypt hash via your terminal:
php -r "echo password_hash('your_secret_password', PASSWORD_BCRYPT);"
Then configure the array in index.php:
// =================== FOLDER PASSWORD PROTECTION ===================
$protectedFolders = [
'secret-docs' => '$2y$12$YourGeneratedBcryptHashHere...',
'private' => '$2y$12$AnotherGeneratedBcryptHashHere...',
];
$passwordSessionLifetime = 2400; // Login session validity in seconds (default: 40 minutes)
$loginMaxAttempts = 5; // Max failed login attempts before lockout
$loginLockSeconds = 300; // Lockout duration in seconds (5 minutes)
[!IMPORTANT] Never store plaintext passwords. Always use a bcrypt hash generated via
password_hash($password, PASSWORD_BCRYPT).
| Shortcut | Action | Description |
|---|---|---|
/ or Ctrl + K (Cmd + K on macOS) |
Focus Search | Instantly highlights and focuses the search input bar from anywhere on the page |
Escape |
Clear & Dismiss | Clears active search filters, restores the full listing, and removes input focus |
The application enforces a defense-in-depth security model across every layer:
realpath() boundary validation and recursive directory traversal checks to reject any directory escape attempts.X-Robots-Tag and meta robots tags prevent search engine indexing of private directory hierarchies.HttpOnly, SameSite=Strict, and Secure attributes with session fixation defenses.password_verify() against bcrypt hashes.bin2hex(random_bytes(32)) CSRF tokens and brute-force attempt lockout timers..htaccess and index.html generation within .cache/ blocks directory listings across Apache, Nginx, Caddy, and Lighttpd.The codebase adheres to rigorous static analysis standards with zero tolerated warnings:
session.upload_progress.enabled at session startup (ini_set('session.upload_progress.enabled', '0')) and enforced strict <8KB Content-Length validation on POST authentication requests, neutralizing memory corruption and Use-After-Free attack vectors.isHttpsRequest() to inspect $_SERVER['SERVER_PORT'] === 443, guaranteeing accurate TLS detection and HSTS enforcement across reverse proxy setups.Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Resource-Policy: same-origin headers to sendSecurityHeaders() for robust browser-level process isolation against cross-origin data leakage and Spectre-class attacks.getBaseWebPath() helper to dynamically compute base URL directory path across root and subdirectory deployments.$downloadUrl), breadcrumb โHomeโ link, password prompt return link, parent directory return link, and post-authentication 303 redirect to strictly respect subfolder installations rather than forcing redirects to domain root.env(safe-area-inset-top/bottom/left/right)) on body and .container, eliminating content clipping behind Xiaomi/Redmi/POCO punch-hole cameras, notches, and navigation gesture bars in portrait and landscape modes.-webkit-text-size-adjust: 100% on html to eliminate MIUI/Chrome Mobile automatic font boost, and locked form input font-sizes to 16px to prevent viewport auto-zooming on mobile browsers.-webkit-overflow-scrolling: touch) and container max-width scaling for 2K/4K displays.<meta name="keywords">, dynamic <meta name="description">, <meta name="color-scheme" content="dark">, <meta name="theme-color">, Open Graph tags (og:type, og:site_name, og:title, og:description, og:url), and Twitter Cards.WebPage structured data with CSP nonce support for answer engines and search indexing.<main id="main"> landmark.min-height: 32px and comfortable touch padding exceeding WCAG 2.2 SC 2.5.8 requirements.isDisplayableFile() by converting the dangerous extension list into an internal static hash lookup table via array_fill_keys(), replacing linear in_array() array scanning.date_default_timezone_set() in a try-catch block with fallback to 'UTC' to eliminate unhandled ValueError fatal crashes if an unrecognized timezone string is configured.session.gc_maxlifetime to align with $passwordSessionLifetime to prevent premature session eviction by the PHP daemon.$alignment against strict allowlist (['left', 'center', 'right']) before injecting into the table stylesheet block.APP_VERSION to '4.1.0'.index-v4.1.php release snapshot.Strict-Transport-Security: max-age=31536000; includeSubDomains; preload header in sendSecurityHeaders(), emitted automatically on HTTPS connections.$_SERVER['HTTPS'] and port 443 to ensure HSTS is enforced only over TLS.viewport-fit=cover across Directory Listing, Password Login, and Hash Check pages.min-height: 100svh with min-height: 100vh fallback across all card containers and body wrapper, eliminating browser URL bar jumping on iOS Safari & mobile Chrome.padding-bottom: max(1.5rem, env(safe-area-inset-bottom)) to ensure seamless footer rendering above home indicators on bezel-less mobile screens (iPhone, iPad, Android).<div class="table-wrap"> with <section class="table-wrap" aria-label="File listing"> without tabindex, eliminating IDE accessibility warnings while preserving smooth native overflow scrolling.<a> tags to accessible <button type="button" class="sort-btn" data-href="..." aria-label="..."> handled via nonce JavaScript navigation, preventing empty or dead link interactions.APP_VERSION to '4.0.0'.$baseDir cannot be enumerated when $allowExternalSymlinks is false.index.html in .cache/ directory alongside .htaccess to prevent directory listing on non-Apache web servers (Nginx, Caddy, Lighttpd, PHP CLI server).X-Robots-Tag: noindex, nofollow, noarchive in sendSecurityHeaders() and <meta name="robots" content="noindex,nofollow"> on password & hash check pages for unified search privacy.parse_url() return value before accessing $parsedUrl['path'], preventing PHP 8 โTrying to access array offset on falseโ warning on malformed request URIs.\r and \n in the URL normalizer redirector to prevent potential CRLF header injection.#noResultRow remained invisible when 0 files matched due to inline style conflict with .hidden-row stylesheet rule; switched to classList.toggle('hidden-row').#emptyRow) gracefully during active search filtering.encodeRelativePath() to parent directory link in table body..footer color #94a3b8), achieving a contrast ratio of > 7.5:1 and passing WCAG AAA compliance.aria-sort: Added table column scope="col" and dynamic aria-sort attributes (ascending/descending/none) to header <th> elements via a dedicated helper function.aria-current="page" to the active breadcrumb path segment for improved screen reader navigation.:focus-visible outlines for links, buttons, and form inputs for seamless keyboard navigation.<output>: Replaced role="status" on <tr> with semantic <output class="empty-state"> for universal assistive device support.@media (prefers-reduced-motion: reduce) CSS rules to honor user motion preferences./ and Ctrl+K (Cmd+K on macOS) search shortcut to instantly focus the input, and Escape to clear search criteria and blur..hash-table th elements and added fallback window.location.href to the back button on the hash verification page when browser history is empty.strnatcasecmp) in usort() for deterministic ordering when date or size attributes match.getSortAriaAttribute() helper function.phpcs --standard=PSR12.APP_VERSION = '3.9.0' constant displayed in the application footer.bin2hex(random_bytes(32)) instead of non-cryptographic time-based hashing (uniqid + microtime). Added explicit DevSkim ignore annotations (DS197836, DS126858) for intentional file checksum features (md5, sha1), query string parameters, and filesystem cache keys.javascript:history.back() URI from hash page back-link; replaced with a proper <button id="backBtn"> handled via nonce script block to fully comply with strict CSP script-src policy.style="display:none" from #noResultRow element; moved to CSS class .hidden-row to comply with strict CSP style-src policy.nonce attribute to <noscript><style> blocks on all pages for consistent CSP compliance across all rendering paths.getSafeHost() is now validated to be within valid TCP range (1โ65535) to prevent malformed Host header injection via out-of-range port values.<td> elements while file rows had 4. Unified date display so both dir and file rows use a single <td class="date-cell"> containing both primary and secondary spans inside, matching thead column count of 4.sanitizePath() preg_replace with /u modifier now has explicit fallback if the regex fails due to invalid UTF-8 input, preventing silent null return.ensureCacheDir() now checks mkdir() return value and logs error on failure instead of silently continuing.calculateHashes() now calls error_log() when fopen() fails, improving production debuggability.writeHashCache() now verifies return value of rename() and logs on failure, ensuring temp file cleanup even on rename failure.humanizeFilesize() now uses number_format() instead of round() to ensure consistent decimal display (e.g., โ1.0 MBโ not โ1 MBโ).humanizeFilesize() caches count($units) before the loop to avoid repeated function calls on every iteration.$_GET['sort'] ?? 'name' and $_GET['order'] ?? 'asc' with explicit null coalescing before allowlist check for strict_types safety.phpcbf and manual formatting fixes across all PHP files to resolve all syntax, indentation, and spacing errors (0 PHPCS errors remaining).getMediaIconClass(), createHashCacheDir(), readHashCache(), and listDirectory() to reduce multiple return statements (max 1 per function).isValidHashData(), processDirectoryItem(), and getScandirFiles() helper functions.foreach($requiredExtensions) was placed after an exit() block.e() escaping on $lockedFolder in the renderPasswordPage() hidden input and all HTML attributes.queryUrl() Empty String: queryUrl() now returns '' instead of '?' when params are empty.calculateHashes() fread Error: Correctly short-circuits on fread() === false before calling hash_update().isValidHashData() Hex Length Validation: Strictly validates hex string length per algorithm (crc32=8, md5=32, sha1=40).ensureCacheDir() Path Sanitization: Sanitizes $hashCacheVersion before using it as a filesystem path component.@ error suppression on file I/O functions and replaced with explicit return-value checks.style attribute on hash page container; removed inline onsubmit handler from search form.session_regenerate_id(true) after successful folder password verification.X-XSS-Protection: 0 Header: Added X-XSS-Protection: 0 header to disable legacy browser XSS auditor.strtolower mapping in isHiddenName() and pre-computed unit count in humanizeFilesize().fa-folder-open in breadcrumb navigation.%2F).$loginMaxAttempts = 5 and $loginLockSeconds = 300 lockout timer.folder to berkas, automatic HTTP 301 redirects from /index.php to clean root, and decoded / slashes.<noscript> style blocks, and eliminated 'unsafe-inline'.password_hash(PASSWORD_BCRYPT) and constant-time password_verify().[!IMPORTANT]
- Cache Folder Permissions: Ensure that the directory containing the script has write permissions so it can create the
.cachefolder automatically. If write permissions are unavailable, file checksum caching is safely bypassed to guarantee uninterrupted runtime.- HTTPS/SSL Deployment: It is strongly recommended to host this script under an SSL/HTTPS domain to guarantee encryption of CSRF session cookies, authentication tokens, and automatic HSTS header activation.
- Exclusion of Sensitive Files: Critical file extensions including
.php,.env,.sql,.htaccess,.git, and others are blocked by default from being listed, viewed, or hashed to prevent unauthorized code execution and credential leaks.- Folder Passwords (Must Be Hashed): Folder protection passwords must not be stored in plaintext. Always use the output of
password_hash('your_password', PASSWORD_BCRYPT). Generate a hash with:php -r "echo password_hash('your_password', PASSWORD_BCRYPT);"- CSP & Nonce Enforcement: This script enforces a strict nonce-based Content-Security-Policy. Inline
onclick=""or<script>elements without the dynamic nonce will be blocked by the browser.- Multi-Webserver Cache Protection: The
.cache/folder automatically generates both.htaccess(Apache) and an emptyindex.htmlfile to prevent unauthorized directory listings across Apache, Nginx, Caddy, Lighttpd, and PHP CLI server.- Search Keyboard Shortcuts: Press
/orCtrl+K(Cmd+Kon macOS) anywhere on the page to focus the search input, and pressEscapeto clear search filters and dismiss the input field.- Accessibility Compliance (WCAG 2.2 AAA): The interface strictly adheres to modern accessibility standards featuring high contrast ratios (> 7.5:1), keyboard navigation with
:focus-visible, breadcrumbaria-current="page", table column headeraria-sort, semantic<output class="empty-state">live regions, andprefers-reduced-motionsupport.
File-Directory-Browser/
โโโ LICENSE # MIT License
โโโ README.md # Project overview, installation, and documentation
โโโ assets/ # Repository banners, media, and visual assets
โ โโโ alsyundawy-banner.png # Alsyundawy IT Solution maintainer banner
โ โโโ file-directory-browser-banner.jpg # Production flyer & banner
โโโ index.php # Complete single-file directory indexer & file browser
Contributions, issues, and feature requests are welcome:
https://github.com/alsyundawy/File-Directory-Browser/fork).git checkout -b feature/amazing-feature).git commit -m 'feat: add amazing feature').git push origin feature/amazing-feature).If these scripts are helpful for your setup, you can support development here:
https://www.paypal.me/alsyundawyScan the QRIS barcode below using any Indonesian mobile banking app (BCA, Mandiri, BRI, BNI, BSI, CIMB Niaga, Permata) or e-wallet (GoPay, OVO, DANA, LinkAja, ShopeePay):
ID1020021153676https://github.com/user-attachments/assets/a0126f28-6dde-43da-ba14-d7c9a27de0df+62 856-8515-212This project is licensed under the MIT License โ see the LICENSE file for details.
Copyright (c) 2026 Harry Dertin Sutisna Alsyundawy (alsyundawy).
Note: Please retain attribution credit to the original author (HARRY DS ALSYUNDAWY โ ALSYUNDAWY IT SOLUTION) if you use or distribute this script. Attribution is appreciated though not legally mandated under the MIT License.