Give us a call: (800) 252-6164
How to make your website load in under 2 seconds using Apache, Nginx, Redis, PHP7, MySQL, and WordPress

Load Times Under 2s: Configuring Nginx

May 6, 2018 | By admin | Filed in: hosting.

This is part 8 of a 10 part tutorial on setting up and configuring Nginx as a reverse proxy with an Apache backend. To view the Nginx installation instructions (including with the aio threading module installation instructions), please view the installing Nginx tutorial, part 7. To start from the beginning, please go to the start of the Nginx+Apache tutorial.

Ready for configuring your Nginx server? Let’s begin…

First up, let’s make a settings folder for all sites.


Now we need to configure Nginx. This is where the main speed improvements happen over a traditional Apache-only system. First up, we’re going to back up the old version of the main configuration file.


Now, we need to edit the nginx.conf file


And copy in the following code…


Here’s what each line means

Main Configurations

user nginx;

This means that our Nginx server should use the nginx user.

error_log /var/log/nginx/error.log;

This sets the location of your primary Nginx error log.

pid /run/;

This sets the process id location of your Nginx processes.

include /usr/share/nginx/modules/*.conf;

This line indicates which directory to include dynamic module configurations from

worker_processes [[Number of Cores]];

This indicates the number of worker processes you want the Nginx main server to spawn. You will want to make sure that the process count is less than or equal to the number of cores available in your server. If you exceed this number, the processes start conflicting and slowing down your server.

events {}

This is the event block. We will put event-based configurations in here.

worker_connections 2048

This specifies the number of connections available per process. In order to determine the number of possible connections at the same time, multiply the number of processes by the number of worker_connections. If you don’t know what to put here, divide the number of worker connections you want by the number of processes you have. That will give you the number of connections you need available at one time.

use epoll

This specifies that we should use epoll as our connection processing method. It’s significantly faster than the other available methods.

worker_rlimit_nofile 2048

This sets the maximum number of open files on each worker_process. You may need to tune this to your own system’s needs.


This section defines our HTTP settings for the Nginx server.

aio threads

This specifies that our system should use worker threads for asynchronous input and output (aio). Essentially, this allows the Nginx process to accept a request for a file on the disk, send off the request, and accept other requests until the response from the hard drive comes back. This greatly speeds up the server, especially under higher loads.

Log_format [format options]

This section allows you to specify how you would like your server logs to be formatted. You can specify any information that you’re looking for. A full list is available here:

Map $enableLogging

This section allows you to manually specify which file requests you want to have logged or not (determination for conditional logging, see below). You can use a regular expression for each file line to determine which you want and don’t want included.

access_log /var/log/nginx/access.log main buffer=256k flush=200ms if=$enableLogging;

This specifies your main access log file. The first part will specify where the logs are saved. The buffer and the flush specify when the log file should be written. In this case, it specifies to write the log to file when it reaches 256k or after 200ms, whichever comes first. Finally, the if=$enableLogging specifies whether the request should be logged (from our map $enableLogging based on the $request_uri above).

root /var/www/;

This specifies our root directory for all of our sites.

index index.php index.html index.htm;

This specifies the index of the files to look for when the server receives a request for a folder path, in order. In our case, it will look for an index.php file, then if it doesn’t exist, an index.html file, and then if that’s not available, an index.htm file.

sendfile on;

Sendfile greatly speeds up the serving of static content by using the system sendfile() call directly. This allows the Linux kernel to copy the content directly without having to first copy the data into the Nginx memory space. If you’re serving static content, you definitely want to have this enabled. However if you’re only using Nginx as a reverse proxy for the Apache backend (e.g. have Apache serve the static files too, which isn’t what we are doing here), it can be disabled.

tcp_nopush on;

This effectively optimizes the amount of data sent in a single packet. This reduces the number of overall round trips required between your server and the client. This is especially useful for situations where the data is received by the Nginx server in chunks (e.g. like with an Apache backend) and you don’t want to have a large number of round trips. It’s worth noting that this MUST be activated in conjunction with “sendfile on;” in order to ensure that Nginx can use this.

tcp_nodelay on;

This is helpful for speeding up your server in situations where the Nginx server has everything all at once. It instructs the server not to wait for a packet to be filled up before sending it to the visitor. I personally enabled it on my server, however, you may want to test it to see if it speeds up the load times for you on your system.

types_hash_max_size 4096;

This specifies the maximum size of the types hash table. The official description is here: but basically it increases the size of the lookup table, increasing the speed. Feel free to modify this from the default value of 1024 if needed.

keepalive_timeout 65;

This specifies the number of seconds that the server will keep the connection open with the client, avoiding having to reconnect, thereby saving time.

include /etc/nginx/mime.types;

This specifies the file to include for the MIME types. It should already be created by default.

default_type application/octet-stream;

This specifies the default content type sent by the Nginx server if none is provided by the individual response.

include /etc/nginx/conf.d/*.conf;

This specifies the configuration files for individual sites that will be loaded by the Nginx server.

server {}

This delineates a server block. Within this group, we will define the configuration for the default sites (e.g. when someone goes to the IP address via port 80 or 443).

listen 80 default_server;

This specifies which port to listen on. In this case, we are listening on port 80 for non-SSL web traffic. This will be the default server settings block for the server over port 80.

server_name _;

This sets the server name. It’s blank since this is for the base IP address.

root /var/www/html;

This specifies the root folder for the files available through this server block (for example, the phpmyadmin clone that we created earlier)

include /etc/nginx/global/gzip.conf;

This specifies that, by default, we should gzip files for this block. We will define our gzip settings in the global/gzip.conf file, and that file will be included in all of the sites that we want gzip enabled. This makes implementing changes much quicker.

include /etc/nginx/default.d/*.conf;

This specifies to include all of the default configuration settings.

include /etc/nginx/global/cache.conf;

This specifies to include the cache settings. Similar in use to the gzip settings, this will allow us to easily make changes to the server’s caching settings.

The Basic HTTP (Non-SSL) Server

location / {}

This specifies to apply these rules for all files (although the ‘/’ is a last option, as compared to some of the more specific location rules). For more about location rules, please see here:

include /etc/nginx/global/proxy.conf;

Within our location block, we’re going to need to include the proxy information. This specifies how Nginx should connect to the Apache backend.


This specifies that all traffic within this block from the local machine should go to the local machine, port 8080. This is the location and port of the Apache server.


This sets the protocol (http) and address ( of our proxy server.

error_page 404 /404.html;

This defines the location of the 404 error page.

error_page 500 502 503 504 /50x.html;

This defines the location of the 50x server error pages.

The Basic HTTPS (SSL) Server

The https version of the basic server block is very similar to the non-ssl version (http). However, there are some changes:

listen 443 ssl http2 default_server;

This specifies that the server should listen on port 443 for SSL encrypted connections (instead of port 80 without encryption). It also specifies that we will have Nginx support HTTP2. While HTTP2 doesn’t need to be SSL only, Firefox and Chrome only support SSL encrypted connections over HTTP2. Therefore it’s better to just have the http2 declaration on the SSL block.

ssl_certificate “/etc/ssl/certs/test-selfsigned.crt”;

This defines the location of the SSL .crt that Nginx should use for connections.

ssl_certificate_key “/etc/ssl/private/test-selfsigned.key”;

This sets the associated .key file that goes with the .crt file defined above.

include /etc/nginx/global/ssl.conf;

This includes all of our SSL settings. These will probably be the same for all sites (besides the crt and key files), so they can be kept in a central place.


The difference here, within the location block, is that it’s going over port 8081 to our Apache server and it’s over https.


Same deal here. Https over port 8081.


This file defines all of our gzip compression settings. Ideally, we will use pre-compressed files, but if they don’t exist, we can use Nginx to compress the files before sending them out. In my experience though, having Nginx do this should be a last resort since it slows the serving time a bit.



GZip Settings

gzip on;

This sets that we should have the gzip compression module enabled.

gzip_vary on

This enables the “Vary: Accept-Encoding” header in the response.

gzip_proxied any;

This allows our script to provide gzip compression to data that comes from our Apache proxy.

gzip_min_length 1000

This defines the minimum length of contents that should be compressed (in bytes). You can set this to whatever you would like, but it should be relatively small in order to ensure that most of your data is compressed to save bandwidth and improve load times.

gzip_comp_level 3

This sets the gzip compression level. You can set it however high you would like, but higher values take more time to compress. The benefit falls off after 3-5, so you should probably keep it around there.

gzip_buffers 16 8k;

This sets the number of buffers and the size of those buffers, respectively, used to compress the response.

gzip_http_version 1.1;

This sets the minimum HTTP version that compressed files should be used for.

Gzip_types …

This sets all of the different types of content that gzip compression should be enabled for.


This file provides the settings for which files should be cached by the user’s browser.



Cache Settings

location ~* \.(gif|jpg|jpeg|png|ico|wmv|3gp|avi|mpg|mpeg|mp4|flv|mp3|mid|js|css|wml|otf|ttf|woff|woff2|etf)$ {}

This looks for any files ending in any of these extensions. We are going to apply browser-side caching headers to these files. You can feel free to add any extensions that you want, as long as they are static files. All dynamic files will need to be handled elsewhere

gzip_static on;

This indicates that Nginx should look for pre-compressed versions of the file (ending in .gz) and serve that directly as opposed to compressing the file every time and then sending it to the visitor. This is very important, especially for large files or ones where you want more compression, since Nginx would have to load the files in completely, taking up a lot of additional time. This is another reason that we needed to compile Nignx by the way, since the module to do this isn’t included in the standard package.

expires max;

This indicates that we should use the maximum expiration time in the future.


This defines the SSL settings common to all of the SSL sites.



SSL Settings

ssl_session_cache shared:SSL:20m;

This stores the SSL session parameters. We are using a shared cache among all of the worker processes. We are specifying that the cache is for SSL and should provide 20mb available. This can store approximately 4000 sessions per megabyte. For more information, please see here:

ssl_session_timeout 4h;

This is the amount of time that the sessions are valid for and may be reused.

ssl_session_tickets on;

This enables sessions to be resumed using TLS session tickets. For more information, see here: and here:

ssl_prefer_server_ciphers on;

This specifies that the server ciphers should be preferred over the client ones for the TLS protocols.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

This specifies that our server should allow the current versions of TLS, but not SSL v2 and 3.

ssl_ciphers …;

This lists all of the ciphers that the server is allowed to use.

resolver valid=300s;

This specifies the name servers that should be used to resolve names of servers into IP addresses (we’re using Google’s public ones). For more information, see here: .


This defines the proxy settings that will allow our Nginx server send requests to the Apache server and receive responses back.



PHP Reverse Proxy Settings

location ~ \.php$ {}

This specifies that we should look for all of the files ending in “.php” and send them over to our Apache server for processing.

include /etc/nginx/global/killCache.conf;

This indicates that we should include our configuration file that prohibits caching of PHP files.

try_files $cacheTryFile @apache;

This says that we should first try our cached version of the page (mainly for work with WordPress), and then if that isn’t available, the request should be sent over to our Apache server section.

location / {}

This specifies that for all files that don’t match anything else, we should be sending them over to the Apache server for processing. This will include directories (both ending in ‘/’ and those that don’t).

try_files $cacheTryFile $uri @apache;

This specifies that we should look for the cached version of the file, then try looking for the requested URI in the filesystem, and if neither of those are found, it should send the request over to the Apache server.

location @apache {}

This is the directive that specifies how to send requests over to the Apache server.

gzip_static off;

We need to turn the gzip module off.

include /etc/nginx/global/proxy.conf;

We need to include the proxy settings that define how our site will connect to the Apache backend.

Proxy Settings


These specify the location (including protocol) of the URI to be requested from the Apache server.

proxy_pass $protocol://$proxyPort;

This actually passes the request over to the Apache server.


This indicates that we’re all done with the processing for this block.


This defines the proxy settings that will allow our Nginx server send requests to the Apache server and receive responses back.



Reverse Proxy Settings

Connection Settings


Nginx Reverse Proxy to Apache Headers

These set the headers that should be sent from the Nginx server over to the Apache server.


These location rules specify some restrictions on access to files. These are mainly for WordPress, but others can prove useful in other situations too (like blocking access to hidden files.)



Nginx File Restrictions Settings

Don’t Log favicon.ico

No need to log requests to the favicon.ico file.

Don’t Log robots.txt

No need to log requests to the robots.txt file.

Block Access to wp-config.php

Block access to the wp-config.php file.

Block Access to Hidden Files

Block access to all hidden files (e.g. .htaccess, .htpasswd, .DS_Store for Macs, etc.)

Block Access to .php Files Uploaded to Uploads Folders

This blocks direct access to any uploaded .php files in the uploads directory.


This file provides support for WordPress caching. It will allow all requests with a query (either GET or POST) directly to the Apache server, but it will directly serve the static files.



WordPress Settings

set $cache_uri $request_uri;

As an initial option, we will try providing the cached URL as simply the URI that’s requested.

Don’t Cache POST HTTP Requests

Send all POST requests directly to the Apache Server for processing.

Don’t Cache GET HTTP Requests with Query

Send all GET requests with a query at the end (e.g. “/?[var]=[value]”) directly to the Apache server.

Don’t Cache a Number of WordPress Files

This specifies that we shouldn’t cache any access to a number of URLs related to WordPress administration, feeds, comments, links, locations, sitemap, etc.

Logged-In Users

We shouldn’t serve a cached version of a URL if the user is logged into the site (e.g. if the logged-in cookie is set.)

set $cacheTryFile $cache_uri;

We need to create a new variable to test for our WP pre-compressed cache version. This is the absolute filepath, not just the URI component.

set $c “”;

This is a temporary variable we’re going to use to determine whether to serve the https version of the page, the http version, or not to provide the cached version at all.

If We Still Have a $cache_uri Value

If the $cache_uri variable hasn’t been cleared yet, indicate that.

If the File was Requested Over HTTPS

If the page is requested over https, indicate that.

If the File was Requested Over HTTP

If the page is requested over http, indicate that.

If Cache and HTTPS

If there is a cache value and it was requested over https, look for the compressed https version of the file.

If Cache and HTTP

If there is a cache value and it was requested over http, look for the compressed http compressed version of the file.

Restarting Nginx

Now that we’re all done, we can restart nginx…

If everything worked, if you go to http://[[Your IP Address]]/, you should see the Apache test page on page on the Nginx port (port 80). This demonstrates that everything should be working properly.


Automatic Installation?

If you like the result of the tutorial, but run into problems or would just like to have the work done automatically, we can help you with that. We have an automated script that can SSH into your server and run this tutorial from beginning to end (as long as it’s running CentOS 7). If you would like this done for you ($100), please contact us using the form below.


    Get Started


    Part 1: Introduction and Planning

    Why it’s important to have your pages load in under 2 seconds, and a plan of how we will set up and configure the server.

    Part 2: Installing Software

    Initial setup of the server, including installing CentOS7, installing tools, installing SSH and SSL, enabling repositories, and installing support packages.

    Part 3: MySQL, Apache, PHP7, & Composer

    Installation of MySQL database serverApache web serverPHP, and Composer package manager for PHP

    Part 4: PHPMyAdmin & Redis

    How to download, install, and configure PHPMyAdmin; How to install and configure Redis

    Part 5: SSL Certificate & Apache Configuration

    How to create a self-signed SSL certificate; how to configure Apache as a PHP backend

    Part 6: FTP & DNS

    How to install and configure an FTP server with dynamic users; how to set up a DNS server

    Part 7: Installing Nginx

    How to download, build, and install Nginx with additional modules

    Part 8: Configuring Nginx

    How to set up Nginx configuration files including (with explanations): nginx.confGzip compression settingsvisitor browser caching settingsSSL settingsreverse proxy settings for sending data to the Apache serverreverse proxy connection configurationsfile access restrictions, and WordPress configuration settings

    Part 9: Adding a WordPress Website

    How to add a WordPress website to the server including configuring the DNS Server, adding a verified SSL certificate, either manually or for free using Let’s Encrypt, setting up FTP for your site, configuring Apache, configuring Nginx, uploading your WordPress site files, and importing your MySQL database.

    Part 10: WordPress Website Speed Improvements

    Speeding up your WordPress installation using a Redis plugin and a caching plugin.


    Comments are closed here.

    Scroll To Top