How to Setup WordPress Nginx with FastCGI Caching in CentOS 7

Wordpress NginxIf you plan to run high traffic wordpress blog(wordpress nginx), i would suggest to run it in virtual private server (VPS) or dedicated server together with NGINX FastCGI Caching.

Besides the low memory consumption when using Nginx as a web server, it has a fast performance. When you combine Nginx and FastCGI Caching module, you will further enhance the performance of your web application, including a WordPress site.

This can be an alternative to NGINX + Varnish setup that uses caching technology to accelerate the performance of wordpress site.

Nginx FastCGI module has instructions for caching dynamic content that is served through a PHP backend.

When a web page is cached, repeated requests for the same page with quickly returned to a customer by the nginx web server because it comes from the cache location.

The following tutorial will guide you to setup WordPress Nginx in CentOS 7 VPS. This setup has been tested in linode 4GB and ramnode 2GB VPS.

Prerequisites :
a) Assume that the linux CentOS 7 VPS has been setup properly with Mariadb as a database and Nginx as a web server run together with PHP-FPM.
b) You have domain name for your wordpress site and dns record pointing to your VPS ip address.

www.ehowstuff.local ->

1. To install Nginx in CentOS run the following commands to create Nginx repository file in CentOS.

vi /etc/yum.repos.d/nginx.repo
name=nginx repo

2. To install nginx, php, php-mysql MariaDB server, run the following command :

# yum install nginx php php-mysql php-fpm mariadb-server

3. Configure php-fpm :

Below are the configuration value for 4Gb ram VPS :

# vi /etc/php-fpm.d/www.conf
listen =

listen.allowed_clients =

listen.mode = 0666

user = nginx
group = nginx

pm = dynamic

pm.max_children = 20

pm.start_servers = 15

pm.min_spare_servers = 15

pm.max_spare_servers = 15

pm.max_requests = 500

4. Configure NGINX with FastCGI Caching :

This is the main configuration file for Nginx.

# vi /etc/nginx/nginx.conf
user  nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/;

events {
    worker_connections  1024;

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    log_format blocked '$time_local: Blocked request from $remote_addr $request';
    access_log  /var/log/nginx/access.log  main;

    include /etc/nginx/conf.d/common.conf;
    include /etc/nginx/conf.d/gzip.conf;

    #FastCGI Cache and other configuration options
    include /etc/nginx/conf.d/option.conf;

    #Nginx for WordPress and security
    include /etc/nginx/conf.d/wordpress.conf;
    #Configure Nginx Fast-CGI Cache Exceptions
    include /etc/nginx/conf.d/fastcgi_no_cache.conf;
    #Multiple wordpress sites container
    include /etc/nginx/sites-available/*.conf;

It is recommended to split a custom configuration of the original nginx.conf to make it easier to read and do modification.

# vi /etc/nginx/conf.d/common.conf
# Global configuration file.
# ESSENTIAL : Configure Nginx Listening Port
listen 80;
# ESSENTIAL : Default file to serve. If the first file isn't found,
index index.php index.html index.htm;
# ESSENTIAL : no favicon logs
location = /favicon.ico {
    log_not_found off;
    access_log off;
# ESSENTIAL : robots.txt
location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
# ESSENTIAL : Configure 404 Pages
error_page 404 /404.html;
# ESSENTIAL : Configure 50x Pages
error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
# SECURITY : Deny all attempts to access hidden files .abcde
location ~ /\. {
    deny all;
# PERFORMANCE : Set expires headers for static files and turn off logging.
location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off; log_not_found off; expires 30d;
   #    expires max;
   add_header Pragma no-cache;
   add_header Cache-Control "public";
vi /etc/nginx/conf.d/gzip.conf
gzip  on;
gzip_comp_level 6;
gzip_proxied any;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";

gzip_types text/css text/x-component application/ecmascript application/json application/pdf application/javascript application/x-javascript text/javascript application/postscript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;

gzip_http_version 1.1;
gzip_vary on;
vi /etc/nginx/conf.d/option.conf
## FAST-CGI Configurations
fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=WPCACHE:1024m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
add_header X-Fastcgi-Cache $upstream_cache_status;

## Other server option
access_log      off;
sendfile        on;
tcp_nopush      on;
tcp_nodelay     on;
server_tokens   off;
keepalive_requests 100000;
reset_timedout_connection on;
port_in_redirect off;
client_body_timeout  1460;
client_header_timeout 1460;
client_max_body_size 10m;
send_timeout 1460;
keepalive_timeout 1300;
vi /etc/nginx/conf.d/wordpress.conf
# WORDPRESS : Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
    try_files $uri $uri/ /index.php?q=$uri&$args;

# SECURITY : Deny all attempts to access PHP Files in the uploads directory
location ~* /(?:uploads|files)/.*\.php$ {
    deny all;

# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
# SECURITY : Zero day Exploit Protection
try_files $uri =404;

# ENABLE : Enable PHP, listen fpm sock
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 4k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
##Added below for fastcgi_cache
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
fastcgi_cache WPCACHE;
fastcgi_cache_valid 200 60m;
fastcgi_cache_valid 404 60m;
fastcgi_max_temp_file_size 4m;
fastcgi_cache_use_stale updating;
fastcgi_cache_methods GET HEAD; # Only GET and HEAD methods apply
add_header X-Fastcgi-Cache $upstream_cache_status;

# Deny access to htaccess files
location ~ /\. {
        deny all;

# Deny access to .php files in the /wp-content/ directory (including sub-folders)
location ~* ^/wp-content/.*.(php|phps)$ {
        deny all;

## Block SQL injections
location ~* union.*select.*\( {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* union.*all.*select.* {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* concat.*\( {access_log /var/log/nginx/*.*.log blocked; deny all;}
### Block common exploits
location ~* (< |%3C).*script.*(>|%3E) {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* base64_(en|de)code\(.*\) {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* (%24&x) {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* (%0|%A|%B|%C|%D|%E|%F|127\.0) {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* \.\.\/  {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* ~$ {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* proc/self/environ {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* /\.(htaccess|htpasswd|svn) {access_log /var/log/nginx/*.*.log blocked; deny all;}
### Block file injections
location ~* [a-zA-Z0-9_]=(\.\.//?)+ {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ {access_log /var/log/nginx/*.*.log blocked; deny all;}
### wordpress security
location ~* wp-config.php {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* wp-admin/includes {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* wp-admin/setup-config.php {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* wp-app\.log {access_log /var/log/nginx/*.*.log blocked; deny all;}
location ~* (licence|readme|license)\.(html|txt) {access_log /var/log/nginx/*.*.log blocked; deny all;}

# PLUGINS : Enable Rewrite Rules for Yoast SEO SiteMap
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml$ "/index.php?xml_sitemap=params=$2" last;
rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml\.gz$ "/index.php?xml_sitemap=params=$2;zip=true" last;
rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html$ "/index.php?xml_sitemap=params=$2;html=true" last;

Configure Cache Exceptions :

vi /etc/nginx/conf.d/fastcgi_no_cache.conf
set $no_cache 0;

    # POST requests and URLs with a query string should always go to PHP
    if ($request_method = POST) {
        set $no_cache 1;

   if ($query_string != "") {
        set $no_cache 1;

    # Don't cache URIs containing the following segments
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php
                         |sitemap(_index)?.xml") {
        set $no_cache 1;

    # Don't use the cache for logged-in users or recent commenters
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass
        |wordpress_no_cache|wordpress_logged_in") {
        set $no_cache 1;
# mkdir -p /etc/nginx/sites-available
vi /etc/nginx/sites-available/ehowstuff.local.conf

Add below information :

server {
    listen     80;
    server_name ehowstuff.local;
    rewrite ^/(.*)$ http://www.ehowstuff.local/$1 permanent;

server {
        server_name www.ehowstuff.local;
        root /var/www/html/ehowstuff;
        access_log /var/log/nginx/ehowstuff.local.access.log;
        error_log /var/log/nginx/ehowstuff.local.error.log;

3. Create a database for wordpress site with your prefered user and password :

MariaDB [wordpressdb]> CREATE DATABASE wordpressdb;
MariaDB [wordpressdb]> CREATE USER 'wordpressuser'@'localhost' IDENTIFIED BY 'wordpresspassword';
MariaDB [wordpressdb]> GRANT ALL PRIVILEGES ON wordpressdb.* to wordpressuser@localhost;

Extract the wordpress file into web server root directory :

# cd /var/www/html/
# wget
# tar xzvf latest.tar.gz
# mv wordpress /var/www/html/ehowstuff

You can follow this steps to install wordpress on CentOS.

READ  How to Change the WordPress URLs in MySQL Database

4. Start the services and make it auto start at boot :

# systemctl restart nginx && systemctl restart php-fpm && systemctl restart mariadb
# systemctl enable nginx && systemctl enable php-fpm && systemctl enable mariadb

Leave a Reply

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