I am trying to set up a multi-user FreeBSD server using nginx.

Here are my steps so far:

In my /usr/local/etc/nginx/nginx.conf file I set user www www; so that nginx acts as the www user, part of the www group. I also set user = www and group = www in my /usr/local/etc/php-fpm.conf.

What I want to achieve is that I (as the administrator) can add users to my system and create a folder for them (and of course the according server entry in the nginx.conf) in /usr/local/www for them to use, a bit like a shared hosting environment (without any automatic setup).

After installing nginx and php I created a first test user anon, and created a folder for him /usr/local/www/anonsite.

I then performed chown anon:www anonsite to make him the owner, and set the group to www, permission of the folder then looked like this: drwxr-xr-x 3 anon www 4 Apr 11 22:00 anonsite .

Creating a info.php in this folder as anon and pointing a browser to it now works. I then tested downloading and extracting grav, however it will only show a blank page (because of my wrong permission setup, I assume). If I change php-fpm.conf user=anon, it works as intended, or alternatively using chmod -R g+w /usr/local/www/anonsite, after extracting the the downloaded grav folder, will make it work as well.

And this is where I am stuck currently and can't wrap my head around. Both of these "fixes" seem wrong or bad practice to me. If I compare my setup to a shared host provider I use, my webroot folder there only has drwxr-x--- 5 username apache 4096 Apr 2 05:00 username permissions, and after extracting a grav test setup it will work right away (is this because of the way Apache works, maybe?).

Could someone explain to me why that's the case and maybe walk me through the steps to properly set this up, or what I am doing wrong?

Is the approach I tried considered bad practice overall?


nginx will likely be able to read the files as needed withing being explicitly setting the file's group owner as the nginx user
and for php-fpm the nginx user can simply be set as the listener

user = user1
group = use1

listen = /var/run/php-fpm-user1.sock

listen.owner = www
listen.group = www

pm = dynamic 
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3



user www;
worker_processes auto;
worker_rlimit_nofile 8192; # should be bigger than worker_connections
pid /run/nginx.pid;

events {
    use kqueue; # No epoll on FreeBSD
    worker_connections 8000;
    multi_accept on;

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 30; # longer values are better for each ssl client, but take up a worker connection longer
    types_hash_max_size 2048;
    server_tokens off;

    # maximum file upload size
    # update 'upload_max_filesize' & 'post_max_size' in /etc/php5/fpm/php.ini accordingly
    client_max_body_size 32m;
    # client_body_timeout 60s; # increase for very long file uploads

    # set default index file (can be overwritten for each site individually)
    index index.html;

    # load MIME types
    include mime.types; # get this file from https://github.com/h5bp/server-configs-nginx
    default_type application/octet-stream; # set default MIME type

    # logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # turn on gzip compression
    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;

    # disable content type sniffing for more security
    add_header "X-Content-Type-Options" "nosniff";

    # force the latest IE version
    add_header "X-UA-Compatible" "IE=Edge";

    # enable anti-cross-site scripting filter built into IE 8+
    add_header "X-XSS-Protection" "1; mode=block";

server {
    #listen 80;
    index index.html index.php;

    ## Begin - Server Info
    root /home/user1/www/html;
    server_name localhost;
    ## End - Server Info

    ## Begin - Index
    # for subfolders, simply adjust:
    # `location /subfolder {`
    # and the rewrite to use `/subfolder/index.php`
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    ## End - Index

    ## Begin - Security
    # deny all direct access for these folders
    location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; }
    # deny running scripts inside core system folders
    location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
    # deny running scripts inside user folder
    location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; }
    # deny access to specific files in the root folder
    location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; }
    ## End - Security

    ## Begin - PHP
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm-user1.sock;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    ## End - PHP

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.