A single-file PHP file manager — hardened, self-hosted, and CDN-free.
Forked from jcampbell1/simple-file-manager with security hardening and features inspired by prasathmani/tinyfilemanager v2.6.
Maintained by Hoelee Enterprise — hoelee.com · WhatsApp +6012-797 2969
- Single PHP file (
index.php) — all frontend JS/CSS stored locally inassets/, no CDN requests - No external dependencies at runtime — works on air-gapped networks and Tor hidden services
- Hardened security — bcrypt auth, CSRF tokens, CSP headers, symlink protection, server-side upload validation
- Modern UI — Bootstrap 5, responsive, light/dark theme toggle
- Full file operations — upload, download, delete, rename, copy/move, mkdir, ZIP archive, in-browser file editor
- Multi-user support — per-user passwords, read-only accounts, per-user directory isolation
- Tor-friendly — no DNS lookups, no external fonts, no analytics, no CDN
- PHP 7.4+ (8.x recommended)
- PHP extensions:
fileinfo(required),zip(optional, for ZIP downloads) - Web server: Apache, Nginx, OpenLiteSpeed, or PHP built-in server
git clone https://github.com/hoelee/simple-file-manager.git
cd simple-file-managerOr just copy index.php + the assets/ folder to your webserver.
Before the app will run, you need at least one password hash. Generate one with PHP:
php -r "echo password_hash('your-strong-password', PASSWORD_DEFAULT), PHP_EOL;"Output looks like:
$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
Open index.php and edit the CONFIGURATION section at the top:
$app_title = 'My File Manager';
// Directory users can manage. Use __DIR__ for the project folder,
// or an absolute path like '/srv/uploads'.
$root_path = __DIR__;
$use_auth = true;
$auth_users = [
'admin' => '$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
];PHP built-in server (quick test):
php -S 0.0.0.0:8080 -t /path/to/simple-file-managerOpen http://localhost:8080 and log in.
Nginx:
server {
listen 80;
server_name files.example.com;
root /var/www/simple-file-manager;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Prevent direct execution of any PHP file except index.php
location ~ /assets/.*\.php$ {
deny all;
}
}Apache `.htaccess (place in the project root):
Options -Indexes
AllowOverride None
<FilesMatch "^(?!index\.php$).+\.php$">
Require all denied
</FilesMatch>Docker:
docker run -d \
--name file-manager \
-p 8080:80 \
-v /path/to/files:/var/www/html/data \
-v /path/to/simple-file-manager:/var/www/html/app \
php:8.2-apache
# Then visit http://localhost:8080/app/All settings are in the CONFIGURATION block at the top of index.php.
| Setting | Default | Description |
|---|---|---|
$use_auth |
true |
Enable login. App refuses to run without a valid password hash. |
$auth_users |
[] |
Map of 'username' => 'bcrypt-hash' |
$readonly_users |
[] |
Users who can browse/download but not modify |
$user_directories |
[] |
Per-user root directory, e.g. ['client-a' => '/srv/client-a'] |
$block_placeholder_credentials |
true |
Refuses to run if a placeholder hash is detected |
| Setting | Default | Description |
|---|---|---|
$allow_upload |
true |
File upload |
$allow_delete |
true |
Delete files/folders |
$allow_create_folder |
true |
Create new folders |
$allow_rename |
true |
Rename files/folders |
$allow_copy_move |
true |
Copy or move between folders |
$allow_edit |
true |
In-browser text file editor (≤2 MiB) |
$allow_archive_download |
true |
Download folder as ZIP |
$allow_direct_link |
false |
Direct file links vs. forced download |
| Setting | Default | Description |
|---|---|---|
$max_upload_size_bytes |
104857600 (100 MiB) |
Per-file upload limit (also configure PHP/nginx) |
$allowed_upload_extensions |
[] |
Allowlist, e.g. ['jpg','png','pdf']; empty = all non-blocked |
$blocked_upload_extensions |
[php, phtml, phar, ...] |
Blocklist of dangerous extensions |
$allowed_upload_mime_types |
[] |
MIME allowlist via finfo; empty = no check |
| Setting | Default | Description |
|---|---|---|
$ip_access_mode |
'off' |
'off', 'allow' (allowlist), or 'deny' (denylist) |
$ip_rules |
[] |
IPs or CIDRs, e.g. ['127.0.0.1', '192.168.1.0/24'] |
$require_https |
false |
Reject non-HTTPS requests |
| Setting | Default | Description |
|---|---|---|
$default_theme |
'light' |
'light' or 'dark' (user can toggle) |
$default_timezone |
'Asia/Kuala_Lumpur' |
PHP timezone |
$show_hidden_files |
false |
Show dotfiles in listing |
$debug |
false |
Show PHP errors (keep false in production) |
This app uses bcrypt via PHP's password_hash(). Never store plaintext passwords.
Command line:
php -r "echo password_hash('MySecretPass123!', PASSWORD_DEFAULT), PHP_EOL;"Interactive (prompts for password, hidden input):
php -r "echo password_hash(readline('Password: '), PASSWORD_DEFAULT), PHP_EOL;"PHP script (generate-hash.php):
<?php
echo "Enter password: ";
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
echo "\n" . password_hash($password, PASSWORD_DEFAULT) . "\n";$auth_users = [
'admin' => '$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
'viewer' => '$2y$10$X5zP2k3vQ8mF1bN7cR4dT6hJ9lE0wA3sB5nC8oY2uZ1xD4eG7hI3a',
];
$readonly_users = ['viewer']; // viewer can browse but not modify$auth_users = [
'admin' => '$2y$10$...hash1...',
'client-a' => '$2y$10$...hash2...',
'client-b' => '$2y$10$...hash3...',
];
$user_directories = [
'client-a' => '/srv/uploads/client-a',
'client-b' => '/srv/uploads/client-b',
];
$readonly_users = ['client-b']; // client-b can only download- Use a unique password per user
- Bcrypt cost factor is 10 by default — fine for most servers
- To rotate a password, just generate a new hash and replace the old one
- The app auto-detects placeholder text (
REPLACE_WITH) and refuses to start
Hosting on Tor Hidden Service
This file manager is ideal for Tor because it makes zero external requests — no CDN, no Google Fonts, no analytics, no DNS lookups. All assets are served from the local assets/ folder.
- A Tor relay or daemon installed on your server
- A web server (Nginx recommended) running on localhost
- PHP-FPM
Ubuntu/Debian:
sudo apt update && sudo apt install torSynology DSM (Docker):
# docker-compose.yml
services:
tor:
image: dperson/torproxy
container_name: tor
restart: unless-stopped
ports:
- "127.0.0.1:9050:9050" # SOCKS proxy
volumes:
- ./tor-data:/var/lib/torStep 2: Configure the hidden service
Edit /etc/tor/torrc (Linux) or your Tor config:
HiddenServiceDir /var/lib/tor/file-manager/
HiddenServicePort 80 127.0.0.1:8765Restart Tor:
sudo systemctl restart torGet your .onion address:
sudo cat /var/lib/tor/file-manager/hostname
# Output: abcdefghijklmnop234567.onionserver {
listen 127.0.0.1:8765;
server_name _;
root /var/www/simple-file-manager;
index index.php;
# No HTTPS needed — Tor provides transport encryption
# But enforce that requests come through Tor (localhost only)
allow 127.0.0.1;
deny all;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Block direct PHP execution in assets/
location ~ /assets/.*\.php$ {
deny all;
}
# Static asset caching
location ~* \.(js|css|png|jpg|gif|svg|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}In index.php:
$app_title = 'Secure Files';
// Tor provides end-to-end encryption, so HTTPS is not required
$require_https = false;
// Bind to the managed directory (NOT the project folder)
$root_path = '/srv/tor-files';
// Strong auth — mandatory for .onion services
$use_auth = true;
$auth_users = [
'admin' => '$2y$10$your-bcrypt-hash-here',
];
// Lock down uploads on a public .onion
$allow_upload = false; // or true if you trust your users
$allow_delete = true;
$allow_direct_link = false; // force downloads only
// Optional: restrict to known clients (Tor exit uses localhost)
$ip_access_mode = 'allow';
$ip_rules = ['127.0.0.1'];# Install Tor Browser, then visit:
http://abcdefghijklmnop234567.onion- No CDN/external requests (assets are local)
- No DNS lookups (
.onionresolves through Tor) - No analytics or third-party scripts
- Session cookies are
SameSite=Strict,HttpOnly - CSP prevents loading external resources
- Nginx listens on
127.0.0.1only (not0.0.0.0) - Strong bcrypt password configured
-
$allow_uploaddisabled or restricted -
$allow_direct_link = false(no hotlinking from clearnet) - Consider disabling
$allow_editif not needed - Set
$show_hidden_files = false - Regularly check Tor logs for relay health
If you allow uploads on the public web, it is only a matter of time before your server is hosting and serving illegal content. Prevent this with:
- Set
$allow_upload = false - Use a strong password (
$use_auth = true) - Restrict by IP (
$ip_access_mode = 'allow') - Use
.htaccess(Apache) orauth_basic(Nginx) - Only expose on a private network or Tor hidden service
| Feature | Status |
|---|---|
| bcrypt password hashing | ✅ password_hash() / password_verify() |
| CSRF protection | ✅ Session token + hash_equals() |
| Session cookie hardening | ✅ HttpOnly, SameSite=Strict, Secure |
| Content Security Policy | ✅ Nonce-based CSP header |
| Path traversal protection | ✅ realpath() + root containment check |
| Symlink protection | ✅ Symlinks hidden & excluded from all operations |
| Server-side upload size limit | ✅ Enforced before move_uploaded_file() |
| Upload extension/MIME validation | ✅ Blocklist + optional allowlists |
| IP allow/deny with CIDR | ✅ Configurable |
| HTTPS enforcement | ✅ Optional via $require_https |
| X-Frame-Options | ✅ DENY (clickjacking protection) |
| X-Content-Type-Options | ✅ nosniff |
| Referrer-Policy | ✅ same-origin |
| Brute-force delay | ✅ 500ms fixed delay on login attempt |
| No default credentials | ✅ Refuses to run without a real hash |
- Original: jcampbell1/simple-file-manager (MIT)
- Enhancements inspired by: prasathmani/tinyfilemanager v2.6
- Frontend: Bootstrap 5.3.3, jQuery 3.7.1, Font Awesome 4.7, Ace Editor, Highlight.js
MIT — see LICENSE
Hoelee Enterprise provides website design for SME, program & web app development, web hosting, and marketing.
- 🌐 hoelee.com — Portfolio
- 📱 WhatsApp: +6012-797 2969
- ✉️ Email: me@hoelee.com