The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To serve files from Linux, the web-server process needs three things: the right identity, read access to each file it serves, and search (traverse) access to every directory above those files. Give it exactly that, and keep deployed code and static content unwritable by the same process. chown decides which user and group a file belongs to; chmod decides what those classes may do. Most permission failures on a web server come from getting one of these two wrong, or from a parent directory that quietly blocks the path.
This guide walks through the order that works in practice: identify the running identity, inspect the full path, change ownership, change modes narrowly, separate writable runtime data, check how new files are created, and verify the result as the service account.
Start with the identity that actually serves requests
Permissions only make sense relative to a specific user and group. On a web server, the account that matters is the one handling requests, not the one that installed the package or started the service.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesFor NGINX, the master process reads and evaluates the configuration and manages worker processes, and the workers are the processes that handle requests. The worker identity is set by the user directive in the main context of nginx.conf, when that directive is present. Package defaults differ between distributions, so do not assume a name such as www-data or nginx without checking.
#1 Best Overall
Confirm the running worker identity and the configured document root:
- Check the configuration for the worker user:
grep -RInE '^s*users' /etc/nginx/ - Check the live worker processes:
ps -eo user,group,pid,args | grep '[n]ginx'. Worker lines show the account that is actually serving requests. - Find the document root and any write locations:
grep -RInE '^s*(root|alias|client_body_temp_path|proxy_temp_path|fastcgi_temp_path)' /etc/nginx/
Apache HTTP Server follows the same principle with its own configuration directives, and PHP-FPM, Node.js, Python application servers and containers each run under their own identity. Whatever the stack, record the identity first. Every later chown or chmod decision depends on it.
Inspect the whole path before changing anything
A file can have correct permissions and still be unreachable. Linux checks each directory in the path from the top down, so a single parent directory without search permission for the web-server identity blocks every file beneath it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use these commands to see the full chain:
namei -l /var/www/example/public/index.htmllists each path component with its owner, group and mode. This is the fastest way to find the component that blocks access.nameicomes from util-linux on most distributions.stat -c '%U:%G %a %n' /var/www/example/public/index.htmlprints the owner, group, octal mode and name for one object.getfacl /var/www/example/publicshows whether extended ACL entries exist. Some systems return no ACL output when none are set, andgetfaclis provided by the acl package where it is not installed by default.
Record the output before you change anything. It becomes your baseline if you need to roll back.
Choose ownership deliberately with chown
chown changes the owner, the group, or both. The syntax chown owner:group path sets both; chown :group path changes only the group. GNU chown accepts a group after a colon, and -R applies the change to every descendant.
A widely used arrangement separates deployment from serving:
Rank #2
- The owner is a deployment or administrator account. It can update files and directories.
- The group is a controlled group that includes the web-server identity. It grants read and, for directories, traversal.
- The other class, meaning every other local user, gets no access unless you have a reason to grant it.
Example, assuming the account deploy and the group www-data both exist on your host:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11sudo chown deploy:www-data /var/www/example/public/index.html
Use chown only when ownership or group membership is the actual problem. Changing the owner of a tree to the web-server identity lets that process rewrite files it should only read, which undoes the separation you are trying to create. Ownership changes also require sufficient privilege; on most systems that means sudo or root.
Before any recursive ownership change, list the tree and check for symbolic links:
find /var/www/example -xdev -type l -print
Symlinks can point outside the intended tree, so review what they reach before you broaden a change.
Set modes narrowly with chmod
chmod changes the permission bits. Octal and symbolic forms both work. The symbolic form is safer for targeted changes because it changes only the classes you name and leaves others alone.
Rank #3
Symbolic changes
chmod g+x /var/www/example/publicadds group traversal to one directory without touching owner or other bits.chmod u=rw,g=r,o= /var/www/example/config.envsets the owner to read and write, the group to read, and removes all access for others, leaving no unlisted bits to guess about.
On a directory, the execute bit means search (traverse). Without it, a process cannot reach names inside the directory, even if it can read the directory listing.
Octal modes and what they expose
The table below shows common octal modes. These are examples of the trade-off, not universal prescriptions.
| Mode | Owner | Group | Others | Typical use | Caveat |
|---|---|---|---|---|---|
| 640 | read, write | read | none | Files the web server reads through group membership, such as configuration with restricted access | The group must be the one the server identity belongs to, or the server cannot read the file |
| 644 | read, write | read | read | Static files readable by everyone | Any local user can read the file, which is often too broad on shared hosts |
| 750 | read, write, search | read, search | none | Directories reachable only by owner and group | Requires the server identity to be in the group |
| 755 | read, write, search | read, search | read, search | Public static directories | Lets every local user list and traverse the directory |
The tradeoff is clear: 644 and 755 are convenient on single-purpose servers, but on a shared machine they expose content to all local users. When local confidentiality matters, use a group-specific mode such as 750 and 640, or ACL entries that name the server identity directly.
Free tools Windows power users keep installed
One-click scans. No signup required.
Modes to avoid
chmod -R 777grants write access to every user on every file and directory beneath the target. It also makes uploaded or injected content easier to alter.- Writable deployed code. Files the web process executes or serves should not be writable by that process.
- Blanket recursive changes. A
-Rrun applies one mode to regular files, directories and scripts alike, so files that need execute permission and files that need only read permission get the same bits.
When a directory needs a different mode from its files, use find to target each type separately:
sudo find /var/www/example/public -type d -exec chmod 750 {} +
sudo find /var/www/example/public -type f -exec chmod 640 {} +
Run the directory and file commands separately, and only after confirming the tree contains no executable scripts that need a different mode.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Separate writable runtime data from served content
Some applications must write. Uploads, caches, session files, and temporary request bodies all need a writable location. The principle is to make only that location writable by the application identity, and keep executable code and static content outside it.
F5 NGINXaaS documentation states: “/var/www is a secure location for static content because the NGINX worker process can serve files from it but cannot modify them, ensuring content integrity.” This describes that platform’s policy. It is an example of the pattern, not a universal Linux default path.
On a self-managed server, a common layout is:
- Static and deployed files: owned by a deployment account, readable by the server group, not writable by the server identity.
- Runtime write directory (for example, an upload or cache path): owned by or writable to the application identity, placed outside the document root where possible.
- If uploads must be inside the document root, configure the stack so uploaded files cannot be executed as code. Serve them with a handler that does not run scripts, and block script extensions in that path.
Where a writable directory is required, set it explicitly rather than widening everything else:
sudo chown deploy:www-data /var/www/example/uploads
sudo chmod 2770 /var/www/example/uploads
The leading 2 sets the setgid bit, so new files inherit the directory’s group. Test the result with the service identity (covered below) before relying on it.
Best Value
Check how new files are created
Permissions for new files come from two sources: the creating process’s umask and, where present, default ACLs on the parent directory.
The umask removes bits from the mode a program requests. Linux man-pages documents the example 0666 & ~022 = 0644 (man-pages 6.19, 2026), which means a file requested with mode 0666 under umask 022 is created as 0644. That is an illustration of the arithmetic, not a statement that every server runs with umask 022.
To check the umask in a running service context:
- Log in as the service account, or run
sudo -u www-data sh -c 'umask', substituting your identity. - Inspect the service unit. For systemd-managed services, check for
UMask=in the unit file or drop-in withsystemctl cat nginx. - Check default ACLs on the parent:
getfacl /var/www/example/uploads. Lines beginning withdefault:apply to new children.
If newly created files have surprising modes, the cause is usually one of these two. Fix it at the source instead of repairing each file afterwards.
Verify as the service identity
Testing as your administrator account can be misleading, because that account may have access the server does not. Run the checks as the server identity:
- Confirm read access to a served file:
sudo -u www-data cat /var/www/example/public/index.html > /dev/null && echo readable - Confirm traversal through each parent:
sudo -u www-data test -x /var/www/example/public && echo traversable - Confirm that deployed code cannot be written:
sudo -u www-data test -w /var/www/example/public/index.html || echo not-writable - Confirm the intended write location is writable:
sudo -u www-data test -w /var/www/example/uploads && echo writable - Request the page:
curl -I http://localhost/and check for200rather than403.
Replace www-data and the paths with your actual identity and locations. If a command fails, the first check is the one that failed.
Troubleshoot when the modes look correct
If mode bits, ownership and traversal all look right but access still fails, check these in order:
- Parent directories. Run
namei -lon the full path again. A single directory without search permission for the server identity is the most common cause of a request that fails on an otherwise correct file. - Error logs. NGINX writes permission errors to its error log. Lines containing
Permission deniedpoint to filesystem or access-control problems, while a 403 response in the access log can also come from directory indexing or configuration rules rather than file permissions. - ACLs. An ACL that denies or masks access overrides the basic mode check for named users. Inspect with
getfacl. - Mandatory access control. SELinux and AppArmor policy can deny access even when mode bits allow it. On SELinux systems,
ls -Zshows file contexts. On AppArmor systems, check the kernel audit or journal messages for denials tied to the server process. - Mount options and containers. Filesystems mounted with restrictive options, network filesystems with mapped identities, and containers that map user IDs can make the effective identity differ from the one you see on the host.
Use each finding to narrow the cause before changing permissions again. Repeated broadening of modes usually hides the real problem and adds exposure.
Recommended Free Tools
Keep changes reviewable
Permission problems often get fixed by trial and error. A simple habit prevents that: record the output of namei -l and stat before a change, make the smallest change that matches the diagnosis, and run the verification commands again afterwards. Where a deployment tool manages the tree, make permission changes in that tool so they survive the next deploy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

