Nginx WordPress

Setup WordPress with Nginx and FastCGI Caching on CentOS 7

wordpress-nginx-fastcgi
mm
Written by Santosh Prasad

FastCGI-Cache is currently considered the most efficient way to implement a dynamic cache mechanism in front of our web server with Nginx: that web server can be Nginx itself or another web server that Nginx will “proxy” by catching all the incoming requests, routing them to the upstream PHP-FPM server, get the resulting HTTP responses and return them to the caller.

The FastCGI Nginx module has directives for caching dynamic content that are served from the PHP backend, thus eliminating the need for additional page caching solutions like reverse proxies – such as Nginx Proxy-Cache, Varnish, Squid and the likes – or application specific plugins – such as WordPress Total Cache, WP Super Cache and so on: content can also be conditionally excluded from caching based on the request method, URL, cookies, or any other server variable, just like any good cache mechanism available.

If you are planing 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.

This is 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.

In this article I will show how to setup WordPress with nginx and fastcgi caching on CentOS 7.

Setup WordPress with Nginx and FastCGI Caching

Follow the below steps to setup WordPress with nginx and fastcgi caching.

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.looklinux.com -> 192.168.0.5

1. Create a repository to install nginx on CentOS as shown below.

# vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

2. Run the below command to install nginx, php, php-mysql MariaDB server.

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

3. Configure php-fpm

This is the configuration file for 4Gb ram VPS.

# vi /etc/php-fpm.d/www.conf
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
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. Nginx and FastCGI Caching configuration.

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

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


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;
}

I will suggest 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_pass   127.0.0.1:9000;
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/looklinux.local.conf

Add the below lines:

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

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

3. Now 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 [email protected];

Then extract the wordpress file into web server root directory.

# cd /var/www/html/
# wget http://wordpress.org/latest.tar.gz
# tar xzvf latest.tar.gz
# mv wordpress /var/www/html/looklinux

4. Finally 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
Thank you! for visiting Look Linux.

If you find this tutorial helpful please share with your friends to keep it alive. For more helpful topic browse my website www.looklinux.com. To become an author at Look Linux Submit Article. Stay connected to Facebook.

About the author

mm

Santosh Prasad

Hi! I'm Santosh and I'm here to post some cool article for you. If you have any query and suggestion please comment in comment section.

Leave a Comment