WordPress: Running multisite on different port

Since I’m maintaining a few WordPress plugins I wanted to have a local installation where I can also make sure that new versions of the plugins also behave well in a multisite environment (site network). Unfortunately, I cannot use ports 80 and 443 since I need them for some other projects I’m working on (using IIS). So my local apache server runs on ports 8080 (http) and 8443 (https).

The problem is that this is not supported by WordPress. You need to run on ports 80 and/or 443. Otherwise WordPress won’t let you create a site network and you’ll get a message saying:

You cannot install a network of sites with your server address.
You cannot use port numbers such as 8080.

A single site installation of WordPress works fine on port 8080 but for some reason, it isn’t support for WordPres Site Networks… Well, that’s not a reason not to use it :-)

Since it’s all PHP, you can easily just fix it. Of course, you need to keep in mind that any update of WordPress might remove the changes and you’ll need to do it all again. But since it’s just a test installation, it’s not a problem for me.

So, to find out how to fix it, we first need to find out where this message is displayed. Using grep, you can quickly find that it comes from the function network_step1 in wp-admin\network.php. The relevant piece of code looks like this:

if ( ( false !== $has_ports && ! in_array( $has_ports, array( ':80', ':443' ) ) ) ) {
	echo '<div class="error"><p><strong>' . __( 'ERROR:') . '</strong> ' . __( 'You cannot install a network of sites with your server address.' ) . '</p></div>';
	echo '<p>' . sprintf( __( 'You cannot use port numbers such as <code>%s</code>.' ), $has_ports ) . '</p>';
	echo '<a href="' . esc_url( admin_url() ) . '">' . __( 'Return to Dashboard' ) . '</a>';
	echo '</div>';
	include( ABSPATH . 'wp-admin/admin-footer.php' );
	die();
}

If you use an extra :80 or :443, WordPress will not complain but it will if it is another port number. So in order not to get this error message, you just need to add the ports you need to the list, replacing:

if ( ( false !== $has_ports && ! in_array( $has_ports, array( ':80', ':443' ) ) ) ) {

by:

if ( ( false !== $has_ports && ! in_array( $has_ports, array( ':80', ':443', ':8080', ':8443' ) ) ) ) {

Of course, I assumed there was a reason why only ports 80 and 443 were supported. So I searched in the code for 80 and 443 and found this in wp-includes\ms-settings.php:

if ( substr( $domain, -3 ) == ':80' ) {
	$domain = substr( $domain, 0, -3 );
	$_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -3 );
} elseif ( substr( $domain, -4 ) == ':443' ) {
	$domain = substr( $domain, 0, -4 );
	$_SERVER['HTTP_HOST'] = substr( $_SERVER['HTTP_HOST'], 0, -4 );
}

In a previous version of this post I was adding some code for ports 8080 and 8443 but I realized it was actually making things worse. The only purpose of this piece of code is to remove unnecessary port numbers which are not relevant for comparing domain names. But ports 8080 and 8443 are relevant (https://benohead.com:8443 doesn’t point to the same web server as https://benohead.com, but https://benohead.com:443 does).

With the changes above I could create the site network. But when I tried to login, I was redirected to the IIS, to http://localhost/wordpress/wp-login.php instead of http://localhost:8080/wordpress/wp-login.php. Since I couldn’t find anything in code which would explain this, I had a look at the wp_options. And saw that the siteurl option contained http://localhost/wordpress/ instead of http://localhost:8080/wordpress/. So I just updated it in MySQL using the following SQL statement:

UPDATE `my_wordpress_db`.`wp_options` SET `option_value` = 'http://localhost:8080/wordpress/' WHERE `wp_options`.`option_name` = 'siteurl';

And tried again. And this time I got to the right login page ! Remember to replace “my_wordpress_db” by the name of your WordPress database.

I then noticed that there were still some links pointing to http://localhost/wordpress. Finally I saw that the “home” option in wp_options was still pointing to the wrong URL. After fixing it, everything looked fine:

UPDATE `my_wordpress_db`.`wp_options` SET `option_value` = 'http://127.0.0.1:8080/wordpress/' WHERE `wp_options`.`option_name` = 'home';

So now everything was working fine, the admin page, the blog post/pages. But then I realized that when creating a new site, I saw “localhost:8080″ in the UI but it was saving localhost8080 instead. So something was removing the colon. And I found the following in the function wpmu_create_blog in wp-includes\ms-functions.php:

$domain = preg_replace( '/\s+/', '', sanitize_user( $domain, true ) );

With the second parameter is true, only alphanumeric characters plus these: _, space, ., -, *, and @ are returned. Of course, one solution would be to add colon to the list but then it would allow usernames to contain colons as well. So I ended up just setting the second parameter to false, or rather commenting it out, since false is the default:

$domain = preg_replace( '/\s+/', '', sanitize_user( $domain/*, true*/ ) );

Now everything seems to be working.

While looking for a solution, I did find a few links explaining how to do it in version prior to WordPress 3.7. But I needed it for WordPress 3.9.1. So I had to find a solution by myself. Of course I have only tested it on WP 3.9.1 and haven’t checked when was last time these pieces of code were changed. So it might work on WP 3.7 and WP 3.8 as well but I cannot guarantee anything.

One thought on “WordPress: Running multisite on different port

  1. Thanks! I spent quite a while googling for a solution and yours is the only which worked. My version is WP 3.9.1, too. In my case the only thing I had to do was the first step you mention – adding my port (8888) to wp-admin\network.php. That was all. Once again – thanks for sharing!

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>