Set Linux website permissions without making code writable

Separate deployment ownership from runtime write access, then check directory traversal and security policy.

Best practices
Illustration of application modules working together

A useful baseline for public static content is mode 755 on directories and 644 on files, owned by a deployment account. The web server normally needs to read code and assets, not rewrite them. Give its runtime identity write access only to directories the application genuinely needs to modify.

Separate deployment from runtime ownership

For a reviewed static release tree, a deployment account can own the files while the web server reads them:

# Example accounts and path; adapt to the deployment.
sudo chown -R deploy:deploy /var/www/example/public
sudo find /var/www/example/public -type d -exec chmod 755 {} \;
sudo find /var/www/example/public -type f -exec chmod 644 {} \;

Do not run blanket permission resets over an unfamiliar application tree. Executable entry points, secrets, symlinked releases, and shared writable paths may need different handling. 755 and 644 also allow other local users to read the content; use a tighter group-based policy when that is inappropriate.

A directory's execute bit permits traversal. Read permission on the final file is insufficient if the service cannot traverse one of its parent directories.

Limit runtime writes

Put uploads, caches, sessions, and application logs in explicitly designated paths. For example, after creating the directory and checking the runtime account:

sudo chown www-data:www-data /var/www/example/storage
sudo chmod 750 /var/www/example/storage

The account may be apache, nginx, a PHP-FPM pool user, or another identity rather than www-data. Configure uploaded files so they cannot be executed as application code. Storing uploads outside the executable document root is often easier to enforce than relying only on filename filtering.

Keep secret configuration outside public content where possible. Mode 600 permits only the owner to read it; 640 permits a selected group too. The application must still be able to read the file. Mode alone does not stop a misconfigured web server from serving a readable secret.

Share a writable area deliberately

When the deployer and runtime both need access to one writable area, use a dedicated group and suitable creation defaults:

sudo chgrp -R webwriters /var/www/example/storage
sudo find /var/www/example/storage -type d -exec chmod 2770 {} \;
sudo find /var/www/example/storage -type f -exec chmod 660 {} \;

The setgid bit on directories makes new children inherit the directory's group. It does not force group-write permission on newly created files; configure the creating processes' umask, application modes, or default ACLs appropriately. New group membership also needs to reach the running processes.

Do not use chmod -R 2775 on the whole site: it adds execute and setgid bits to ordinary files and expands write access far beyond the intended storage area. 777 is not a repair for an ownership mistake.

Investigate denied access

Check the path component by component, then the service identity, filesystem permissions, ACLs, and SELinux or AppArmor policy. On SELinux systems, ls -Z shows labels and restorecon reapplies the configured policy labels; a custom path may first need an appropriate persistent file-context rule. Do not disable enforcement to hide the symptom.

For CMS updates, choose between deployment-managed releases and narrowly controlled application-managed updates. Granting the entire web process permanent write access to all code has a real compromise impact.

Collected Nginx error logs in Guance can identify the affected host and path. Repeat the request after a targeted fix to confirm that the original denial is resolved.

References: Apache security guidance and GNU file permissions.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting