WordPress with nginx ssl offloading


WordPress is a free content management system used for hosting web sites. The web application written in php can running under apache or nginx. Whatever the solution adopted, it’s necessary to have a reverse proxy for better managing the web site and for performing SSL termination.

In this article a solution with nginx as reverse proxy and wordpress running inside a docker container is presented. The goal is to show how configure the nginx for ssl offloading: the functionality to process the SSL encryption is performed by nginx, and the apache where is hosted the wordpress is designated only for its task.

The ssl offloading increases the web application performance, the scalability of the web service permitting to add more wordpress applications without ssl certification time overhead and offers the possibility to scan the http decrypted traffic by ids/ips systems.

The reference architecture of this article is the following:

WordPress with nginx ssl offloading

WordPress with nginx ssl offloading

Let’s start with wordpress container installation

WordPress Container Installation

The choice to use docker containers for wordpress is due for the easy process deployment: no need to configure any LAMP architecture (Linux, Apache, Mysql, PHP). Everything is configured and isolated with a free security layer provided by docker containers.

Two wordpress containers are installed in a Centos 7.2 system: one containing the apache with the latest wordpress version and the other with the mysql database used for storing the html pages of the site.

Following the commands for installing docker with docker-compose.

[root@nikto docker]# yum install docker-engine
[root@nikto docker]#systemctl enable docker.service
[root@nikto docker]#curl -L “https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)$(uname -m) -o /usr/local/bin/docker-compose
[root@nikto docker]#chmod +x /usr/local/bin/docker-compose

The docker-compose file is:

[root@nikto docker]# vi wordpress.yml
version: ‘2’
image: mysql:5.7
– /var/lib/mysql:/var/lib/mysql
restart: always
MYSQL_USER: wordpress
– db
image: wordpress:latest
– /var/www/html:/var/www/html
– “8000:80”
restart: always
working_dir: /var/www/html

The web site is stored in the database; other files like images are stored instead in working_dir. For avoiding to lose all the data when the containers are deleted, it’s better to mount the working_dir of the wordpress cointainer and the data of mysql container in a directory of the host where docker engine is running. This explains the directive volumes in the compose file.

Following the commands to execute for starting the containes:

root@nikto docker]# mkdir /var/www/html
root@nikto docker]# mkdir /var/lib/mysql
root@nikto docker]#docker-compose -f wordpress.yml db up -d
root@nikto docker]#docker-compose -f wordpress.yml  up -d wordpress

This is enough: with docker containers is easy to bring services up&running:

[root@nikto docker]# docker ps -a
05eadc99d1c4 wordpress:latest “docker-entrypoint.sh” 3 weeks ago Up 2 days>80/tcp docker_wordpress_1
033524f70310 mysql:5.7 “docker-entrypoint.sh” 3 weeks ago Up 3 we6/tcp docker_db_1

Remember that the wordpress container is listening on 8000 port. Let’s start now to nginx configuration.

Nginx SSL Offloading Configuration

Nginx is installed in another centos 7.2 system. Before configuring it, you should have the public certificate of your site with the private key. For doing that, you can read my article https://www.securityandit.com/security/openssl-and-certificates/.

The nginx installation is very simple::

[root@nikto-rp docker]#yum install nginx
[root@nikto-rp docker]#systemctl enable nginx
[root@nikto-rp docker]#systemctl start nginx

The nginx configuration:

[root@nikto-rp conf.d]# pwd
[root@nikto-rp conf.d]# vi nikto.sysandnetsecurity.com.conf
server {
listen 443;
server_name nikto.sysandnetsecurity.com;
access_log /var/log/nginx/nikto.sysandnetsecurity.com.log;
error_log /var/log/nginx/nikto.sysandnetsecurity.com.log;
ssl_certificate /etc/letsencrypt/live/nikto.sysandnetsecurity.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nikto.sysandnetsecurity.com/privkey.pem;
ssl on;
ssl_session_timeout 5m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #libssl > 1.0
ssl_session_cache shared:TLSSL:16m;
location / {
sub_filter http://nikto.sysandnetsecurity.com https://nikto.sysandnetsecurity.com;
sub_filter_once off;
proxy_redirect http://nikto.sysandnetsecurity.com https://nikto.sysandnetsecurity.com;
proxy_set_header Accept-Encoding “”;
proxy_buffering off;
add_header X-Frame-Options SAMEORIGIN;
add_header Strict-Transport-Security: max-age=3456000;
add_header X-XSS-Protection “1; mode=block”;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
allow all;
proxy_pass http://wordpress-ip:8000;} }
server {
server_name nikto.sysandnetsecurity.com;
listen 80;
access_log /var/log/nginx/nikto.sysandnetsecurity.com.log;
error_log /var/log/nginx/nikto.sysandnetsecurity.com.log;
return 301 https://$host;}

Let’s comment the configuration above:

  1. The server name of the site is nikto.sysandnetsecurity.
  2. The ssl certificate was signed by letsencrypt: it’s free. I suggest to try it: https://letsencrypt.org/.
  3. The ssl configuration is explained in my article https://www.securityandit.com/security/openssl-and-certificates/.
  4. The nginx proxies evertything (location /) versus the backend wordpress container.
  5. The sub_filter command is the most important configuration. WordPress returns in the html page links to http protocol from moment that is contacted in http. Modern browsers blocks this traffic due mismatch content because the site is called in ssl and the content contains http links (it could cause main in the middle attack). The command above rewrites all the http links in https.
  6. proxy_set_header Accept-Encoding “” is another important parameter. If it’s not present, the wordpress could return zip traffic to browser and it prevents the nginx to rewrite the links inside http body answer. This header sent to wordpress avoids the zip of the answers.
  7. The proxy_redirect is for rewriting in https the content location returned from wordpress container.
  8. The headers X-Frame-Options, Strict-Transport-Security and X-XSS-Protection are for improving the security and are explained in my article https://www.securityandit.com/security/penetration-testing/.
  9. The proxy_set_header are explained in another my article https://www.securityandit.com/network/security-with-nginx-and-haproxy/.
  10. The http request are redirect in https.

I hope that everything is clear.

Don’t hesitate to contact me for any suggestion or doubt.