CentOS: Using NGinx to serve static files and Apache for dynamic

Apache is a great web-server, but it has a pretty heavy memory footprint. It can get quite restrictive quite quickly, especially if you're on a system will limited resources (given how many people now run on a VPS, and the poor disk IO of these systems it's all the more important - swapping is slow).

The way around it, is to configure your system to use NGinx as a reverse-proxy. Depending how many virtualhosts you have, you can make the changes almost completely transparently within about 10 minutes.

Pre-Requisites

First, we need to be able to install NGinx, which means setting up the EPEL repo (if you already have it enabled, skip this step)

CentOS 5.x

http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm

CentOS 6.x

rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

Now that the repo is installed, we need to install NGinx

yum install nginx

 

Configuring NGinx

Now that NGinx is installed we need to create a VirtualHost (actually NGinx calls them Server Blocks) for each site we are hosting.

nano /etc/nginx/conf.d/virtual.conf
#Insert one of these for each of the virtualhosts you have configured in Apache

server {
listen 80;
root /path/to/site/root;
index index.php index.html index.htm;
server_name www.yourdomain.com yourdomain.com;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;

}

location ~ /\.ht {
deny all;
}
}

This configuration tells NGinx to try and serve the requested file, but to pass the request onto Apache if it's unable to do so. Requests for PHP files should be forwarded automatically. Apache will be told who requested the file in the 'X-Forwarded-For' header.

The final section tells NGinx not to honour requests for .htaccess files as we don't want anyone to see the contents of these.

So, assuming you had the following VirtualHost directive in your Apache configuration

<VirtualHost *:80>
ServerAdmin webmaster@example.com
DocumentRoot /home/example/public_html
ServerName www.exaple.com
ErrorLog logs/example.com-error_log
CustomLog logs/example.com-access_log common
<Directory />
AllowOverride All
</Directory>
</VirtualHost>

You'd want to enter

server {
listen 80;
root /path/to/site/root;
index index.php index.html index.htm;
server_name www.example.com example.com;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;

}

location ~ /\.ht {
deny all;
}
}

Note: the second entry on server_name is essentially the equivalent of Apache's ServerAlias - we're telling NGinx to use that server block for both www.example.com and example.com.

Configuring Apache

The next step represents the point where we risk interruption to service if something goes wrong. We're going to edit Apache's config ready for the switch round. You really shouldn't start this bit unless you plan to finish the rest of the steps in this documentation without interruption - if you edit the config and then leave it, the next time Apache is restarted your sites will appear to be down!

Change the port Apache runs on

 We want users to hit our NGinx installation (otherwise this effort is wasted) but Apache is currently sat on port 80. So we're going to move it to 8080 (given that's the port we specified in the NGinx configuration we created).

nano /etc/httpd/conf/httpd.conf
# Find the following

Listen (someIP) 80
# Change the port to
Listen 127.0.0.1 8080

# Now at the bottom of the file, you'll find your virtualhost directives,
# Change all port definitions of 80 to 8080
# Don't forget the Default virtualhost definition
# <virtualhost *:80> becomes <virtualhost *:8080>

We change the Listen address as we don't want external hosts to access Apache directly, everything should go through NGinx. Ideally, we also want to forbid outside access to port 8080 at the firewall to ensure that the point of entry to our system is restricted to the authorised route - through NGinx.

Start the Services

We've now configured Apache to listen on a different port, so all we need to do know is restart Apache (so that it moves to port 8080) and start NGinx so that it can start handling requests.

service httpd restart
service nginx start

Now if you browse to your site, nothing should have changed visibly. However, if you check the HTTP headers you should see NGinx instead of Apache, checking a phpinfo file should still show Apache as having called the PHP parser though.

 

Additional Considerations

Security

By adding NGinx into the mix, we're increasing our potential attack surface a little - we've now got an extra application to keep patched and up to date (which is why we installed from the repo's and didn't go out-of-band). Although we've hidden Apache away behind NGinx, don't assume it's automatically shielded - if a vulnerability is exploited using a valid request, NGinx will pass the request through verbatim (assuming it couldn't handle itself). What you are protected from, though, is exploits that involve an invalid request.

SSL Connections

Nothing we've done will affect SSL connections, if Apache was configured to listen on port 443, it will continue to do so. However, this also means that all SSL requests will be handled by Apache and so the memory benefits of using NGinx as a proxy won't be present on these connections. It's more than possible (and not particularly hard) to set NGinx up as a reverse SSL proxy, but that's outside the scope of this documentation (although the steps involved are almost identical).

 

CPanel

If your server is running CPanel, you probably won't want to edit everything by hand. In that instance, it may be worth trying the CPanel NGinx plugin at http://nginxcp.com/. Plesk users should find that support for NGinx is included, so long as they are running version 11 or later.