chmod for Web Server Directories: Secure Permission Patterns
Why Web Server Permissions Matter
Misconfigured web server permissions are a leading cause of security breaches. If a web root is world-writable, an attacker who finds any code execution vulnerability can modify your site, plant malware, or steal data. If permissions are too restrictive, the server cannot serve pages at all.
The goal: set permissions that are restrictive enough to prevent unauthorized writes, but permissive enough for the server to function normally.
The Standard Web Server Permission Model
Web servers like Apache and Nginx run as a specific user (often www-data on Debian/Ubuntu or nginx on RHEL/CentOS). Your files need to be readable by that user — but not writable unless there is a specific reason.
Directory Structure
/var/www/
├── html/ # Document root (public files)
│ ├── index.html
│ ├── css/
│ └── js/
├── uploads/ # User-uploaded files
├── logs/ # Server logs
└── config/ # Configuration files with secrets
Recommended Permissions
| Path | Permissions | Mode | Owner | Rationale |
|---|---|---|---|---|
/var/www/html | rwxr-xr-x | 755 | root:www-data | Owner manages, server reads |
/var/www/html/* | rw-r--r-- | 644 | root:www-data | Static files, server reads only |
/var/www/uploads | rwxrwx--- | 770 | root:www-data | Server can write, others cannot |
/var/www/config | rwx------ | 700 | root:root | Only root can access |
/var/www/config/*.php | rw------- | 600 | root:root | Secrets — no group or others |
/var/www/logs | rwxr-x--- | 750 | root:www-data | Server writes, group reads |
Applying Permissions
Set Up a New Web Root
# Create directory structure
sudo mkdir -p /var/www/html /var/www/uploads /var/www/logs /var/www/config
# Set ownership
sudo chown -R root:www-data /var/www
# Set directory permissions
sudo find /var/www -type d -exec chmod 755 {} +
# Set file permissions
sudo find /var/www -type f -exec chmod 644 {} +
# Tighten config directory
sudo chmod 700 /var/www/config
sudo find /var/www/config -type f -exec chmod 600 {} +
# Open uploads directory for server writes
sudo chmod 770 /var/www/uploads
Using ACLs for Granular Control
When multiple developers need different access levels, POSIX ACLs provide finer control than traditional owner/group/others:
# Give developer "alice" read/write access to the web root
sudo setfacl -R -m u:alice:rwx /var/www/html
# Ensure new files inherit the ACL
sudo setfacl -R -d -m u:alice:rwx /var/www/html
Upload Directories: The Danger Zone
Upload directories are the highest-risk part of any web server because they allow external input to become files on your system.
Critical Rules
- Never allow execution in upload directories. If using Apache, add an
.htaccess:
# Disable script execution in uploads
<FilesMatch "\.ph(p[2-7]?|t|tml)$">
Require all denied
</FilesMatch>
- Separate uploads from the document root. Serve uploads through a script or a separate virtual host, not directly from
/var/www/html/uploads. - Limit file sizes at the server level (Nginx:
client_max_body_size, Apache:LimitRequestBody). - Validate file types server-side — never trust file extensions from the client.
Nginx: Deny PHP Execution in Uploads
location /uploads/ {
location ~ \.php$ {
deny all;
}
}
Common Mistakes
Mistake 1: chmod -R 777 /var/www
This makes every file writable by every user on the system. If any service is compromised, the attacker can modify your website at will.
Fix: Use 755 for directories and 644 for files, always.
Mistake 2: Running the Server as root
If the webserver process runs as root, every vulnerability gives the attacker root access.
Fix: The server should run as an unprivileged user (www-data, nginx, etc.).
Mistake 3: Config Files World-Readable
Database passwords, API keys, and secrets in PHP/Python config files must not be readable by "others."
Fix: Mode 600, owned by root. The server reads them via group or ACL if needed.
Mistake 4: Uploads in the Document Root Without Execution Block
Even if you validate file types, an attacker might upload a .php file disguised as an image.
Fix: Block script execution in upload directories at the server level.
Permission Verification Script
#!/bin/bash
echo "=== Web Server Permission Audit ==="
echo "Document root:"
find /var/www/html -type d ! -perm 755 -ls 2>/dev/null
find /var/www/html -type f ! -perm 644 -ls 2>/dev/null
echo "Upload directory:"
stat -c "%a %n" /var/www/uploads
echo "Config directory:"
stat -c "%a %n" /var/www/config
find /var/www/config -type f ! -perm 600 -ls 2>/dev/null
echo "World-writable files:"
find /var/www -xdev -type f -perm -o+w -ls 2>/dev/null
Run this periodically or in CI to catch permission drift.
Key Takeaways
- Directories: 755, static files: 644, config files: 600 — these are your defaults
- Never use 777 in production; it is always wrong
- Separate upload directories from the document root and block script execution
- Run the web server as an unprivileged user, never as root
- Use ACLs when traditional permissions are not flexible enough
- Audit permissions regularly with a verification script
Try It Yourself
Visualize permission settings for any web server path using our chmod Calculator. Toggle bits for owner, group, and others, then copy the exact chmod command for your setup.
Related Guides
- chmod Guide — complete chmod syntax and examples
- Unix Permissions Reference — special bits, ACLs, and advanced patterns