WordPress (LAMP) using an external reverse proxy (nginx)

WordPress is a very popular and easy to use CMS. But if you want to use nginx as an external reverse proxy with SSL support, the configuration becomes a little more complicated.

The following example describes a working configuration running on two separate servers: WordPress on the backend server with self signed SSL certificates and nginx on the frontend server with a fully qualified servername and externally signed SSL certificates:

This tutorial focuses on the critical configuration steps and skip all the general settings like firewall rules, advanced security settings of nginx an so forth.

Nginx has to shift from pure HTTP to HTTPS. Furthermore it should not run into endless redirections which is a very common problem and for which a lot of reasons can be found. Hence wordpress should provide readable and reliable permalinks.

Install wordpress
Let’s start with the backend server. Install all necessary LAMP packages such as:

$ sudo  apt-get install apache2 mysql-client
mysql-server php5 php5-mysql php5-curl php5-gd

Log in to your database:

$ mysql -u root -p

Create the database and user:

mysql> CREATE DATABASE DB_NAME; 
mysql> CREATE USER DB_USER@localhost 
       IDENTIFIED BY 'DB_PASSWORD';

mysql> GRANT ALL PRIVILEGES ON DB_NAME.* 
       TO DB_USER@localhost;

mysql> FLUSH PRIVILEGES; 
mysql> EXIT

Restart apache and mysql

$ sudo service apache2 restart 
$ sudo service mysql restart

Get the wordpress‘ binaries:

$ cd /tmp 
$ wget -c http://wordpress.org/latest.zip 
$ sudo unzip -q latest.zip -d /var/www/html/

Important: Install wordpress into a subdirectory – called WP_DIR – within apache’s root. For debian/ubuntu based Linux distributions this looks typically like:

/var/www/html/WP_DIR

Installing wordpress into a subdirectory prevents nginx from infinite redirecting loops. So keep the subdirectory’s name in mind. It will be used later.

Finish the worpress installation:

$ cd /var/www/html
$ sudo mv wordpress WP_DIR
$ sudo chown -R www-data.www-data WP_DIR
$ sudo chmod -R 755 WP_DIR
$ cd WP_DIR 
$ sudo mkdir -p wp-content/uploads
$ sudo chown -R www-data.www-data
wp-content/uploads

Prepare the wordpress configuration;

$ cd /var/www/html/WP_DIR
$ cp wp-config-sample.php wp-config.php

Open the configuration file:

$ vi wp-config.php

Register the database by replacing the bold parameters with values you have choosen above:

define('DB_NAME', 'DB_NAME');
define('DB_USER', 'DB_USER');
define('DB_PASSWORD', 'DB_PASSWORD');

Restart apache and mysql again:

$ sudo service apache2 restart 
$ sudo service mysql restart

Generate self signed SSL certificates
Let’s continue with the SSL configuration on the backend. Start with the generation of the private RSA key:

$ openssl genrsa -des3 
                 -passout pass:x 
                 -out server.key 1024

Copy the key:

$ cp server.key server.key.out

Remove the passphrase:

$ openssl rsa -passin pass:x 
              -in server.key.out  
              -out server.key

Remove the temporary key:

$ rm server.key.out

Create CSR (Certificate Signing Request):

$ openssl req -new 
              -key server.key 
              -out server.csr

Create the self signed certificate:

$ openssl x509 -req 
               -days 365 
               -in server.csr 
               -signkey server.key 
               -out server.crt

Copy the keys to apache:

$ sudo cp server.crt /etc/ssl/certs/ssl.crt
$ sudo cp server.key /etc/ssl/private/ssl.key

Configure apache
The following commands enable SSL and rewriting support:

$ sudo a2enmod ssl
$ sudo a2enmod rewrite 
$ sudo a2ensite default-ssl
$ sudo service apache2 restart

Be sure that the AllowOverride directive is set correctly in the httpd.config file:

<Directory />
    Options FollowSymLinks
    AllowOverride All
</Directory>

respectively

<Directory /var/www/html>
    # ... other directives...
    AllowOverride All
</Directory></code>

WordPress SSL/nginx configuration
Open the wordpress configuration file:
$ sudo vi wp-config.php

Append the following lines (Remember: the WP_DIR is now used):

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 
    'https')
    $_SERVER['HTTPS']='on';
 
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
    $_SERVER['HTTP_HOST'] = 
        $_SERVER['HTTP_X_FORWARDED_HOST'];
}
 
$_SERVER['REQUEST_URI'] = 
    str_replace("/wp-admin/", 
        "/WP_DIR/wp-admin/",  
            $_SERVER['REQUEST_URI']);

$_SERVER['REQUEST_URI'] = 
    "/WP_DIR".$_SERVER['REQUEST_URI'];
 
define( 'WP_SITEURL', '/WP_DIR' );
define( 'WP_HOME', '/WP_DIR' );
define(‘FORCE_SSL_ADMIN’, true);

Nginx configuration
Now the frontend called ex.com needs to be configured. To do so, create a nginx configuration file:

$ sudo vi /etc/ngingx/sites-available/nginx.conf

First add security headers:

add_header X-Frame-Options "SAMEORIGIN";
add_header Strict-Transport-Security 
    "max-age=15768000; includeSubDomains";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Content-Security-Policy-Report-Only 
    "default-src 'self'; img-src *; 
    style-src 'self' 'unsafe-inline'; 
    script-src 'self' 'unsafe-inline' 
    'unsafe-eval'";

Create a rule, which shifts the HTTP requests to HTTPS:

server {
    server_name ex.com www.ex.com;
    return 301 https://ex.com$request_uri;
}

The servername example.com stands for any servername which has a DNS entry and can be resolved.

Finally the https rule is to be defined. To do so you need to have two parameters:

  • The wordpress install directory WP_DIR
  • Reference to the externally signed SSL certificate and key

Enter the following snipped to nginx.conf as well:

server {
    server ex.com;
    listen 443 ssl;
    ssl_certificate
        /etc/nginx/ssl/CERTIFICATE_NAME;
    ssl_certificate_key 
        /etc/nginx/ssl/PRIVATE_KEY;

    location / {
        return 301
        https://ex.com/WP_DIR$request_uri;
    }

    location /WP_DIR/ {
        proxy_pass https://backend_server;
        proxy_set_header
            X-Forwarded-Host $host;
        proxy_set_header
            X-Forwarded-Proto $scheme;
    }
}

Save nginx.conf and change to the following directory:

$ cd /etc/nginx/sites-enabled

Create the following soft link

$ sudo su -
$ ln -s /etc/ngingx/sites-available/nginx.conf

To terminate the nginx configuration just restart it:

$ sudo service nginx restart

Putting all together
Open a browser and enter the URL http://example.com resp. https://example.com. You should now be able to finish the browser based wordpress configuration.
I hope that this article was useful. Feel free to leave a comment!