Konfigurasi nginx untuk virtual hosting dengan PHP

From SmartCommunity

Jump to: navigation, search

nginx is a lightweight web server. The main difference between nginx and Apache is that nginx is using asynchronous IO to handle requests when Apache is using process spawning. This allows nginx to be smaller and faster. nginx has been developed for highloaded systems. It supports load balancing, caching on the web server level and many other features.

Contents

Setting up nginx

Nginx comes with most Linux and BSD distributions, but it is highly recommended always to use the latest available version from the official site. We’ll begin with compiling nginx from source code. First of all, get nginx’s sources from the official site. Be sure to download the latest version, not to copy-paste link listed in this example!

[root@rainbow-dash ~]# wget http://nginx.org/download/nginx-1.0.4.tar.gz
--2011-07-10 21:59:08--  http://nginx.org/download/nginx-1.0.4.tar.gz
Resolving nginx.org... 81.19.68.137
Menghubungi nginx.org|81.19.68.137|:80... terhubung.
Permintaan HTTP dikirimkan, menunggu balasan... 200 OK
Besar: 661444 (646K) [application/octet-stream]
Simpan ke: `nginx-1.0.4.tar.gz'

100%[================================================================================>] 661.444      167K/s   dalam 4,1s 

2011-07-10 21:59:13 (157 KB/s) - `nginx-1.0.4.tar.gz' disimpan [661444/661444]

[root@rainbow-dash ~]#

And then extract the tarball and enter the source code’s directory.

# tar -xf nginx-1.0.4.tar.gz
# cd nginx-1.0.4

Before you continue, you have to install some dependancies. We are going to build nginx from the source code, so, we probably need the compiler and some header files to be installed. For pure CentOS installation you usually need such set:

# yum install gcc pcre-devel zlib-devel openssl-devel

PCRE enables URL rewriting, zlib provides compression, and OpenSSL allows to use HTTPS protocol. If you also need GeoIP functionality, you should install GeoIP development files as well.

# yum install GeoIP-devel GeoIP-data

Finally, we’ll set up environment variable CFLAGS which forces the compiler to use optimizations, because they are disabled by default.

# export CFLAGS="-Os -pipe"

Now you may run configure script. We’ll add some options to enable all nginx’s features, including GeoIP binding (disable it if you don’t need it).

# ./configure --prefix=/opt/nginx --with-zlib-asm=pentium --with-sha1-asm \
--with-md5-asm --with-pcre --with-http_stub_status_module --with-http_gzip_static_module \
--with-http_geoip_module --with-http_ssl_module --with-ipv6 --user=web --group=web

You may add some other modules, like --with-http_image_filter_module, which allows you to modify images “on the fly”, but note that they may need some extra dependancies.

After your configure script finishes, type make to start compiling. It usually takes less than a minute and depends on your server configuration. If your server has many processors (cores), you may run make -jN where N is number of your processing cores + 1.

And run the following commands to install nginx.

# useradd web
# make install

Congratulations, now you have nginx installed onto your system. You can test it by running /opt/nginx/sbin/nginx. Don’t worry that you run it as root user, because you’ve configured an unprivileged account for nginx, so, it will use it for working processes. If you now open your server’s IP in your browser, you’ll see the default nginx’s welcome page.

Configuring nginx

In the next step we’ll prepare our nginx instance for mass web hosting. Open /opt/nginx/conf/nginx.conf in your favorite text editor. It’s full of examples, but we don’t need them because the official documentation is good enough. So, let’s purge that file and leave only some needed things.

user web;
worker_processes 1;
pid logs/nginx.pid;

events {
 worker_connections 1024;
}

http {
 include mime.types;
 default_type application/octet-stream;
 geoip_country /var/lib/GeoIP/GeoIP.dat;
 charset 'utf-8';
 sendfile on;
 keepalive_timeout 65;
 gzip on;
 
 include vhosts/*.conf;
}

Note that the preset GeoIP database installed with “GeoIP-data” package is very old. If you want to upgrade it, grab the newest version from MaxMind’s website.

The directive “include vhosts/*.conf” is very useful. It tells nginx that it should include all files matching “vhosts/*.conf” pattern into the server’s configuration. It’s good, because we can split the server’s configuration into many small files instead of making a huge one. Now let’s create our first custom virtual host. To do that, create a directory “vhosts” in nginx’s “conf” directory and create a file named “default.conf” there.

server {
 listen 80 default_server;
 server_name localhost;
 root /home/ayes/www/localhost;
 index index.html index.shtml;
 ssi on;
}

Save the file. Now restart nginx to ensure that everything is right. The created vhost’s configuration is already enough to serve simple HTML + SSI web sites. Note that nginx takes very small RAM amount for that, so, you may have an extremely basic and cheap VPS for such task. Sure that you’ve also noticed how simple is the configuration syntax. Yeah, nginx is great.

Setting up PHP

PHP does not provide a module with nginx’s native API, but fortunately it’s possible to use universal FastCGI API. PHP’s FastCGI implementation includes FPM (FastCGI process manager) — process management system that allows you to easilly configure your worker settings. FPM is available only in the latest PHP versions (starting from 5.3.4), so, as with nginx, the package provided by CentOS distributor does not suit our requirements.

We’ll begin with fetching PHP’s source code tarball.

# wget -O php-5.3.6.tar.bz2 http://id.php.net/get/php-5.3.6.tar.bz2/from/this/mirror
# tar -xf php-5.3.6.tar.bz2
# cd php-5.3.6

Install some dependancies.

# yum install libxml2-devel libxslt-devel curl-devel libjpeg-devel libpng-devel \
freetype-devel gmp-devel mhash-devel libmcrypt-devel libicu-devel mysql-devel gcc-c++

Now configure your PHP build. We’ll use configuration similar to what we use on SmartCommunity’s server, just a little lighter (without LDAP, SNMP, PostgreSQL and so on). Note that PHP’s configuration is very flexible and there are thousands of possible configurations. You may run ./configure --help to see the full list of available options.

# export CFLAGS="-Os -pipe"
# export CXXFLAGS="-Os -pipe"
# ./configure --prefix=/opt/php --enable-fpm --with-fpm-user=web --with-fpm-group=web \
--with-zlib --enable-bcmath --enable-calendar --with-curl --enable-exif --enable-ftp \
--with-gd --enable-gd-native-ttf --with-gettext --with-gmp --with-mhash --enable-intl \
--enable-mbstring --with-mcrypt --with-mysql --with-mysqli --enable-pcntl \
--with-pdo-mysql --with-pdo-sqlite --enable-sockets --enable-sqlite-utf8 --with-xsl \
--enable-zip --with-pear --with-pcre-regex --with-freetype-dir=/usr \
--with-config-file-path=/opt/php/etc --enable-ftp --with-jpeg-dir --with-png-dir \
--with-zlib-dir --with-openssl

It will take some time. After “configure” finishes, you may run make to compile PHP. It will take some minutes, depending on your server’s hardware. Anyway it will be way longer than compiling nginx, so you may take a cup of tea while it’s running.

After compiling, run make install to install PHP into the target directory. After installing you may safely remove the source directory since it takes lots of disk space.

Configuring PHP

Configuring PHP instance includes 2 steps: configuring PHP processor itself with file named php.ini, and configuring FPM with php-fpm.conf. These files are stored in /opt/php/etc directory. The file php.ini probably doesn’t exist, it means that PHP will use default settings. But you are advised to create your own php.ini there (using some example which can be found in PHP distribution).

Let’s begin with php-fpm.conf. This file describes worker pools. We’ll define 1 pool named “www” with dynamic number of worker processes. Note that you may use static number of worker processes, but if your server gets overloaded in this case, some users will get 502 (bad gateway) error.

[global]
process_control_timeout = 1s
daemonize = yes

[www]
listen = 127.0.0.1:13000
listen.allowed_clients = 127.0.0.1
user = web
group = web
pm = dynamic
pm.max_children = 50
pm.start_servers = 3
pm.min_spare_servers = 1
pm.max_spare_servers = 4
pm.max_requests = 200
php_admin_value[error_log] = /dev/null
php_admin_value[disable_functions] = "system,exec,passthru,popen,dl,shell_exec,proc_open,set_time_limit"
php_admin_flag[log_errors] = on
php_admin_value[max_execution_time] = 60s
php_admin_value[memory_limit] = 128M

We tell PHP to listen on TCP port 13000. This port will be used to interact with nginx. We also disable some functions for better security. You may ask why not to define disable_functions in php.ini. The answer is simple: if you want to use PHP for writing console scripts, it’s better to have different configurations for PHP working as a command-line program and a web server. If you don’t need to use PHP to write console scripts, feel free to define disable_functions in your php.ini.

Now you may start PHP by running /opt/php/sbin/php-fpm. Like with nginx, you may do it as superuser, PHP will automatically start workers using unprivileged user account.

Virtual host configuration

Now you may try to configure a virtual host that can serve PHP scripts. Go to nginx’s “vhosts” directory that you’ve created in one of the previous steps, and edit your “default.conf” file (or create a new file named *.conf).

server {
 listen 80 default_server;
 server_name localhost;
 root /home/ayes/www/localhost;
 index index.php index.html index.shtml;
 ssi on;
 location ~ \.php$ {
  fastcgi_pass 127.0.0.1:13000; # redirect request to FastCGI server
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; # tell PHP path to the requested script
  include fastcgi_params; # some required params
 }
}

Important note: if you create a new file, the directive “listen” should not include “default_server”, just type: “listen 80;”. Only 1 default virtual host is allowed. It is used when nginx cannot find a virtual host with suitable name for the request.

This should be enough for running PHP scripts within your virtual host. Try to put some PHP script into virtual host’s document root and open it in the web browser.

URL rewriting

nginx does not support .htaccess, but it is possible to enable rewriting URLs from the virtual host’s configuration file. Let’s enable rewriting in our vhost example. We’ll set index.php to handle all requests to non-existant files as it is usually done for most programs that use nicer URLs, including WordPress and CodeIgniter applications.

server {
 listen 80 default_server;
 server_name localhost;
 root /home/ayes/www/localhost;
 index index.php index.html index.shtml;
 ssi on;
 location / {
  if (!-e $request_filename) {
   rewrite ^(.+)$ /index.php last;
  }
 }
 location ~ \.php$ {
  fastcgi_pass 127.0.0.1:13000; # redirect request to FastCGI server
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; # tell PHP path to the requested script
  include fastcgi_params; # some required params
 }
}

Securing your web server

Making your web server secure is very important part of any setup. The setup yg we made is already fully working, but it contains some potentially dangerous vulnerabilities.

The first issue is absense of any open_basedir restriction. Any user may access files of any other user, get his MySQL access information, enter his MySQL database and destroy it. If you are not going to provide hosting for real users, you may not worry about it, otherwise you should setup open_basedir. In previous editions of PHP with FPM administrators had to use various complex tricks to set up open_basedir, now this problem has been completely solved. When you add a virtual host, open your php.ini with any text editor and append to the end something like this:

[HOST=example.web.id]
open_basedir = /home/ayes:/tmp:/opt/php/lib/php/PEAR

You may also specify any other common PHP configuration directives here. Be very careful: if www.example.web.id is an alias for example.web.id, you should also add a section for it as well! If you got tired from www prefixes, you may simply disable them in general. Create a new nginx virtual host configuration file named nowww.conf.

server {
 listen 80;
 server_name ~^www\.(.+)$;
 set $target $1;
 rewrite ^(.*)$ http://$target$1 permanent;
}

Just be sure not to add “www.blah” into the configuration files of other virtual hosts.

Another severe problem may be possibility of DoS attacks (TODO).

PHP extensions support

We have built PHP with PEAR and PECL support, so, it is now easy to install PHP extensions. For example, if you need GeoIP functionality on PHP’s level, you may install GeoIP extension. Before running installing, install package autoconf first.

# yum install autoconf
# /opt/php/bin/pecl install geoip

PEAR classes may be installed the same way.

# /opt/php/bin/pear channel-discover pear.twig-project.org
# /opt/php/bin/pear install twig/Twig

Links