Nginx https reverse proxy to Wordpress with Apache, http and different port

3 minute read

Today I had to hide a Wordpress 3.8.1 blog behind an Nginx reverse proxy configured to use only https. Nginx was behind an external firewall which forwarded https://bob.org:8080/blog to Nginx using https (port 4443). The difficulty was that whe Wordpress blog was installed in an Apache HTTP server in port 80 and it worked using http!. Lets say that my site was https://bob.org and I had to put the blog in https://bob.org:8080/blog. Thus my list of burdens was the following:

  1. Configure Nginx to use only https and redirect http to https.
  2. Reverse proxy in Nginx from bob.org:8080/blog to 192.168.1.10/wordpress in Apache.
  3. Force Wordpress links to use port 8080

The following is the diagram summarised my server configuration:

      +--------------+
      |  Firewall    |
      | bob.org:8080 | 
      +------+-------+
            |
            |
            v
      +–––––––––+                  +––––––––––––––+
      |  Nginx  |      http        |  Wordpress   |
      | (https) |+---------------->| Apache HTTP  |
      |         |                  | (port 80)    |
      +–––––––––+                  +––––––––––––––+ 

So you will say: “Are you completely insane ??!!” I was asked to do it! That is my answer!

Finally after one hour of searching I used a combination of two support articles (post1, post2), some PHP knowledge and experience and a little of imagination, I created a solution:

Configure Nginx to reverse proxy all requests to /blog

This is a fragment of my /etc/nginx/sites-available/default-ssl Nginx configuration file:

upstream blog-webservers {
    server 192.168.1.10:80;
}

# redirect http to https
# http://serverfault.com/a/171238
server {
    listen 80;
    rewrite ^(.*) https://$host$1 permanent;
}

server {
    listen *:443;

    ssl on;
    ssl_certificate     /etc/nginx/crypto/server.crt;
    ssl_certificate_key /etc/nginx/crypto/server.key;

    ssl_client_certificate /etc/nginx/crypto/ca.crt;
    ssl_verify_client optional;
    ssl_verify_depth 10;

    # pem key asking for password problem
    # http://pandemoniumillusion.wordpress.com/2008/04/21/nginx-ssl-passphrase-at-startup/

    server_name bob.org;
    access_log  /var/log/nginx/ssl.access.log;
    error_log   /var/log/nginx/ssl.error.log;
    error_page  404 /404.html;


    # reverse proxy to blog web servers
    location /blog { 
        proxy_pass http://blog-webservers/wordpress/;
        proxy_redirect https://server_name http://blog-webservers/wordpress;
	
        proxy_read_timeout       3500;
        proxy_connect_timeout    3250;

        proxy_set_header   X-Real-IP          $remote_addr;
        proxy_set_header   Host               $host;
        proxy_set_header   X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto  https;
       
 
        proxy_set_header   SSL_PROTOCOL $ssl_protocol;
        proxy_set_header   SSL_CLIENT_CERT $ssl_client_cert;
        proxy_set_header   SSL_CLIENT_VERIFY $ssl_client_verify;
        proxy_set_header   SSL_SERVER_S_DN $ssl_client_s_dn;
    }
}

Configure Wordpress wp-config.php

I added the following lines on top of wp-config.php file:

// If Wordpress is behind reverse proxy 
// which proxies https to http
if ( (!empty( $_SERVER['HTTP_X_FORWARDED_HOST'])) ||
     (!empty( $_SERVER['HTTP_X_FORWARDED_FOR'])) ) { 

    // http://wordpress.org/support/topic/wordpress-behind-reverse-proxy-1
    $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];

    define('WP_HOME', 'https://bob.org:8080/blog');
    define('WP_SITEURL', 'https://bob.org:8080/blog');

    // rewrite blog word with wordpress
    $_SERVER['REQUEST_URI'] = str_replace("wordpress", "blog",
    $_SERVER['REQUEST_URI']);

    // http://wordpress.org/support/topic/compatibility-with-wordpress-behind-a-reverse-proxy
    $_SERVER['HTTPS'] = 'on';
}

If you want to debug the former PHP code fragment you can print $_SERVER variables before and after configuration changing command inside the if block:

echo '<br>Before:';
echo '<br>$_SERVER[\'HTTP_HOST\'] : ' . $_SERVER['HTTP_HOST'];
echo '<br>$_SERVER[\'HTTP_X_FORWARDED_HOST\']: ' . $_SERVER['HTTP_X_FORWARDED_HOST'];
echo '<br>$_SERVER[\'REQUEST_URI\']: ' . $_SERVER['REQUEST_URI'];
echo '<br>$_SERVER[\'HTTP_X_FORWARDED_SERVER\']: ' . $_SERVER['HTTP_X_FORWARDED_SERVER'];
echo '<br>$_SERVER[\'HTTP_X_FORWARDED_FOR\']: ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
echo '<br>$_SERVER[\'HTTP_X_FORWARDED_FOR\']: ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
echo '<br>$_SERVER[\'HTTPS\']: ' . $_SERVER['HTTPS'];
echo '<br>$_SERVER[\'REMOTE_ADDR\']: ' . $_SERVER['REMOTE_ADDR'];    
echo '<br>$_SERVER[\'SERVER_NAME\']: ' . $_SERVER['SERVER_NAME'];
echo '<br>$_SERVER[\'SERVER_PROTOCOL\']: ' . $_SERVER['SERVER_PROTOCOL'];
echo '<br>WP_HOME: ' . WP_HOME;
echo '<br>WP_SITEURL : ' . WP_SITEURL;

I am exhausted, just writting it down! Be patient!

Leave a Comment