October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk7 min

How to Manage Linux File Permissions for Web Servers with chmod and chown

A practical method for letting a Linux web server read static content without giving it write access to deployed code, with commands, a permissions table, and troubleshooting steps.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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, x means “may run.” On a directory, x means “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.

  1. Read the effective configuration and find the account and document root:

    sudo nginx -T | grep -E '^s*(user|root|alias)s'

    Expected result: a user line such as user www-data; and one or more root or alias lines naming the served directories. If the user line is absent, nginx uses the compiled-in default, which varies by package.
  2. 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 shows root.
  3. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Step 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.

  • namei -l /var/www/example/public/index.html prints 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.html reports the owner, group, and octal mode of a single object (GNU coreutils syntax).
  • getfacl /var/www/example/public shows whether an ACL grants or denies access beyond the mode bits. A + after the mode in ls -l indicates an ACL is present.
  • ls -Z shows SELinux labels on systems where SELinux is enabled; AppArmor profiles are listed by aa-status where 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.html to set both owner and group. The syntax is owner:group path; omitting the group changes only the owner.
  • Use chgrp www-data path when only the group is wrong.
  • Both commands require sufficient privilege, normally sudo, and both assume the named accounts exist on this host.
  • Add -R only after the tree has been inspected. Recursive changes apply to every descendant, including files that should keep a different owner. Review symbolic links first with find /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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For targeted changes, symbolic modes avoid disturbing unrelated bits:

  • chmod u=rw,g=r,o= /var/www/example/public/index.html sets the owner, group, and other classes explicitly.
  • chmod g+x /var/www/example/public adds 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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 with default: 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Traversal and read: sudo -u www-data test -r /var/www/example/public/index.html && echo readable
  2. Directory traversal: sudo -u www-data ls /var/www/example/public >/dev/null && echo traversable
  3. No write access to code: sudo -u www-data test -w /var/www/example/public/index.html || echo not-writable
  4. Request the page over HTTP and confirm the status code, then check the error log, which is often at /var/log/nginx/error.log on package installs.

Troubleshooting branches

  • 403 with “Permission denied” (errno 13) in the error log. The worker cannot traverse or read a path. Run namei -l on 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, check sudo journalctl -k | grep -i apparmor for denials.
  • 404 for a file you can see on disk. This usually points to the root, alias, or try_files configuration 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.

“

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.