In short: For web server file permissions, one principle applies: the user PHP runs as may read its own code but not change it. Only upload, cache and temp folders get write permissions, and no PHP is executed there. Permissions such as 777 have no place on a production server.
Why file permissions make the difference after a break-in
When attackers execute code on a server through a vulnerability, that code runs with the permissions of the PHP process. What this user may do, the attacker may do too. This is exactly where it is decided whether a break-in turns into a permanent backdoor.
With PHP2Shell this scenario became very concrete in October 2026. The vulnerabilities CVE-2026-109618 and CVE-2026-109619 allow remote code execution without authentication. They have been fixed since 21 October 2026 in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Good file permissions do not prevent an attack, but they prevent planted code from rewriting your application permanently.
In many installations the entire code belongs to the web server user so that updates work at the push of a button. That is convenient, but exactly the constellation attackers look for.
The basic principle: separate the owner from the runtime user
A split into two roles makes sense. An owner, for example a deploy user, owns the files and installs updates. The PHP process runs under a different user and has read-only permission via the group.
| Area | Owner : group | File permissions | Folder permissions |
|---|---|---|---|
| Program code | deploy : seite1 | 640 | 750 |
| Configuration with passwords | deploy : seite1 | 640 | – |
| Uploads, cache, temp | seite1 : seite1 | 640 | 750 |
| Logs | root or seite1 | 640 | 750 |
With PHP-FPM, the web server itself, such as Nginx, needs only read access to static files such as images and CSS. You provide this via the group or a targeted access rule. Anyone running several websites gives each its own runtime user; how this interacts with pools is in the guide open_basedir and isolation per website.
Checking web server file permissions
Before you change anything, get an overview. The following commands change nothing.
# Which user do the PHP processes run as?
ps -eo user,cmd | grep -E "[p]hp-fpm|[a]pache2|[h]ttpd" | sort | uniq -c
# Files and folders writable by everyone
find /var/www -perm -o+w -ls
# Files owned by the web server user (example www-data)
find /var/www -user www-data -type f -name "*.php" | head -50
# Permissions of the configuration files
ls -la /var/www/*/wp-config.php /var/www/*/.env 2>/dev/null
Entries writable by everyone are almost always a mistake. PHP files owned by the runtime user show that it can change its code. Also conspicuous are individual files whose owner or permissions differ from the neighbouring files. That can be a sign of planted files, as described in the guide Detecting and removing webshells.
Correcting permissions step by step
Change permissions only after a backup and, if possible, first in a test environment. If you suspect a break-in, secure the traces beforehand, because owners and timestamps are then evidence.
SITE=/var/www/seite1
# Owner: deploy user, group: runtime user
sudo chown -R deploy:seite1 "$SITE"
# Folders 750, files 640
sudo find "$SITE" -type d -exec chmod 750 {} +
sudo find "$SITE" -type f -exec chmod 640 {} +
# PHP may write only to these folders (adjust paths to the application)
sudo chown -R seite1:seite1 "$SITE/public/uploads" "$SITE/var/cache"
Afterwards, check the website completely: home page, login, forms, image upload, caches. Which folders an application must write to is stated in the vendor's system requirements on its official site.
A chmod -R 777 to solve a problem quickly opens the server to every local user and every planted process. Correct the owner and group instead.
Preventing PHP execution in writable folders
Write permissions are only half as dangerous if no code can run there. Block the execution of PHP in upload and cache folders at web server level. For Apache in the VirtualHost configuration:
<Directory "/var/www/seite1/public/uploads">
<FilesMatch "\.(php|phtml|phar)$">
Require all denied
</FilesMatch>
</Directory>
For Nginx, a location block that rejects requests for PHP files in these folders is enough. The rule belongs in the server configuration, not in a .htaccess in the upload folder, which an attacker with write permissions could change again.
It is also worth looking at the deploy user's umask, so that new files are not created too openly by default. A value of 027 results in 640 for files and 750 for folders. On your own servers this responsibility lies entirely with you; what else belongs there is described in Securing root servers and vServers yourself.
Special cases: hosting panels, shared hosting and deployments
Not every server can be set up by the textbook. Hosting panels such as Plesk or cPanel create their own users and permissions and reset them with some actions. Change permissions there preferably through the panel's functions, or check after panel updates whether your settings still apply.
With shared hosting you usually have only one user for everything. A separation of owner and runtime user is then not possible. All the more important are tight permissions on configuration files, no 777 permissions and switching off PHP execution in upload folders, as far as the hosting provider allows it.
Anyone working with automatic deployments should set the permissions in the deployment script itself. Otherwise every new release restores the old, too open state. A short check at the end of the deployment, for example with the command for writable files from above, reliably catches such regressions and can be built into any pipeline without much effort.
How does PHP SOS help?
- We record owners, permissions and runtime users of all websites on the server.
- We define a permissions concept that fits your application and your update route.
- We implement permissions and execution blocks and then test the website completely.
- We document the concept so that deployments and updates do not weaken it again.
How this is embedded in lasting protection is described in the service Server hardening for PHP web servers.
Frequently asked questions
Are 777 permissions ever acceptable?
On a production server, practically never. With 777, every user on the system may create and change files. If an application does not run without 777, the owner is usually set wrongly; the permissions are not too tight.
Why should the web server not be allowed to change its own files?
Because planted code runs with the web server's permissions. If the web server may write PHP files, an attacker can manipulate existing scripts or create new backdoors. If you separate the owner from the runtime user, the code stays write-protected.
How do I update a CMS if the web server may not write?
Updates then run via the owner, for example with WP-CLI, Composer or a deployment, not via the update button in the backend. That is somewhat more laborious, but planted code can then not change program files.
Which permissions does the configuration file with the database password need?
As few as possible: readable for the PHP user and for nobody else. Usual are 640 with a suitable group, or 400 if the PHP user is the owner itself.