Adding nginx to the LAMP stack for performance

The Apache HTTP Server is a very flexible server that can be used with almost all open source projects with little to no customisation due to Apache’s ubiquity within the community, however being a jack of all trades unfortunately means it isn’t necessarily a master of everything. One of the more commonly cited issues with Apache HTTP, is that its resource usage is more considerable compared to other options available, which can lead to issues when under heavy load.

In order to obtain the benefits of lower resource usage, there are plenty of other options available such as the choice for this article of nginx as well as others such as Apache Traffic Server or lighttpd, however making use of these options as your server software of choice may lead to compatibility issues with your applications. To avoid any of these issues, this article outlines how to add nginx into the mix, without losing Apache.

In the end, we will have nginx responding to all public access requests for your website, selectively serving back any static content (such as CSS, Javascript and Images) and proxying a request through to Apache (which is not directly accessible by the public). This gives us the benefits of nginx’s lower resource usage and faster response times under heavy load to serve the static content (which depending on your site is typically be the bulk of the content), without having to make any changes to our applications, which are still run under Apache.

As usual, the remainder of this article assumes a Centos/RHEL based server. In addition, for this scenario, we assume that Apache is already installed and is publically serving your live website(s).

Step 1 – Install nginx

nginx helpfully publishes specific packages from its own repositories and provides documentation, detailing the below:

CentOS:
[nginx] 
 name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ 
 gpgcheck=0 
 enabled=1
RHEL:
[nginx] 
 name=nginx repo
 baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
 gpgcheck=0 
 enabled=1

Due to differences between how CentOS, RHEL, and Scientific Linux populate the $releasever variable, it is necessary to manually replace $releasever with either “5” (for 5.x) or “6” (for 6.x), depending upon your OS version.

After the repo has been installed, simply complete a yum installation of nginx:

# yum install nginx

**It is important at this step not to try to start nginx. **Although it will fail as port 80 will already be in use by Apache, you should not try to fix it yet, as we need to complete the configuration before doing so.

Step 2 – Configuring your reverse proxy

In order to set up the desired functionality, we are going to configure nginx as a reverse proxy to Apache, which we will then re-configure so that Apache listens on port 8080 and nginx listens on port 80. There are two (or more, depending on the number of sites you have) main configuration files that are used in this setup, which are outlined below.

Server wide configuration

The below configuration file sets up the settings for all websites served by nginx. This example file should be modified to your requirements as detailed in the comments and then placed in the /etc/nginx/nginx.conf file.

#Set the user that nginx should run as. Almost always "nginx" for standard installations.
 user nginx; 
 
 #Set the number of processes that should be used by nginx. General rule of thumb is one per CPU cores. Alternatively, this can be set to "auto" to attempt to automatically set this.
 worker_processes 1; 
 
 #Set the location for error logging. 
 error_log /var/log/nginx/error.log warn; 
 
 #Set the location for the nginx pid file. 
 pid /var/run/nginx.pid;
 
 #Limit the number of connections that nginx will serve. 
 events { 
  worker_connections 1024; 
 }
 
 http { 
  #Configure the mime types and the default. 
  include /etc/nginx/mime.types; 
  default_type application/octet-stream;
  
  #Configure logging. 
  log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
 
 #Enable various performance settings. 
  sendfile on; 
  tcp_nopush on; 
  tcp_nodelay on; 
  keepalive_timeout 65; 
  gzip on; 
  gzip_disable "msie6"; 
  gzip_comp_level 6; 
  gzip_min_length 1100; 
  gzip_buffers 16 8k; 
  gzip_proxied any; 
  gzip_http_version 1.1; 
  gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss; 
  proxy_buffering on; 
  proxy_cache_valid any 20m; 
  proxy_cache_path /var/www/cache levels=1:2 keys_zone=shared-cache:8m max_size=2000m inactive=600m; 
  proxy_temp_path /var/www/cache/tmp; 
  proxy_buffer_size 4k; 
  proxy_buffers 100 8k; 
  
  #Pre-requirement for enabling multiple origin requests in the event of failed requests. 
  proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
 
  #Set the location for additional configuration files - i.e. the location of all the per site configuration files. 
  include /etc/nginx/conf.d/*.conf; 
 }

You will also need to create the below directories referenced in the above for the cache files:

# mkdir /var/www/cache && mkdir /var/www/cache/tmp 
 # chown -R nginx /var/www/cache

Site level configuration

The below configuration applies on a per site basis and therefore you should adjust the below file contents to suit your needs for each of your sites.

server { 
  #Set the port for site access 
  listen 80; 
  
  #Set the name of virtual host. Pre-appended "." character covers www. and non/apex domain. Extra values can be added separated by a space 
  server_name .example.com;
 
  #Set the location for access logs 
  access_log /var/log/nginx/access.log main;
 
 #Set various headers to be passed to Apache. Useful for logging in Apache, determining real IPs in application code and more. 
 proxy_set_header Accept-Encoding ""; 
 proxy_set_header Host $host; 
 proxy_set_header X-Real-IP $remote_addr; 
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto $scheme;
 
 #Set the location for nginx to proxy requests to. This should match the IP and Port combination that Apache will be set to. Additionally we set the cache to be used for the static files served by this configuration. 
 location / { 
  proxy_pass http://127.0.0.1:8080; 
  proxy_cache shared-cache; 
  proxy_cache_valid 200; 
 } 
 }

Now the configuration is in place, you can run a configuration test by nginx, by running the below command.

# service nginx configtest

Any issues shown should be addressed before proceeding.

Updating Apache configuration

In order to allow the setup to function, we need to change the port that Apache is listening on in the Apache configuration file. We also need to enable some additional configuration in Apache to fully utilise the configuration we made in the above files, such as ensuring keepalives in Apache are enabled and also to enable Apache logging of the additional fields if specifically required (as nginx is logging, you may wish to disable Apache logging entirely).

To do this, set or adjust the following directives in your Apache configuration file(s) as appropriate:

KeepAlive On 
 MaxKeepAliveRequests 1000 #
 KeepAliveTimeout 60 
 LogFormat ""%{X-Real-IP}i" %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined 
 Listen 8080 
 <VirtualHost [URL or wildcard]:8080>

Once the above directives have been adjusted, test the new configuration for any issues and resolve them.

# apachectl -t

If there are no issues, proceed with restarting Apache and starting nginx.

# service httpd restart && service nginx start

Your website should now be browseable on the internet, being served by nginx. Should you wish to add SSL support to your server, you may want to read my follow up article on SSL Best Practice for nginx.

Image credit: Nathan E

Leave a Reply

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