For a local nginx site, the goal is narrow: the worker process must be able to reach every directory on the path to a file and read the static files it serves, while deployed code stays unwritable by that same process. Get the service identity right first, then change only the ownership or mode bit that is actually wrong. chmod changes permission bits, chown changes the owner and group, and neither fixes a problem that lives in a parent directory you have not inspected.
The access model in four checks
- Ownership picks one permission class. The kernel compares the requesting process’s user and group against the file’s owner and group. If the owner matches, only the owner bits apply; if the group matches, only the group bits apply; otherwise the “other” bits apply.
- Mode bits grant read, write, and execute. On a regular file,
xmeans “may run.” On a directory,xmeans “may enter and look up names inside it,” which is why it is often called search or traversal permission. - Every parent directory must allow traversal. A world-readable file inside a directory that the worker cannot enter is still unreachable.
- Extra policy can add rules. POSIX ACLs, SELinux, and AppArmor can deny access even when the mode bits look correct.
Step 1: Find the worker identity and the configured paths
nginx runs in two roles. The master process reads and evaluates configuration and manages workers, and the worker processes handle requests. Files are served by the workers, so the permissions that matter are those of the account named in the user directive, not the account that started the service.
As an Amazon Associate I earn from qualifying purchases.
- Read the effective configuration and find the account and document root:
sudo nginx -T | grep -E '^s*(user|root|alias)s'
Expected result: auserline such asuser www-data;and one or morerootoraliaslines naming the served directories. If theuserline is absent, nginx uses the compiled-in default, which varies by package. - Confirm the identity that is actually running:
ps -eo pid,user,group,comm | grep '[n]ginx'
Worker rows should show the account from step 1. The master usually showsroot. - Check for runtime write locations, such as upload, cache, or session directories, by searching the configuration for
client_body_temp_path,proxy_cache_path,fastcgi_temp_path, and any application-specific paths.
Distribution packages commonly use www-data on Debian and Ubuntu and nginx on many RHEL-family systems, but these are defaults you should confirm on the host rather than assume.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteStep 2: Inspect the full path before changing anything
Walk the path from the root down to the file. A single bad parent directory is the most common cause of a permission error that looks like a file problem.
#1 Best Overall
namei -l /var/www/example/public/index.htmlprints the owner, group, and mode of every component, so you can see exactly where traversal fails.stat -c '%U:%G %a %n' /var/www/example/public/index.htmlreports the owner, group, and octal mode of a single object (GNU coreutils syntax).getfacl /var/www/example/publicshows whether an ACL grants or denies access beyond the mode bits. A+after the mode inls -lindicates an ACL is present.ls -Zshows SELinux labels on systems where SELinux is enabled; AppArmor profiles are listed byaa-statuswhere that tool is installed.
Step 3: Choose ownership deliberately
A common design keeps deployment ownership with an administrator or deployment account and gives the server read access through a group. The worker is then a group member, not the owner, so it cannot change the files even if a bug or misconfiguration reaches them.
- Use
chown deploy:www-data /var/www/example/public/index.htmlto set both owner and group. The syntax isowner:group path; omitting the group changes only the owner. - Use
chgrp www-data pathwhen only the group is wrong. - Both commands require sufficient privilege, normally
sudo, and both assume the named accounts exist on this host. - Add
-Ronly after the tree has been inspected. Recursive changes apply to every descendant, including files that should keep a different owner. Review symbolic links first withfind /var/www/example -type l.
Step 4: Grant the minimum mode each path type needs
The right mode depends on what each path is for, not on a single value applied across the site. The table below lists conservative starting points for a layout where the deployment account owns the files and the worker belongs to the group.
Rank #2
| Path type | Suggested mode | Owner:group | Why |
|---|---|---|---|
| Static directories | 0750 | deploy:www-data | Owner full access, group read and traverse, other users none. |
| Static files | 0640 | deploy:www-data | Owner read and write, group read only, no execute. |
| Application code and templates | 0640 for files, 0750 for directories | deploy:www-data | The worker can read but not modify code. |
| Writable runtime data | 0770 for the directory | app:www-data | Only the process that writes needs write access; see Step 5. |
Numeric 0755 on directories and 0644 on files are common, but they grant read and traverse access to every local account. Use them only when the host’s other users may read the site. If local confidentiality matters, use the group-based modes above or an ACL that names only the worker account.
For targeted changes, symbolic modes avoid disturbing unrelated bits:
Rank #3
chmod u=rw,g=r,o= /var/www/example/public/index.htmlsets the owner, group, and other classes explicitly.chmod g+x /var/www/example/publicadds group search permission to a single directory without changing anything else.
To apply directory and file modes separately across a tree, use find rather than chmod -R, because a recursive numeric mode gives files execute bits they do not need:
find /var/www/example/public -type d -exec chmod 0750 {} +find /var/www/example/public -type f -exec chmod 0640 {} +
Step 5: Separate writable application data
If an application must accept uploads or write cache and session files, make only that data directory writable by the identity that writes to it. Keep application code and static content outside that directory. Apache’s security documentation for Apache HTTP Server 2.4 makes the same point: writable directories served or executed by the server can create security problems.
Where uploads land inside the served tree, verify that the application stack will not execute them. Serving an uploaded .php or script file through the web server is a far larger risk than any permission bit.
Recommended Free Tools
The F5 NGINXaaS documentation illustrates this separation with its own platform layout. It 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 product’s policy and should not be read as a universal Linux default.
Best Value
Step 6: Check how new files are created
Permissions on new files come from the creating process’s umask, reduced by any default ACL on the parent directory. A common umask of 022 turns a requested 0666 file mode into 0644, as the Linux man-pages project (man-pages 6.19, 2026) illustrates with 0666 & ~022 = 0644. Other services and parent directories may behave differently.
- Check the umask a systemd-managed service receives with
systemctl show nginx -p UMask. The unit file may override the default; check the unit name on your distribution. - Check a directory’s default ACL with
getfacl /var/www/example/public. Lines beginning withdefault:apply to new children. - If newly generated files have unexpected modes, correct the creating service’s umask or the parent default ACL, then fix existing files with the commands above.
Commands and what they change
| Command | Effect | When to use it |
|---|---|---|
chmod 640 file |
Owner read and write, group read, other none | Static or code files the worker only reads |
chmod u=rw,g=r,o= file |
Same class settings, stated explicitly | When you want the intent visible and unrelated bits untouched |
chmod g+x dir |
Adds group search on that directory only | Fixing a single blocked parent directory |
chown deploy:www-data file |
Sets owner and group | When ownership itself is the problem and the accounts exist |
Avoid chmod -R 777 and any recipe that makes the document root owned or writable by the web process. Those changes allow any local account, or a compromised worker, to alter served content.
Verify as the service identity
Ownership and mode checks are a model, not proof. Test as the worker account, using the account you found in Step 1:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →- Traversal and read:
sudo -u www-data test -r /var/www/example/public/index.html && echo readable - Directory traversal:
sudo -u www-data ls /var/www/example/public >/dev/null && echo traversable - No write access to code:
sudo -u www-data test -w /var/www/example/public/index.html || echo not-writable - Request the page over HTTP and confirm the status code, then check the error log, which is often at
/var/log/nginx/error.logon package installs.
Troubleshooting branches
- 403 with “Permission denied” (errno 13) in the error log. The worker cannot traverse or read a path. Run
namei -lon the requested file and find the first component the worker cannot enter or read. - Test commands succeed but the request still fails. Check the MAC layer. For SELinux, run
sudo ausearch -m avc -ts recent; the fix is usually the file label, not the mode. For AppArmor, checksudo journalctl -k | grep -i apparmorfor denials. - 404 for a file you can see on disk. This usually points to the
root,alias, ortry_filesconfiguration rather than to permissions. - Files created with the wrong mode. Review the creating service’s umask and any default ACL on the parent, as described in Step 6.
Scope and what changes between systems
There is no single correct web-server account, document root, or permission recipe. Package defaults, PHP and other application runtimes, containers, ACLs, SELinux or AppArmor policy, and mount options all change the effective result. Confirm the running identity and policy on the target host before applying any of the modes above. The examples here were not tested on a specific distribution release; they describe the general model that the Linux manual pages and the nginx and Apache documentation support.
The Apache material above applies to Apache HTTP Server 2.4. The NGINXaaS layout is product-specific, and the nginx master and worker roles described in Step 1 apply to self-managed nginx installs.
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.




