NGINX / PHP / Server Setup

NGINX and PHP-FPM. What my permissions should be?

by , , revisited on


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

Being an avid StackExchange user, I could see how many users completely lack understanding of the proper permissions model in the most popular, LEMP stack.

You can see recommendations that 777 is never good, but I could not see a simple guide on permissions that can be used as a reference point for everyone.

The following permissions/ownership model applies to all NGINX/PHP-FPM websites and allows you to host websites without any problems, in a secure way.

PHP-FPM user (as known as the website user)

The PHP-FPM user should be a special user that you create for running your website, whether it is Magento, WordPress, or anything.
Its characteristics are the following:

  • This is the user that PHP-FPM will execute scripts with
  • This user must be unique. Do not reuse an existing user account. Create a separate user for each website!
  • Do not reuse any sudo capable users. If your website user is ubuntu or centos, or, root – you’re asking for much trouble.
  • Do not use www-data or nginx as website user. This is wrong and will lead to more troubles!
  • The username should reflect either the domain name of the website that it “runs”, or the type of corresponding CMS, e.g. magento for a Magento website; or example for example.com website.

Let’s create this user:

useradd example

Now, set its password by running:

passwd example

Each website in PHP-FPM should be run under a separate pool. In the pool settings file, e.g. /etc/php-fpm.d/example.com.conf, you must set things to match with the created username:

listen = /var/run/php-fpm/example.com.sock
listen.owner = example
listen.group = example
listen.mode = 0660
user = example
group = example

The webserver user

NGINX must run with it own unprivileged user, which is nginx (RHEL-based systems) or www-data (Debian-based systems).

This is the “global” webserver user that is used for all websites. So the configuration is straightforward and translates to the following directives in /etc/nginx/nginx.conf:

user nginx;

Connecting website and webserver users

Now the magic. We must connect things up so that NGINX (webserver) user can read files that belong to the website user’s group.

This will allow us to control what NGINX can read or not, via group chmod permission bit.

usermod -a -G example nginx

This reads as: add nginx user to group example.

File ownership (chown)

Here is a simple rule: all the files should be owned by the website user and the website user’s group:

chown -R example:example /path/to/website/files

Incorrect chown examples:

  • www-data:www-data
  • example:nginx
  • nginx:nginx

Correct chown example:

  • example:example
  • foo:foo

Permissions (chmod)

The following general chmod setup will allow for any website to function properly:

chmod -R u=rwX,g=rX,o= /path/to/website/files

This translates to the following:

  • Website user (example) can read, write all files and read all directories
  • Website group (webserver user) can read all files and traverse all directories, but not write
  • All other users cannot read or write anything

In octal notation, this results in 0750 chmod for all directories and 0640 for all files.

If you simply stick to this permissions model, you will not encounter any chmod / chown issues in the future.
This is the only proper model, functionality, and security-wise.

These general permissions are quite secure already out of the box. But of course, if you know your website structure, you may want to tighten up permissions even further.

For example, see Magento 1.x lockdown chmod.

  1. Shnoulle

    With chmod -R u=rwX,g=rX,o= /path/to/website/files ,

    If you have assets in /path/to/website/files/assets/css for example : nginx can not read/load it …

    How do you manage this?
    A potential solution ACL for nginx/www-data user.

    Reply
    • Danila Vershinin

      NGINX will be able to read the files.

      css directory will be owned by example:example (owned by example user and by example group). Because we added nginx user (or www-data, depends on distro) as a member of the site user group, and the directory chmod allows for group reads, it will be able to access the directory just fine.

      See the “Connecting website and webserver users” section. This is the essential step: the webserver user is a member of each website’s user group. Not the other way around.

      All the files are owned by example:example. Where necessary for NGINX to read the files, the group permission bit is used to control what it can and what it can’t read.

      Reply
      • Steve M

        Danila,

        Thank you for your article. I followed your instructions and configured nginx and php-fpm to work the same way, but I have issues with stylesheets. I have WordPress installation and stylesheets and other statics aren’t loaded properly. In browser’s console it says “Resource interpreted as Stylesheet but transferred with MIME type text/html”. I figured it’s statics going through php-fpm and not straight through nginx. I still haven’t got a clue how to fix it though.

        Reply
        • Danila Vershinin

          Your error rather indicates a case when the browser receives a 404 (not found) or similar error, generated by NGINX, when accessing /some/style.css or /some/script.js. Those have Content-Type: text/html, which is of course neither a stylesheet nor a script.

          It doesn’t matter here WordPress or not you’re dealing with. The above permission setup applies to any NGINX/PHP-FPM website without any flaws or problems.

          Your issue means that NGINX cannot read the files. You haven’t followed some of the steps as outlined above. Make sure nginx user in website user’s group and the permissions were set as chmod -R u=rwX,g=rX,o= /path/to/website/files

          Reply
  2. suges

    It really helped a lot, thank you, Danila.

    Reply
  3. Person

    What about using Linux ACLs? No need to modify the owner of the files, but still allow access to any folder that needs it.
    This is an example for Laravel: https://stackoverflow.com/a/41268166/12869323

    Reply
    • Danila Vershinin

      Bear with me 🙂 You really meant “no need to set the user ownership and permissions right”.

      In the case of NGINX (which can be applied to Apache as well, depending on a mode it runs with) and PHP-FPM, there are basically 2 services interacting with each other: NGINX is one user, PHP-FPM is another.

      Two services mean two different Linux users. They can be connected through the group permission bit.
      “Connected” means both of the mentioned services (users) can access/modify the files without giving problems to each other.

      Funnily enough, as far as your referenced StackOverflow question, the accepted/most-upvoted answer almost got it right.
      But never far enough right. At least for any of the stacks of “Apache + PHP-FPM” or “NGINX + PHP-FPM,” it won’t be right:

      In the section “Webserver as owner (the way most people do it, and the Laravel doc’s way)”, even its title is wrong.
      I don’t think there is such a reference in Laravel doc that would dare to state that all files should be owned by www-data,
      which is outright wrong.
      Instead, the right thing: every website has its own dedicated username (which PHP-FPM pool runs with),
      and all the particular website files are owned by their respective website user.
      The correct part in that section was about connecting users through adding www-data to the user’s group.
      As far as chmod, any of the 755 or 644 permissions will allow any Linux user on the server to read anyone’s website files.
      Those should be 750 and 640 max, unless you really want to grant access to all Linux users on the system
      (which in the context of website files, I think is a case of “never”).

      In the section “Your user as owner” now it’s incorrect the other way around.
      The files are now owned correctly by the dedicated “website user”, but he does have to identify
      which directories have to be written to by the Apache user www-data. Likely so because the question itself is about
      Apache running with mod_php, which is now more or less deprecated way to run PHP in the first place.

      As for the referenced answer. Only when you have 3 users, or when you’re using Apache+mod_php – only then setfacl may be applicable. Otherwise using it is only a patch on top of an often incorrect simple permission model of chown/chmod.

      If you’re running PHP as an Apache module, then there are 2 users, but there is a more severe disconnection between who often manages/uploads the files (SFTP user, human being), and who reads/executes PHP scripts among those files (www-data, Apache user).
      The default Linux mask usually results in the new directories/files created with group chmod that allows for reading only.
      But since in this setup sometimes directories are being created by the webserver user (as a result of running scripts/web installs, etc.),
      those created directories only end up being readable to the human being who cannot change/delete such directories.

      Then (again, for Apache+mod_php) the solution can easily be to set umask 002 for the Apache, e.g.:

      echo "umask 002" >> /etc/sysconfig/httpd
      

      And then all the files/directories created by it will be readily writable to the www-data group. And to finalize this approach for this kind of setup, you’d do the magic part as described above, just in the other way around: add the SFTP user to the www-data group. That’s it, no setfacls.

      The setfactl is great when there are really three users. Having three distinct users working on the same set of files pretty much introduces requirement to use it because chmod has only 3 bits and the last bit is for “all others”.

      I say, why would you want to use setfacl if you haven’t managed to reap the value of even the first 2 bits of the basic Linux permission system.

      Reply
  4. Amit Saini

    Hello Danila Vershinin,
    Thanks for shared this useful information. I have installed magento2 on private server wth ubuntu and nginx. I have created a new user and make changes as per you guide. But I want to know by which user I need to run the command process? Because magento root files owner is new user that I have created, but this user has not root user permissions. So, for commands another user called “ubuntu” is used or not?

    Reply
    • Danila Vershinin

      It’s pretty straightforward: for interacting with your website, like running SSH command magento or working with files over SFTP – use your website user.

      Use your ubuntu user (or any other user with sudo privileges) only for administrating your server. That is, for example, installing or upgrading server packages. Do not use your ubuntu user for interacting with your website files.

      Reply
  5. CM

    GREAT explanation!!!
    It helped me a lot.
    Thanks

    I believe there’s one additional setting required:

    The same value assigned to ‘listen’ in the pool settings file must be assigned to ‘fastcgi_pass’ in the Nginx Server Block.

    PHP-FPM pool config:
    listen = /var/run/php-fpm/example.com.sock

    Nginx Server Block
    fastcgi_pass unix:/var/run/php-fpm/example.com.sock

    (using unix socket in this example)

    Reply
  6. iddqd_x

    Hi Danila! Your post is the clearest I’ve seen on the net. Everything works fine, but I don’t understand how to safely give additional user account access to the website files. All files owned by example:example, I’ve added user foobar and added him to example group (nginx user also in the group example). So in this case I should use chmod 760 right? How safe this configuration? Or it can be achieved in another way? And probably I should also set SGID (sticky bit) attribute? Thank you.

    Reply
    • Danila Vershinin

      I’d say when you really need to give access to another account (besides the site user and nginx), then use setfacl (extended ACL for Linux).
      This will let you tune access beyond the bits provided by chmod.

      However, if it’s being done for the sake of “adding developers” for a website, or other project management, this is still bad.
      For this, a git repository is most appropriate. Developers should not have access to the live system.

      Reply
      • iddqd_x

        I’m really appreciate for your so fast answer. The situation is pretty standard. I have one user “example” (it owns all files and fpm runs by this account). And every developer use this single account to access ssh. Each developer has its own git account to push/pull changes. But this situation is not secure by design when developers user same ssh account.

        So my goal is to restrict access to “example” account and provide unique ssh account for each developer and keep the owner of the files by “example” user/group. What is your opinion about “-o” flag for “useradd” command which creates user account with non-unique UID? I suppose that would do the trick but how safe it is. In this case I don’t need to set higher chmod as developers accounts will have same UID/GID as “example” user.

        Reply
        • Danila Vershinin

          Likewise, my answer is the same. Don’t let developers SSH at all. There should not be “their account”, neither they should access the live account. If you have the time to puzzle your head with how to give them access, you likely have also the time to set up website updating through git hooks. A good developer SSH account on your server is none 🙂 The only thing they would have access to is git and nothing else. Development by a sane developer happens in their local machine. Not on a server of any kind (not even on dev server – that is for integration/testing).

          Reply
  7. Dibs

    So, I did this, though I am not certain I FULLY understand it…

    All seems to be running fine but nginx -t throws some errors now.

    These are permission issues on /var/log/nginx/error.log and my letsencrypt certs – I don’t really understand this as user:nginx is still running the web server?

    Reply
    • Danila Vershinin

      What are the exact word to word errors you receive?

      Reply
      • Dibs

        My error was being a moron and running nginx -t without proper privledges

        Reply
  8. Phil T.

    This means “webserver user” has no write access to ‘wp-content/*’, right? So, is sftp or ssh required for plug-in management etc in WordPress?

    Reply
    • Danila Vershinin

      No, it means webserver user (nginx) can read to the wp-content, but can’t write; and website user (example) can do both.
      Both SSH and WP-admin management of plugins will be functional.

      Reply
  9. Jake

    How would you apply this method to Apache?

    Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: