Permission wp folder
Use cases: securing /var/www, permission for WordPress folders.
What You Will Learn
- Understand the difference between **permissions and **ownership in a WordPress context.
- Map **recommended permissions per WordPress folder (
wp-admin,wp-includes,wp-content,uploads,plugins,themes, root files). - Select the right **ownership model (
www-data, deploy user + group, panel users) and when to use each. - Apply safe, idempotent **hardening steps for
/var/wwwafter install/migration. - Use
chmod,chown,chgrp, and (optionally)setfaclto implement secure collaboration. - Read and decode numeric (octal) vs symbolic permissions and the **setgid bit for shared dirs.
- Diagnose **permission denied and **cannot write to directory with a quick troubleshooting matrix.
- Execute a **quick lab to harden a site and verify results with expected outputs.
Prerequisites
- **Access Level:
sudo(or root) required to change ownership in/var/www. - **Software: SSH client (Terminal, PuTTY).
- **Knowledge: Basic Linux navigation (
ls,cd), readingls -loutput, understanding users/groups.
5W + 1H Framework
| Question | Answer |
|---|---|
| What | Secure, workable permissions/ownership for WordPress, focusing on /var/www. |
| Why | Prevent compromise while allowing WordPress, PHP, and admins to read/write where necessary (updates, uploads, cache). |
| When | Immediately post-install, post-migration, after restoring backups, or when fixing upload/update errors. |
| Where | Typical root: /var/www/html/yourdomain/ (or panel-specific roots). |
| Who | System admins, developers, DevOps, site owners maintaining VPS WordPress. |
| How | Apply folder/file baselines (755/644), lock sensitive files (600), assign correct owner (www-data) and collaboration groups; use setfacl/setgid when needed. |
Permission Codes Reference
| Code | Symbolic | Owner | Group | Others | Typical WordPress Use | Explanation |
|---|---|---|---|---|---|---|
| 600 | rw------- | rw | — | — | wp-config.php | Only owner can read/write (DB creds). |
| 640 | rw-r----- | rw | r | — | wp-config.php when group read is required | Owner controls; group can read. |
| 644 | rw-r--r-- | rw | r | r | Most PHP/JS/CSS files | Default safe for files. |
| 750 | rwxr-x--- | rwx | r-x | — | Private admin dirs/scripts | Group read/exec; others no access. |
| 755 | rwxr-xr-x | rwx | r-x | r-x | Directories (wp-content, wp-admin, etc.) | Enter/browse dirs but no public write. |
| 775 | rwxrwxr-x | rwx | rwx | r-x | Shared dev dirs (with setgid) | Owner+group write; others read/exec. |
| 777 | rwxrwxrwx | rwx | rwx | rwx | Avoid | World-writable (security risk). |
Command Variations & Flags
| Command | Option | Meaning | Example |
|---|---|---|---|
chmod | Numeric | Set exact mode via octal | chmod 644 wp-config.php |
chmod | Symbolic | Incremental changes | chmod g+w wp-content/uploads/ |
chmod | -R | Recurse into subpaths | chmod -R 755 wp-content/ |
chown | user:group | Set owner and group | chown www-data:www-data index.php |
chown | -R | Recursive ownership | chown -R www-data:www-data /var/www/html |
chgrp | group only | Change group | chgrp developers wp-content/ |
Structure of Symbolic Permissions
[WHO][OPERATOR][PERMISSION]
- **WHO:
u(owner),g(group),o(others),a(all) - **OPERATOR:
+add, remove,=set exactly - **PERMISSION:
rread,wwrite,xexecute
Examples of Symbolic Notation
| Command | Meaning | Example Use Case |
|---|---|---|
chmod g+w wp-content/uploads/ | Add write to group | Team uploads via shared group |
chmod o-r wp-config.php | Remove others’ read | Hide DB creds from world |
chmod a+X wp-content/ | Add execute on dirs | Ensure all dirs are traversable |
chmod u=rw,g=r,o= index.php | Explicit mode | Owner edit; group read; others none |
Detailed Breakdown of g+w (Example)
chmod g+w wp-content/uploads/
Before:
drwxr-xr-x 5 www-data www-data 4096 Sep 23 uploads
After:
drwxrwxr-x 5 www-data www-data 4096 Sep 23 uploads
**Meaning: Group gains write on uploads/ (useful when group is your dev or webserver group).
WordPress Folder Map & Baselines
| Path | Type | Recommended Mode | Owner:Group (common) | Notes |
|---|---|---|---|---|
/var/www/html/ | Site root dir | 755 | www-data:www-data | Web server can traverse; not writable by public. |
wp-admin/ | Dir | 755 | www-data:www-data | No writes needed at runtime. |
wp-includes/ | Dir | 755 | www-data:www-data | No writes needed at runtime. |
wp-content/ | Dir | 755 | www-data:www-data | Writable subpaths only (uploads, cache). |
wp-content/plugins/ | Dir | 755 | www-data:www-data | Write only during updates/installs. |
wp-content/themes/ | Dir | 755 | www-data:www-data | Write during updates; otherwise read-only. |
wp-content/uploads/ | Dir | 755 (or 775 w/ group) | www-data:www-data (or ubuntu:www-data) | Must be writable by PHP (uploads). Consider 775 + setgid for teams. |
wp-content/cache/ | Dir | 755 (or 775) | As above | Must be writable by PHP. |
wp-config.php | File | 600 (or 640) | www-data:www-data (or owner) | Lock down credentials. |
index.php, core files | Files | 644 | www-data:www-data | Default for files. |
Ownership Models (Choose One)
Single-Owner (Web Server Owns Files)
- **Owner:Group:
www-data:www-data - **Pros: Simple; updates/uploads just work.
- **Cons: If web server is compromised, attacker owns files.
- Apply:
sudo chown -R www-data:www-data /var/www/html/
# Expected (sample):
# drwxr-xr-x www-data www-data /var/www/html
Two-User Collaboration (Deploy User + Web Server Group)
- **Owner:Group:
ubuntu:www-data(owner is deploy/admin, group is webserver) - **Dirs needing runtime write (uploads/cache):
775with **setgid so new files inherit the group. - **Pros: Separation of duties; easier audits.
- **Cons: Need discipline on group membership & umask.
- Apply:
sudo chown -R ubuntu:www-data /var/www/html/
sudo find /var/www/html/ -type d -exec chmod 775 {} ;
sudo find /var/www/html/ -type f -exec chmod 664 {} ;
sudo chmod g+s /var/www/html/wp-content/uploads
# Expected:
# uploads shows 's' on group perms: drwxrwsr-x
Panel-Managed Users (e.g., per-site system user)
- **Owner:Group:
siteuser:siteuser - **Web Server Access: via service integration (e.g., PHP-FPM pool running as
siteuser). - **Pros: Good isolation per site.
- **Cons: Varies by panel; follow panels convention.
Hardening /var/www (Baseline Steps + Outputs)
Replace /var/www/html with your actual docroot.
Set directory permissions:
find /var/www/html -type d -exec chmod 755 {} ;
# Expected (sample):
# drwxr-xr-x ... /var/www/html/wp-admin
# drwxr-xr-x ... /var/www/html/wp-includes
# drwxr-xr-x ... /var/www/html/wp-content
Set file permissions:
find /var/www/html -type f -exec chmod 644 {} ;
# Expected (sample):
# -rw-r--r-- ... /var/www/html/index.php
# -rw-r--r-- ... /var/www/html/wp-settings.php
Lock config file:
chmod 600 /var/www/html/wp-config.php
ls -l /var/www/html/wp-config.php
# Expected:
# -rw------- 1 www-data www-data ... wp-config.php
Fix ownership (single-owner model):
chown -R www-data:www-data /var/www/html
# Expected (sample):
# drwxr-xr-x www-data www-data /var/www/html
Ensure uploads/cache writable (two-user model optional):
chown -R ubuntu:www-data /var/www/html/wp-content/{uploads,cache
chmod -R 775 /var/www/html/wp-content/{uploads,cache
chmod g+s /var/www/html/wp-content/{uploads,cache
# Expected:
# drwxrwsr-x ubuntu www-data ... /uploads
# drwxrwsr-x ubuntu www-data ... /cache
Advanced Collaboration (Optional ACL)
Use **ACL to grant granular write only where needed without changing global modes.
# Give www-data write access to uploads and cache:
sudo setfacl -R -m u:www-data:rwx /var/www/html/wp-content/uploads
sudo setfacl -R -m u:www-data:rwx /var/www/html/wp-content/cache
# Ensure new files inherit ACLs:
sudo setfacl -dR -m u:www-data:rwx /var/www/html/wp-content/uploads
sudo setfacl -dR -m u:www-data:rwx /var/www/html/wp-content/cache
# Verify:
getfacl /var/www/html/wp-content/uploads | head
# Expected includes "user:www-data:rwx" and default entries
Special Cases & Notes
- **Plugin/Theme Updates via WP Admin: Temporarily require write on plugin/theme dirs. If you deploy via git/CI, keep them read-only and run updates in pipeline instead.
- **
wp-content/uploads& cache: Must be writable by the PHP user (oftenwww-data). - **Multisite: Same baseline; watch per-site upload subfolders.
- **PHP-FPM Pools: If pool runs as
siteuser, ownership should match that user (notwww-data). - **Never use
777in production; fix underlying ownership instead.
Troubleshooting Matrix
| Symptom | Likely Cause | Verify | Fix |
|---|---|---|---|
| “Unable to create directory … uploads” | uploads/ not writable by PHP user | ls -ld wp-content/uploads | chown -R www-data:www-data wp-content/uploads && chmod -R 755 wp-content/uploads (or 775 with group) |
| “Could not create directory … plugin/theme update” | No write on plugins/ or themes/ | ls -ld wp-content/plugins | Temporarily chown -R www-data:www-data wp-content/plugins and chmod -R 755 (or CI deploy) |
| “Permission denied writing debug.log” | debug.log or parent not writable | ls -l wp-content/debug.log; ls -ld wp-content | touch wp-content/debug.log && chown www-data:www-data $_ && chmod 664 $_ |
Files created as root after CLI ops | Ran commands via sudo outside web user | ls -l shows root root | chown -R targetUser:targetGroup /var/www/html and avoid sudo on write ops |
| Public can read secrets | wp-config.php too open | ls -l wp-config.php | chmod 600 wp-config.php (or 640) |
Quick Lab (End-to-End Hardening)
Scenario: Single-owner model with www-data. Site at /var/www/html.
cd /var/www/html
# 1) Reset safe modes
find . -type d -exec chmod 755 {} ;
find . -type f -exec chmod 644 {} ;
# 2) Lock config
chmod 600 wp-config.php
# 3) Ownership to web server
sudo chown -R www-data:www-data .
# 4) Ensure uploads/cache writable
sudo mkdir -p wp-content/{uploads,cache
sudo chown -R www-data:www-data wp-content/{uploads,cache
sudo chmod -R 755 wp-content/{uploads,cache
# 5) Verify
ls -ld wp-content wp-content/uploads
ls -l wp-config.php
Expected Results (sample):
drwxr-xr-x ... www-data www-data wp-content
drwxr-xr-x ... www-data www-data wp-content/uploads
-rw------- 1 www-data www-data ... wp-config.php
WordPress-Specific Benefits
| Action | Benefit |
|---|---|
Files 644, Dirs 755 | Principle of least privilege; safe defaults. |
wp-config.php → 600 | Protect DB credentials and salts. |
Correct owner (www-data or PHP-FPM user) | Updates/uploads/logging work reliably. |
775 + setgid on uploads (team) | Smooth collaboration without 777. |
ACLs on uploads/cache | Fine-grained access without changing global modes. |
Implementation Checklist
- Map site root and PHP user (
ps aux | grep php-fpmor web server docs). - Choose ownership model (single-owner vs two-user vs panel).
- Apply baseline modes: dirs
755, files644. - Lock
wp-config.phpto600(or640if group read is required). - Ensure
uploads/cacheare writable by runtime user (owning or ACL). - Verify with
ls -land test: media upload, plugin/theme update (or CI deploy).
Cheat Sheet
| Command | Purpose |
|---|---|
find . -type d -exec chmod 755 {} ; | Reset directory modes |
find . -type f -exec chmod 644 {} ; | Reset file modes |
chmod 600 wp-config.php | Lock configuration |
chown -R www-data:www-data /var/www/html | Single-owner model |
chown -R ubuntu:www-data /var/www/html | Two-user model (owner + webserver group) |
chmod -R 775 wp-content/{uploads,cache | Group-writable runtime dirs |
chmod g+s wp-content/uploads | Preserve group on new files (setgid) |
setfacl -m u:www-data:rwx -d -m u:www-data:rwx <path> | Grant & inherit ACLs |
Mini Quiz
- Why should
wp-content/uploadsbe writable by the PHP/web server user? - Whats the risk of using
777on runtime directories? - In a two-user model, why might you set **setgid on
uploads/? - Which files should be
600and why? - After migrating a site, which **three steps do you run to normalize perms/ownership?