Configuring nginx, php5-fpm and user permissions

“Nobody can hurt me without my permission.” Gandhi*
*(and anyone who has ever used Linux)
I recently moved the blog over to a pretty cool new server config, the same kind that some of the really big boys like Mashable, WordPress.com, WPengine and PHPFog use. What started off as a simple exercise to tune up my existing hosting environment to cope with the increased traffic we’re getting lately turned into a full month long exploration of how to tune the hell out of a VPS for optimal performance for extremely high load and in particular extremely high load with WordPress. (Sidenote: I find it hard not to fall down the rabbit hole once I start digging into anything I’m not currently necessarily very skilled in – a month later I know a lot of VPS tuning kung fu 🙂

Anyway, even though it’s not directly related to WordPress themes, I’ll be writing a detailed blog post series on my experiences tuning this VPS and WordPress over the next couple of weeks. It might not be directly theme related, but it is definitely indirectly related and we’ll show in future a blog post that your theme choice and theme development practices can have a massive impact on your websites performance. So fuck it I AM on topic.

Anyway one of the final pieces of my VPS config puzzle was figuring out the best way to configure Nginx and PHP-fpm to play nice with multiple websites requiring individual user accounts on the same VPS. On your average shared host, most hosts are running some variant of cPanel/WHM or Plesk. These configs generally have php running under Apache with suEXEC and/or suPHP which allow php to execute under individual user accounts rather than a system user like www-data, php or httpd. This has many benefits from the host’s perspective as it allows for more finely grained control over individual system processes. More importantly, it is a much more secure setup. It’s also one of those rare occasions where security and usability don’t conflict! Why? One of the biggest pain in the asses for people running WordPress under a system account like www-data is that you inevitably end up with one or all of the following predicaments:

  • You need to enter ftp account details during the plugin install process which is a pain and may not be an option if your host has disabled ftp in favour of ssh/scp access.
  • WordPress automatic updates are a major pain in the ass as it involves setting world writeable permissions to the entire root of your WordPress website – NOT a nice option.
  • Image uploads, permalink structure changes and any other WordPress function that requires writing to the disk become a nuisance as it generally means having to go into the shell and setting world writeable permissions on specific files and folders which is not only a pain but is also insecure.

Common but insecure and messy nginx/php-fpm configurations.

While those of your running suPHP don’t have to suffer from these problems, those of us who have moved over to nginx and PHP-FPM don’t have an equivalent to suPHP and so are left searching for an alternative configuration. There are many posts floating around the web suggesting numerous variations on the same theme:

  • Chown your webroot to the system user www-data. Add <myuser> to the same group as the user which executes your php scripts e.g. www-data. This setup makes the problems above go away but is inherently insecure. Basically avoid this if you can. Should a hacker be able to upload a php script they will be able to do all sorts of nasty things. Additionally, you will probably have problems transferring files via ftp/scp under the <myuser> account without further messy permissions configuration.
  • Chown your webroot to <myuser> and add www-data to the same group as <myuser>. In this scenario <myuser> remains the owner of the files which can solve the ftp/scp permissions issues but basically leave you open to the same security problems highlighted above. Don’t do this either.

A better more secure and usable nginx/php-fpm configuration.

While php-fpm doesn’t have an equivalent to suPHP, the good news is that it doesn’t need one. The process manager configuration options included with php-fpm are immense and very flexible. Is was during my research of these that I stumbled upon a few posts which provided the basis for the following configuration. The good news is that once this is in place you will have all the benefits of suPHP in that you’re scripts will run under a user account of your choice and you won’t need to set permissions on individual files/directories AND you won’t have any ftp/scp transfer issues.

So let’s crack on with the setup:

Step 1: Create user account

Create a new user account. I’m on Ubuntu so I’ll use the following command:

[sourcecode]/usr/sbin/adduser myusernamehere[/sourcecode]

Step 2: Create www directory

As part of the user creation process on Ubuntu a home directory will be created. I suggest you create a “www” directory under that account. You will be configuring your virtual hosts to sit in that location rather than your typical /var/www/ or /srv/www location. This will allow you to setup individual accounts for different users and different websites who will then be only able to access their own accounts. Perfecto.

Step 3: Create website directory structure

Right next step should be to create your virtual host. First, create a few subdirectories under www as follows:

[sourcecode]
mkdir www.mywebsite.com

cd www.mywebsite.com

mkdir logs

mkdir public_html[/sourcecode]

Step 4: Create your nginx vhost

Next, you’ll need to create your nginx conf file for www.mywebsite.com. I’m not going to cover nginx vhost configurations in this post (that will be part of the main post series) but for anyone who knows how nginx/php-fpm works the main difference here is that we are going to pass php requests to a separate process pool than what you might be used to. php-fpm is very flexible in how it handles php process pools. We are going to setup a separate process pool just for www.mywebsite.com in Step 5. For now, amend your vhosts file php block as follows:

[sourcecode]

# Pass PHP scripts on to PHP-FPM

location ~* \.php$ {

try_files       $uri /index.php;

fastcgi_index   index.php;

fastcgi_pass    127.0.0.1:9001;

include         fastcgi_params;

fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;

fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;

}

[/sourcecode]

Spot the difference? We are now telling nginx to pass php requests to php-fpm on port 9001 instead of 9000.

Step 5: Configure php pool

On Ubuntu 10.10 php5-fpm creates a series of handy configuration files that makes is real simple to create a separate php process pool for individual user accounts. I set one up as follows:

[sourcecode]

cp /etc/php5/fpm/pool.d/www.conf mywebsite.conf

[/sourcecode]

Crack open mywebsite.conf and edit some key variables as follows:

[sourcecode]

; Start a new pool named ‘myuser’.

[myuser]

listen = 127.0.0.1:9001

user = myuser

group = myusergroup

[/sourcecode]

You should also configure your child processes accordingly. I keep mine dynamic and keep  a small number of children for each pool rather than having a large number of children in a main default process pool. I generally work off a base of 5 children per website but you will need to setup your config based on your specific environment.

Restart nginx and php-fpm and you should be up and running!

[sourcecode]

/etc/init.d/nginx restart

/etc/init.d/php5-fpm restart

[/sourcecode]

That’s it!

I’ve been testing this config for a few days and everything is working swimmingly well.

I’d love to hear anybody’s feedback and comments with regard to their experiences with this config.

Stay tuned for more VPS tutorials in the near future.


Comments

31 responses to “Configuring nginx, php5-fpm and user permissions”

  1. Hello,
    Thanks for the post. Very useful. I have tried to follow this solution but in a more advanced way, i follwed a tut here :
    http://publications.jbfavre.org/web/php-fpm-apps-server-nginx.fr

    The author is proposing to chroot the directory where the vhosts are located to isolate them from other apps hosted on the server.
    Everytime i uncomment the chroot line and arrange the settings accordingly, i have a 404 error. Did you try the chroot method ?

    1. Hey yaz,

      that’s looks like a nice config. I haven’t gone down the chroot just yet as I haven’t needed to – probably overkill for what I need. Those kind of 404’s are quite common when permissions haven’t been set right or else the php process is having trouble finding the docroot. You should check your logfiles. If there’s no entries it would suggest a permissions issue.

      Ed

    2. Hello,
      I’m the author of the tutorial you’re refering to.
      Yes, chroot everytime you can. But…
      1. If your app has to connect outside, you’ll have to copy all libraries into chroot (man ldd)
      2. Remember NGinx pass request to PHP-FPM… from NGinx’s point of vue. For exemple, if you have:
      location /somelocation {
      root /absolute/path/to/php/app
      [fastcgi stuff]
      }
      And PHP-FPM chrooted in /absolute/path/to/php/app

      Then when receiving request, PHP will look for /absolute/path/to/php/app/absolute/path/to/php/app/file.php

      And that’s why you’ll get a 404 error. The solution is either to modify FastCGI params (SCRIPT_FILENAME & so on), or to modify root directive in NGinx.

      AFAIK, this has nothing to do with wrong permissions.

      Regards,
      JB

      1. Hey JB,

        Thanks for stopping by! and thanks for clarifying what was probably going on for yaz.

        Ed

  2. I had this config for a while, but PHP5-FPM’s usage of child processes can be inefficient… and caused more problems than I really wanted to deal with…ultimately I moved over to lighttpd and haven’t looked back. I’m always experimenting though. I may try this again on a different server with some more horsepower. 🙂

    1. Hey Kevin,

      Thanks for your comments. What aspect of the child process config were you finding inefficient? I’m not seeing that at the moment but am curious for future deployments. The child process config is working fine for me on a server with 512MB of ram.

      I have a number of sites running lighty too and really like it. Cherokee is next on the experimentation list 🙂

      Ed

  3. I would recommend using a socket instead of introducing TCP/IP overhead on the proxy_pass parameter.

    So, have PHP-FPM listin on /var/run/whatever.sock and have proxy_pass set as unix:/var/run/whatever.sock

    1. Hi Ryan,

      Thanks for the tip – I’ve been meaning to experiment with the socket option and will do so in the next few days hopefully.

      Ed

      1. interesting numbers there Rob – as one of your commenters mentioned (although in quite a rude way!) I think 1000 concurrents would be a more comprehensive test – but good to know theres still not much different at 100 concurrents.

  4. Hi Ed,

    Great post. Just started using NGINX and needed the user configuration for security issues as well. Had a lot of problems in the start with memory usage where my Server got really laggy, but sorted that out by setting max_request and downsizing children and servers. I use sockets since it’s recommended in the nginx wiki and it works really well. Have to play a bit more with it to get optimal performance though.

    Cheers Chris

    1. Hey Christian,

      apologies for the long delay replying to your comment – it slipped through akismet – yes sockets do seem to be the way to go for really pushing optimal performance. Good luck with the rest of your tuning!

      Ed

  5. […] WordPress with Nginx, Varnish, APC, W3 Total Cache, and Amazon S3 (With Benchmarks)3、Configuring nginx, php5-fpm and user permissions4、Nignx Configuration For WordPress // SyntaxHighlighter.all() _ // ]]> […]

  6. Hello,

    Don’t you guys haven’t faced any problems accessing static files? I followed this post (but used socket instead of TCP) and PHP scripts are doing fine now. However, I can’t access static files through browser anymore, as Nginx worker process is still running as www-data/www-data, but static files are myuser/myuser now.

    How should Nginx be able to access these files?

    (btw, great post!)

    1. Hey Ilkka,

      thanks for the feedback!

      You should be able to see static files just fine. The way this config works if memory serves correct is that it checks to see if the static file exists first before passing php requests over to php-fpm. I could be wrong but this is what I understand it to be. For example you can see my robots file is served just fine with this config

      http://themesforge.com/robots.txt

      Ed

      1. Thanks for quick answer Ed!

        I just wonder how should I chmod all the files now? My Nginx doesn’t have permissions to get that file anymore. If I chmod static file to 644 it works, but if I chmod to i.e. 770 my Nginx doesn’t have permission to get the file.

        I just realized… Maybe I should chmod all the files to 644 and folders to 755. Is that correct?

        1. sorry this reply was not as quick 🙂

          Did you get this sorted in the end? Normally what I would do it ensure that the files are owned by the user you created for the php process pool instead of www-data.

          e.g. chown -R myuser:mygroup /public_html

          1. Yes, also my files are chownd myser:mygroup but the problem with it is, that Nginx (the worker process that gets the static files straight) is running always as www-data:www-data, no matter what pool we are using with php-fpm. Therefore I ended up giving 755 for directories and 644 for files.

            At the moment nothing is writable for world and different sites are separated by different users, using different pools. Sounds reasonable good to me 🙂

            1. Wouldn’t chmod 755 be a security risk if a malicious php file would scan the directories and find another users home directory?

              1. it depends Alex. if the script was being executed in the same web root then it would have execute access on files within that root only. It won’t have any permissions to do anything in other web roots or any other OS directories outside of /home/myuser/ Of course, if a user is able to get a malicious php script on to your server in the first place that’s a bigger problem 🙂

  7. Ed, if I changed the apache’s (username WordPress uses to upload files) default folder (chgrp -R apache /srv/www) to /srv/www (where I have my WordPress websites) and chmod it (chmod -R g+w /srv/www) seems to be working (I can upload via WordPress)!

    I also created a link in my /home/alex/www directory of the /srv/www so I can upload files via FTP (I can’t upload or edit anything in root or /srv/www only in /home/alex/www)

    However, I wanted to ask you, are there any security risks hidden with these operations?
    Thanks!

    1. Hey Alex,

      Short answer is there are ALWAYS security risks! Some people will tell you not to set the owner to be nginx/apache/www-data as it is a security risk but in my experience I think it’s ok as long as you’re not in a shared hosting environment. I think as long as you’re keeping WordPress up to date and don’t allow non-secure scripts (like older versions of timthumb) you should be ok. It’s a hell of a lot more secure than setting 777 permissions all over the place. If you wanted you could restrict your apache ownership to just directories like /uploads /themes /plugins – but in my experience it’s overkill.

      Ed

      1. That’s what I was thinking as well! I could make it work with 777 but I spent 2 hours searching for a better solution!

        Thanks for the reply Ed!

  8. Worked great. No issues. Thanks for the article!

  9. Is it a risk to let php run as a user? I have a setting where php-fpm and nginx are both www-data:www-data and file permissions are 6750 for directories and 0640 for files. This means that a user can ssh into system and edit files. Nginx and php need only read access (and execute bit on directories). In the folders where cms need to create files (eg. uploads) there is permission for 6770, and nginx is configured so that it will only serve static files from those folders, not execute any scripts.

    Now, when user is added to www-data group, user can delete and modify files created by www-data. In a shared environment this might lead to problems, when user could delete files from other users upload directories.

    1. In a shared environment I would suggest it is a security risk – in a VPS environment – less so.
      It sounds like you have a pretty good secure setup msh.

    2. grave-digging, but do not allow that user to login then:

      eg. shell can be set to false, true, nologin.

  10. Hello, thanks for tutorial.
    In debian squeeze, Bad gateway…
    Where do i put the file mywebsite.conf?
    in /home/user/www.website.com/
    or into /home/user/www.website.com/mywebsite.conf?

    Can you post your site enabled?
    thanks

    1. In Debian these files are usually stored in /etc/nginx/conf.d/mywebsite.conf

      Remember, that in nginx there is no such thing like .htaccess or other kind of runtime configuration. After every change is made in config files nginx needs to be restarted. So it makes no sense to give write access to mywebsite.conf for non-root users.

  11. Hi, nice tutorial you have there.
    I follow your tutorial and I think manage to do the same.

    the weird part is when I install new themes on wordpress (using upload in wordpress) the themes won’t show up properly. The images missing and css won’t load up.
    I try activate the build in themes, it works ok.
    I compare the files and directory permission between the two themes, but everything looks the same.

    Do you have any idea what went wrong here? Thank you

  12. […] wanted to have some seperate users still, rather than resorting to a single monolithic user. Setting up user pools for Nginx seems like the perfect […]

Leave a Reply

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