WordPress behind IIS SSL proxy

Long title, but what it boils down to is how to run a WordPress site on a Linux box behind a Windows server running IIS acting as a reverse proxy with a Let’s Encrypt certificate.

Configuration
Linux host:
OpenSuse Leap 42.3, Apache, PHP 5.5.14, MariaDB 10.2.15, WordPress 4.9.5

Windows server:
Server 2008 R2, IIS 7.5

Installing WordPress on OpenSuse
I already had the LAMP stack installed, so all I needed to do was install WordPress itself.
I tried following the instructions for the 5 minute installation.

  • Open a terminal window and su to root.
  • Type zypper in WordPress.

It ran for a while and completed successfully.

I located the installation at /srv/www/htdocs/wp and within that was a file called README.SUSE. So I opened that file and it said to create the database and set up credentials. I already had phpMyAdmin installed and running, so I connected to MariaDB and created a database called, “wordpress”. Not original, but pretty obvious. Then I created a user and ran:
GRANT ALL PRIVILEGES ON wordpress.* to the_user IDENTIFIED BY ‘password’;
FLUSH PRIVILEGES;

The 5 minute instructions said next to edit the wp-config.php file and put the name of the database and the credentials of the user just created into the respective variables. Then open a browser and go to http://localhost/wp/install.php and allow the WordPress installation to populate the database and set itself up to run. Simple. Nope.

Got the dreaded, “ERROR ESTABLISHING A DATABASE CONNECTION” error. Hmm, check the database name and credentials. All looks good. Google.

There were numerous hits and most of them recommended checking that the information in the config file was correct. I double checked and it was. One suggested writing a simple standalone connection test to see if the database was responding.

<?php 
$link = mysql_connect('localhost', 'the user', 'password'); 
if (!$link) { 
  die('Could not connect: ' . mysql_error()); 
} 
echo 'Connected successfully'; 
mysql_close($link); 
?>

And for good measure, I added phpinfo(); to display information about php, the database and the server. I saved the file as testconnection.php in the wp directory.

Then I went to my browser and tried: http://localhost/wp/testconnection.php. And it came back with, “Could not connect: “.

I won’t go into everything, but after many hours I found that if a port isn’t specified and you use “localhost” as the server name, PHP tries to use sockets. This is apparently a Linux convention. And for whatever reason, OpenSuse (mine at least) had put the socket in a non-standard place. You can use the IP address and port or “localhost” and specify the socket name. I eventually got both to work and decided to stick with TCP. No reason other than I just had to pick something.

OK, so several hours wasted, but I now have my test case connecting to the database.

Now to try WordPress. But still getting, “ERROR ESTABLISHING A DATABASE CONNECTION”. More searching and I finally found a page that mentioned that in OpenSuse, the config file isn’t in the wp directory, it’s in /etc. Sigh.

I edit /etc/wordpress/wp-config.php and put in the database and user credentials. Then try the installation again. It connects to the database, but still fails to work.

It’s late, I’ve been at this for hours and I’m grasping at straws. I know that the root login will work, so I put those credentials into the config file and try again. This time it works. Aha! That command I executed to grant permissions to the wordpress user I created? There weren’t any existing objects to grant permission on. I had to create them as root, then change to the user I created.

So, rerun the grant command and reedit the config file and put the new credentials in it. And this time WordPress comes up.

The first thing I discover is that, while WordPress comes up, I can’t get to the admin tools. So rather than spend a lot of time trying to figure out how to configure apache or set up the .htaccess file, I just moved the wp directory under htdocs and renamed it wordpress. Now I can get to the admin tools and set up the rest of WordPress. At least inside my network.

But I really want it to be on my live site and that requires making it a virtual under my existing domain. My domain is served by Windows running IIS 7.5 on Server 2008 R2. IIS is set up to handle both .NET sites running on that server and as a reverse proxy for sites running on the Linux box. It also has the Let’s Encrypt SSL certificate installed.

For more information on setting up IIS as a reverse proxy read this or this. You will need to install URL Rewrite and ARRL.

I’ve already got a couple of other sites on the Linux box running, so I just copy one of those directories and edit the web.config file to point to this new site.
Then I go to https://mydomain.com/wordpress and voila. 502 Bad Gateway. Crap!

It turns out that WordPress doesn’t like running behind IIS with IIS acting as an SSL reverse proxy. For starters, IIS isn’t behaving nicely and passing the X_FORWARDED headers through, so WordPress has no idea that it’s actually running as SSL. Except it isn’t. On the local network it’s getting its requests as http.

Why does this matter? Because WordPress creates all of its links absolute. That means that none of the Javascript or CSS files load. It violates browser security since the browser thinks the site it’s requesting is https, but the generated links to the assets are all http.

And any page links are incorrect because it also thinks that the host is “mediaserver” not my domain.

So first I had to figure out how to get IIS to generate the correct headers and then I had to get WordPress to recognize and use them. Because it turned out that even though I was sending the headers, WordPress wasn’t using them.

To configure IIS to pass the headers requires making a change to the IIS configuration (described here and here) to support those headers and then adding them to the web config.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <remove name="CanonicalHostNameRule1" />
                <remove name="CanonicalHostNameRule2" />
                <remove name="CanonicalHostNameRule3" />
                <rule name="ReverseProxyInboundRule1" stopProcessing="true">
                    <match url="(.*)" />
                    <action type="Rewrite" url="http://mediaserver{REQUEST_URI}" />
                    <serverVariables>
                        <set name="HTTP_X_FORWARDED_PROTO" value="{MapProtocol:{HTTPS}}" />
                        <set name="HTTP_X_FORWARDED_PORT" value="{SERVER_PORT}" />
                        <set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
                        <set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
                    </serverVariables>
                </rule>
            </rules>
            <rewriteMaps>
                <rewriteMap name="MapProtocol">
                    <add key="on" value="https" />
                    <add key="off" value="http" />
                </rewriteMap>
            </rewriteMaps>
        </rewrite>
    </system.webServer>
</configuration>

I won’t go into great detail, but it contains the forwarding rule and defines the headers.

After getting IIS to pass the headers, I ended up editing a bunch of core WordPress files to make it use those forwarded headers.

Now, I’m working on submitting those changes to WordPress.

Leave a Reply

Your email address will not be published. Required fields are marked *