Skip to main content

Permission wp folder

Use cases: securing /var/www, permission for WordPress folders.

What You Will Learn

  1. Understand the difference between **permissions and **ownership in a WordPress context.
  2. Map **recommended permissions per WordPress folder (wp-admin, wp-includes, wp-content, uploads, plugins, themes, root files).
  3. Select the right **ownership model (www-data, deploy user + group, panel users) and when to use each.
  4. Apply safe, idempotent **hardening steps for /var/www after install/migration.
  5. Use chmod, chown, chgrp, and (optionally) setfacl to implement secure collaboration.
  6. Read and decode numeric (octal) vs symbolic permissions and the **setgid bit for shared dirs.
  7. Diagnose **permission denied and **cannot write to directory with a quick troubleshooting matrix.
  8. 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), reading ls -l output, understanding users/groups.

5W + 1H Framework

QuestionAnswer
WhatSecure, workable permissions/ownership for WordPress, focusing on /var/www.
WhyPrevent compromise while allowing WordPress, PHP, and admins to read/write where necessary (updates, uploads, cache).
WhenImmediately post-install, post-migration, after restoring backups, or when fixing upload/update errors.
WhereTypical root: /var/www/html/yourdomain/ (or panel-specific roots).
WhoSystem admins, developers, DevOps, site owners maintaining VPS WordPress.
HowApply 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

CodeSymbolicOwnerGroupOthersTypical WordPress UseExplanation
600rw-------rwwp-config.phpOnly owner can read/write (DB creds).
640rw-r-----rwrwp-config.php when group read is requiredOwner controls; group can read.
644rw-r--r--rwrrMost PHP/JS/CSS filesDefault safe for files.
750rwxr-x---rwxr-xPrivate admin dirs/scriptsGroup read/exec; others no access.
755rwxr-xr-xrwxr-xr-xDirectories (wp-content, wp-admin, etc.)Enter/browse dirs but no public write.
775rwxrwxr-xrwxrwxr-xShared dev dirs (with setgid)Owner+group write; others read/exec.
777rwxrwxrwxrwxrwxrwxAvoidWorld-writable (security risk).

Command Variations & Flags

CommandOptionMeaningExample
chmodNumericSet exact mode via octalchmod 644 wp-config.php
chmodSymbolicIncremental changeschmod g+w wp-content/uploads/
chmod-RRecurse into subpathschmod -R 755 wp-content/
chownuser:groupSet owner and groupchown www-data:www-data index.php
chown-RRecursive ownershipchown -R www-data:www-data /var/www/html
chgrpgroup onlyChange groupchgrp 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: r read, w write, x execute

Examples of Symbolic Notation

CommandMeaningExample Use Case
chmod g+w wp-content/uploads/Add write to groupTeam uploads via shared group
chmod o-r wp-config.phpRemove others’ readHide DB creds from world
chmod a+X wp-content/Add execute on dirsEnsure all dirs are traversable
chmod u=rw,g=r,o= index.phpExplicit modeOwner 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

PathTypeRecommended ModeOwner:Group (common)Notes
/var/www/html/Site root dir755www-data:www-dataWeb server can traverse; not writable by public.
wp-admin/Dir755www-data:www-dataNo writes needed at runtime.
wp-includes/Dir755www-data:www-dataNo writes needed at runtime.
wp-content/Dir755www-data:www-dataWritable subpaths only (uploads, cache).
wp-content/plugins/Dir755www-data:www-dataWrite only during updates/installs.
wp-content/themes/Dir755www-data:www-dataWrite during updates; otherwise read-only.
wp-content/uploads/Dir755 (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/Dir755 (or 775)As aboveMust be writable by PHP.
wp-config.phpFile600 (or 640)www-data:www-data (or owner)Lock down credentials.
index.php, core filesFiles644www-data:www-dataDefault 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): 775 with **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 (often www-data).
  • **Multisite: Same baseline; watch per-site upload subfolders.
  • **PHP-FPM Pools: If pool runs as siteuser, ownership should match that user (not www-data).
  • **Never use 777 in production; fix underlying ownership instead.

Troubleshooting Matrix

SymptomLikely CauseVerifyFix
Unable to create directory … uploads”uploads/ not writable by PHP userls -ld wp-content/uploadschown -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/pluginsTemporarily 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 writablels -l wp-content/debug.log; ls -ld wp-contenttouch wp-content/debug.log && chown www-data:www-data $_ && chmod 664 $_
Files created as root after CLI opsRan commands via sudo outside web userls -l shows root rootchown -R targetUser:targetGroup /var/www/html and avoid sudo on write ops
Public can read secretswp-config.php too openls -l wp-config.phpchmod 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

ActionBenefit
Files 644, Dirs 755Principle of least privilege; safe defaults.
wp-config.php600Protect 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/cacheFine-grained access without changing global modes.

Implementation Checklist

  1. Map site root and PHP user (ps aux | grep php-fpm or web server docs).
  2. Choose ownership model (single-owner vs two-user vs panel).
  3. Apply baseline modes: dirs 755, files 644.
  4. Lock wp-config.php to 600 (or 640 if group read is required).
  5. Ensure uploads/cache are writable by runtime user (owning or ACL).
  6. Verify with ls -l and test: media upload, plugin/theme update (or CI deploy).

Cheat Sheet

CommandPurpose
find . -type d -exec chmod 755 {} ;Reset directory modes
find . -type f -exec chmod 644 {} ;Reset file modes
chmod 600 wp-config.phpLock configuration
chown -R www-data:www-data /var/www/htmlSingle-owner model
chown -R ubuntu:www-data /var/www/htmlTwo-user model (owner + webserver group)
chmod -R 775 wp-content/{uploads,cacheGroup-writable runtime dirs
chmod g+s wp-content/uploadsPreserve group on new files (setgid)
setfacl -m u:www-data:rwx -d -m u:www-data:rwx <path>Grant & inherit ACLs

Mini Quiz

  1. Why should wp-content/uploads be writable by the PHP/web server user?
  2. Whats the risk of using 777 on runtime directories?
  3. In a two-user model, why might you set **setgid on uploads/?
  4. Which files should be 600 and why?
  5. After migrating a site, which **three steps do you run to normalize perms/ownership?