Nginx web server with php@FastCGI support part I

Find out why Nginx beats Apache in performance and memory usage. Global configuration, FastCGI, and virtual hosts. Take a look!

nginx httpd server

Nginx is a lightweight HTTP server and reverse proxy. It can also act as a load balancer, which is why it's often called an HTTP router. It's a serious alternative to overloaded Apache. In a production environment, nginx impresses with its performance and low memory usage, which is an advantage for both large sites like wordpress.com and small ones like a weak 128MB VPS.
What works against nginx is the lack of .htaccess file support and the syntax of the rewrite module, which is somewhat different from the mod_rewrite syntax. The lack of .htaccess support means that rewrite directives or other things normally handled in .htaccess must be introduced into the server configuration by its administrator.
The advantage nginx has over popular Apache comes from the model of handling incoming connections. In Apache, every new connection requires starting a new process (mpm-prefork) or thread (mpm-worker) to handle the incoming request.
With nginx, we're dealing with an event-driven model where many connections are handled by the same process, which reacts to events such as a new request from a client, a response from the application server, etc. If nothing is happening on one connection, the nginx process can handle another, active connection. An Apache process (thread) then waits idly.
Nginx creates 1-n... processes (depending on needs and hardware); each of these processes can handle several dozen/hundred thousand connections. So much for the theory, in short.

At the outset, I want to note that I won't go into the process of installing nginx itself - as we know, that depends on the distribution (package) or an individual configuration (compiling from source).

  • Global server configuration:
  • main configuration file - nginx.conf
  • 
    user www-data;
    worker_processes  1;
    
    error_log  /var/log/nginx/error.log;
    pid        /var/run/nginx.pid;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;
    
        access_log  /var/log/nginx/access.log;
    
        sendfile        on;
        tcp_nopush     on;
    
        keepalive_timeout  0;
        keepalive_timeout  65;
        tcp_nodelay        on;
    
        gzip  on;
    
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
    }
    
    and a short description of this file:
  • user www-data; - configuration of which permissions the server will run with
  • worker_processes 1; - number of running processes; the rule of thumb here is: number of cores = number of processes (if needed)
  • worker_connections 1024; - maximum number of connections within 1 process
    Thanks to the two parameters above, we can theoretically estimate the maximum number of simultaneously served clients: max_clients = worker_processes * worker_connections , however keep in mind that when serving dynamic pages, e.g. in PHP, one client opens two connections (FastCGI), and then: max_clients = worker_processes * worker_connections / 2
    and even more safely, divide by 4.
  • include - the entire configuration can be placed in the nginx.conf file, but for greater clarity and order, especially if it's extensive (e.g. many domains), it can be placed in separate files.
  • In Debian and related distributions, the file layout model is borrowed from Apache (directories: sites-available, sites-enabled).
    Sticking to the Debian convention, I'll delegate additional configuration to separate files.
  • Nginx serving static content:
  • let's create a file called example in /etc/nginx/sites-available, containing:
  • 
    server {
    	# Directive specifying the address and/or port the server listens on
    	listen       80;
    	
    	# Directive assigning names to virtual servers
    	server_name  example.com www.example.com;
    	
    	# Logs for the example.com domain
    	access_log  /var/log/nginx/example.access.log; 
    	error_log /var/log/nginx/example.com.error.log;
            
    		location / {
    		root   path_to_our_site_directory;
    		index  index.html index.htm;    
    		}  
    
    }
    
  • now we create a symbolic link: ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
  • then restart nginx: /etc/inid.d/nginx restart
  • If we don't have a domain, we don't use the server_name directive; if however we want to have several sites on one IP, we can expose them on different ports:
    
    # site_1:
    server {
    	listen       80;
    	
    	# ....
    
    		location / {
    		root   /var/www/site_1;
    		index  index.html index.htm;    
    		}  
    
    }
    # site_2:
    server {
    	listen       81;
    	
    	# ....
    
    		location / {
    		root   /var/www/site_2;
    		index  index.html index.htm;    
    		}  
    
    }
    

    While configuring nginx I ran into a rather troublesome situation. Let me present it with a concrete example:
    Imagine we have the domain example.com, with 4 subdomains: main.example.com, mail.example.com, users.example.com, qwerty.example.com . The first three are virtual hosts handled by nginx, while the last one, qwerty.example.com, is not associated with any virtual host configuration. And here's where the problem appears - when we type http://qwerty.example.com/ into the browser, one of these three sites will be displayed (probably depending on the order in which nginx does the includes).
    In the nginx.conf file, we insert a server block before the include directives:

    
    server {
        listen          80 default;
        server_name    _ ; # Catch all
        
        return 444;  # Code 444 closes the connection without sending headers.
    }
    

    Of course, this is just a fraction of nginx's capabilities; I haven't mentioned anything about reverse-proxy configuration or its strong support for regular expressions...
  • Nginx with PHP support via FastCGI part II