How to Set Up Proper File & Directory Permissions for WordPress and Nginx

Improper file and directory permissions are responsible for more than half of all WordPress security breaches and administrative headaches on Linux servers. When inexperienced administrators encounter an “Upload folder is not writable” or “403 Forbidden” error, many resort to executing chmod -R 777 /var/www/ in frustration. This single action is catastrophic: 777 grants read, write, and execute privileges to every local user and web process on the machine, allowing any attacker who exploits a single PHP vulnerability to overwrite core files, inject web shells, and seize total control of your Linux VPS.

Conversely, setting permissions too strictly breaks automated plugin updates, blocks image uploads, and corrupts cache generation. In this guide, you will learn the exact principle of least privilege required to maintain airtight security while ensuring seamless WordPress and Nginx operations.

1. The Linux Permission Model & User Architecture

Every file and folder on Linux is governed by ownership and permission bits across three scopes: User (Owner), Group, and Others (World).

  • Nginx Worker User: Typically runs as www-data on Debian/Ubuntu or nginx on RHEL/CentOS.
  • PHP-FPM Worker User: Configured in /etc/php/8.3/fpm/pool.d/www.conf, usually running as www-data.
  • Administrative SFTP/SSH User: The user account you log in with (e.g., deployer or administrator).

The optimal enterprise architecture separates administrative ownership from runtime execution by adding your deployer user to the www-data group.

2. Golden Permission Standard for WordPress & Nginx

Follow the universally accepted production permission baseline:

  • Directories: Set to 755 (drwxr-xr-x). Allows owner full access, while web server processes can traverse directories and read contents.
  • Files: Set to 644 (-rw-r--r--). Allows owner to read and write, while web processes can read files without having write privileges.
  • wp-config.php: Set to 400 or 440. Read-only for the owner and web server group; invisible to other system users.
  • wp-content/uploads/: Owned by www-data:www-data with 755 directories and 644 files to allow authenticated media uploads while blocking script execution.

3. Step-by-Step Shell Script to Lock Down Permissions

Execute this production shell script to enforce the security baseline across your WordPress root directory:

#!/usr/bin/env bash
set -euo pipefail

TARGET_DIR="/var/www/my-site"
WEB_USER="www-data"
WEB_GROUP="www-data"

echo "[$(date)] Enforcing enterprise permission baseline on ${TARGET_DIR}..."

# 1. Reset ownership across all files and directories
sudo chown -R ${WEB_USER}:${WEB_GROUP} "${TARGET_DIR}"

# 2. Reset all directory permissions to 755
sudo find "${TARGET_DIR}" -type d -exec chmod 755 {} \;

# 3. Reset all file permissions to 644
sudo find "${TARGET_DIR}" -type f -exec chmod 644 {} \;

# 4. Lock down wp-config.php (Read-only for web user)
sudo chmod 440 "${TARGET_DIR}/wp-config.php"

# 5. Lock down .htaccess or Nginx configuration files
if [ -f "${TARGET_DIR}/.htaccess" ]; then
    sudo chmod 444 "${TARGET_DIR}/.htaccess"
fi

echo "File permissions successfully locked down!"

4. Preserving Permissions for SFTP Deployments with SetGID

If you upload files via SFTP using an administrative user (e.g., deployer), new files are created under deployer:deployer, causing PHP-FPM to lose write access to cache and upload directories. To permanently solve this without running manual chown scripts, enable the Linux SetGID (Set Group ID) bit on directories:

# Enable SetGID on all existing directories
sudo find /var/www/my-site -type d -exec chmod g+s {} \;

The g+s flag commands the Linux kernel to ensure that any new file or subdirectory created inside inherits the www-data group ownership automatically, eliminating ownership conflicts forever.

5. Blocking PHP Execution in Writable Directories via Nginx

The most important security rule in web hosting is simple: directories that permit file uploads must never permit code execution. Add this hardened rule inside your Nginx server block to render uploaded web shells completely inert:

# Block direct PHP execution in uploads and cache directories
location ~* ^/(?:wp-content/(?:uploads|cache)|wp-includes)/.*\.php$ {
    deny all;
    access_log off;
    log_not_found off;
    return 403;
}

Linux Security Standards: Immutable Files with chattr & Auditd Tracking

Beyond traditional chmod and chown permission bits, enterprise Linux systems provide immutable filesystem flags and automated kernel auditing:

  • Locking Down wp-config.php with chattr +i: The Linux chattr +i command sets the immutable attribute on a file. Once set, even the root administrative user cannot modify, overwrite, rename, or delete the file until the attribute is explicitly removed:
    # Make wp-config.php completely immutable
    sudo chattr +i /var/www/my-site/wp-config.php
    
    # Verify immutable flag is active
    lsattr /var/www/my-site/wp-config.php
    # Output displays: ----i---------e-- /var/www/my-site/wp-config.php

    Even if an attacker gains root or web shell access, automated scripts cannot alter your database credentials or inject malicious PHP headers into wp-config.php.

  • Monitoring Critical File Access with Linux Auditd: Configure the Linux audit daemon to record any attempt to modify sensitive directories in real time:
    sudo apt install -y auditd
    # Watch /var/www/my-site/wp-content/plugins for unauthorized writes
    sudo auditctl -w /var/www/my-site/wp-content/plugins/ -p wa -k plugin_tamper_watch

    Inspect audit alerts via ausearch -k plugin_tamper_watch -ts recent.

  • Hardening Temporary Directory Mounts (/tmp): Many attackers upload and execute exploit binaries inside /tmp. Mount /tmp with noexec,nosuid,nodev in /etc/fstab to prevent execution of downloaded shell binaries.

Hardened Web Hosting Infrastructure on CpanelFree

Deploy production web apps on isolated, pre-hardened cloud instances. Benefit from enterprise SSD/NVMe speeds, zero shared-hosting permission bugs, and complete root control.

Discover CpanelFree Cloud Hosting →

Leave a Comment