Moving from Apache to NGINX on Centos 7

For about 10 years myelectrons.ru, myelectrons.com, 775mv.com and a few other websites of ours have been rather slow. Last time we came back to them, they were so slow (9-15 seconds), it was painful to just wait for it to load so you can hit another button in the administration panel. So we started searching for ways to speed them up. The more obvious solution was to move to a different hosting service because the ping was so slow (200 ms). Nginx, an engine many recommended to speed up a website, appeared to be a suitable option to try. We weren't exactly sure yet how it could speed up a website. So we tried a simple Nginx installation and it didn't bring much of an improvement. That's when my dad came across this blog post https://danielmiessler.com/blog/wordpress-nginx-speed-guide/ Notice how fast it loads? That's lightning speed! The post itself talks about how to make your website faster using Nginx and both server and client side caching. And although it does cover the main points, some stuff the author considers basic doesn't get covered. When I read through it, it left me with a lot of questions still unanswered. It took me several days to figure it all out with the help of my dad. Here I'll share with you, and for myself to refer to it later, how I went about installing Nginx.

What one could do is just install Nginx from the system packages with yum however, as I did that the first time I realized that doesn't speed up the website much if at all. What I ended up doing is installing Nginx from source, and that was a bit more tedious than I expected but when it all works, it's worth it!


So, if you aren't root already type in

sudo -i

Update openssl

First we'll want to update openssl (which is needed for Nginx) to the lastest version which unfortunately doesn't come with Centos 7. For that, we basically follow this tutorial https://cloudwafer.com/blog/installing-openssl-on-centos-7/ while making some small changes of our own along the way to make sure we have the latest version. (It does take a while to update it)

First, we install some packages openssl depends on

yum group install 'Development Tools'
yum install perl-core zlib-devel -y

Now let's go where we'll be installing openssl, download, extract and go into it.

cd /usr/local/src/
wget https://www.openssl.org/source/openssl-1.1.1d.tar.gz
tar -xf openssl-1.1.1d.tar.gz
cd openssl-1.1.1d

Install it (During "make test" a test called enc.t might fail, if it does, no need to panic. It happened to me every time I installed it and all still works fine)

./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib
make
make test
make install

Now according to the source we want to configure shared libraries

cd /etc/ld.so.conf.d/

Now we need to create a file, I like to do that with "vi" or "vim" but "nano" is more intuitive and a better choice if you're not familiar with "vi" or "vim"

nano openssl-1.1.1d.conf

and paste the following in there:

/usr/local/ssl/lib

Exit with Ctrl+x and then I believe it will ask you to save it

Reload the dynamic link

ldconfig -v

Now we configure the openssl binary

Backup the default

mv /bin/openssl /bin/openssl.backup

And let's make the binary

nano /etc/profile.d/openssl.sh
#Set OPENSSL_PATH
OPENSSL_PATH="/usr/local/ssl/bin"
export OPENSSL_PATH
PATH=$PATH:$OPENSSL_PATH
export PATH

Again, exit and save

Make it executable

chmod +x /etc/profile.d/openssl.sh

Reload the openssl environment and check that everything is in place

source /etc/profile.d/openssl.sh
echo $PATH
which openssl
openssl version -a

That last one should show you that you have the version 1.1.1d

Phew, done with the openssl installation. Now take a quick break if you wish, maybe drink some water (I know I will as this post is already getting long).


Install Nginx

Before we get to installing Nginx, let's install all the dependencies

yum install zlib-devel pcre-devel git libxslt-devel gd gd-devel geoip-devel -y

Now let's get the latest Nginx, extract it, and go into it

wget http://nginx.org/download/nginx-1.16.1.tar.gz
tar -xzvf nginx-1.16.1.tar.gz
cd nginx-1.16.1/

Now inside the directory let's get some additional modules we'll need for the caching

git clone https://github.com/openresty/headers-more-nginx-module
git clone https://github.com/FRiCKLE/ngx_cache_purge

And install Nginx

./configure --with-pcre --prefix=/opt/nginx-1.16.1 --user=nginx --group=nginx --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --without-http_charset_module --with-http_perl_module --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-stream_ssl_preread_module --with-openssl=/usr/local/src/openssl-1.1.1d --add-module=./ngx_cache_purge --add-module=./headers-more-nginx-module

Yes, that's a really long line, it tells what modules to add to the Nginx installation

make
make install

Now we have Nginx installed however this doesn't include the system service or the nginx user, so let's add them in

Starting with the service we'll want to create a new file

nano /etc/systemd/system/nginx-1.16.1.service

And put the following inside:

[Unit]
Description=nginx 1.16.1
After=syslog.target network.target

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/nginx-1.16.1
ExecStart=/opt/nginx-1.16.1/sbin/nginx $CLI_OPTIONS
ExecReload=/opt/nginx-1.16.1/sbin/nginx -s reload
ExecStop=/opt/nginx-1.16.1/sbin/nginx -s quit

[Install]
WantedBy=multi-user.target

Create an environment file for Nginx

nano /etc/sysconfig/nginx-1.16.1
# Command line options to use when starting nginx
#CLI_OPTIONS=""

Add the Nginx user like this:

adduser --system --no-create-home --user-group --shell /bin/false nginx

You can now start the Nginx service:

systemctl start nginx-1.16.1

And make sure it starts on bootup

systemctl enable nginx-1.16.1

One last thing, let's check that it actually runs

systemctl status nginx-1.16.1

Configure php and the websites to work with Nginx

Now we have Nginx installed and running, but that doesn't mean the websites will work with it yet. We still need to tell php so nginx controlls it not apache and configure our websites to work with nginx instead of apache

Let's start with php

nano /etc/php-fpm.d/www.conf

find where it says

user = apache
group = apache

and replace with

user = nginx
group = nginx

Let's restart php for the changes to take effect

systemctl restart php-fpm

Now unlike a standard Nginx installation this one will have everything related to Nginx installed in one place: "/opt/nginx-1.16.1/". There are 2 configs we need to edit purely for Nginx and then configs for however many sites you have on your machine

I'll show you how the configs I have look like. Before modifying whatever you have to look like what I have, please make a backup of it as you never know if you might need to go back to it.

This is the fastcgi config where I added most of the caching settings

nano /opt/nginx-1.16.1/conf/fastcgi.conf
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect 
fastcgi_param  REDIRECT_STATUS    200;

fastcgi_pass_header Set-Cookie; 
fastcgi_pass_header Cookie; 
fastcgi_ignore_headers Cache-Control Expires Set-Cookie; 
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 
fastcgi_split_path_info ^(.+.php)(/.+)$; 
fastcgi_param  PATH_INFO $fastcgi_path_info;
fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_path_info; 
fastcgi_intercept_errors on; 
include fastcgi_params; 
fastcgi_no_cache $no_cache; 
fastcgi_cache_bypass $no_cache; 
fastcgi_cache drm_custom_cache; 
fastcgi_cache_key $server_name|$request_uri; 
fastcgi_cache_valid 404 60m; 
fastcgi_cache_valid 200 60m; 
fastcgi_max_temp_file_size 4m; 
fastcgi_cache_use_stale updating; 
fastcgi_pass localhost:9000;

The main Nginx config

nano /opt/nginx-1.16.1/conf/nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       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"';        

    #access_log  logs/access.log  main; 

    sendfile        on; 
    tcp_nopush     on; 
    tcp_nodelay on; 
    #keepalive_timeout  0; 
    keepalive_timeout  65; 
    types_hash_max_size 2048; 
    server_names_hash_bucket_size 64; 

    gzip  on; 
    gzip_vary on; 
    gzip_proxied any; 
    gzip_comp_level 9; 
    gzip_buffers 16 8k; 
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;
        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html      

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # skipped comments

        location ~* .(ico|jpg|webp|jpeg|gif|css|png|js|ico|bmp|zip|woff)$ {         
            access_log off;
            log_not_found off;
            add_header Pragma public;
            add_header Cache-Control "public";
            expires 14d;
        }

        location ~* .(php|html)$ {
            access_log off;
            log_not_found off;
            add_header Pragma public;
            add_header Cache-Control "public";
            expires 14d;
        }
    }

# skipped comments

    include /opt/nginx-1.16.1/conf/conf.d/*;
    fastcgi_cache_path /dev/shm/nginx levels=1:2 
    keys_zone=stupidfast:16m max_size=1024m inactive=60m;
}

Yes I skipped a ton of comments where it says "#skipped comments", but otherwise there would be a whole lot of unnecessary stuff that just clutters the page

And the site config (replace DOMAIN_NAME with your website's domain name like "example.com")

First we need to make the directory for the site config

mkdir /opt/nginx-1.16.1/conf/conf.d

Now the site config

nano /opt/nginx-1.16.1/conf/conf.d/DOMAIN_NAME.conf
server {
     listen         80 default_server;
     listen         [::]:80 default_server;
     server_name    DOMAIN_NAME www.DOMAIN_NAME;
     root           html/DOMAIN_NAME;
     index          index.php;


     location / {
         try_files $uri $uri/ =404;
     }

     if (!-e $request_filename) {
         rewrite ^.*$ /index.php last;
     }

     location ~* \.php$ {
         fastcgi_pass 127.0.0.1:9000;
         include         fastcgi_params;
         fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
         fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
     }

#SECURITY

#Ignore other host headers
if ($host !~* ^(DOMAIN_NAME|www.DOMAIN_NAME)$ ) {
return 444;
}

#Obfuscation rule (hide identifying files)
location ~ /(.|wp-config.php|readme.html|licence.txt) {
return 404;
}

#Only allow GET , POST, and HEAD
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
return 444;
}

#Disable viewing of hidden files (files starting with a dot)
location ~ /. {
deny  all;
}


}

Now create the directory for your website and move it there

mkdir /opt/nginx-1.16.1/html/DOMAIN_NAME
mv path_to_site /opt/nginx-1.16.1/html/DOMAIN_NAME

As a last step, let's change the attributes so Nginx has access to the website

chown -R nginx:nginx /opt/nginx-1.16.1/html/DOMAIN_NAME

That's it, we made it!

You can also make sure Apache doesn't bother you anymore if you had it installed and running:

systemctl disable apache
systemctl stop apache

One Reply to “Moving from Apache to NGINX on Centos 7”

Leave a Reply

Your email address will not be published.