July 7, 2004

Hacking WordPress with Microsoft’s IIS Web Server

Making WordPress work on Microsoft’s IIS server had a couple tricky issues that I think are worth writing down.

Now that I’ve finished the entry, I’ve noticed that the only IIS-specific items are #6. Permalinks Formatting, and #8. RSS Comments Feed, the rest of the entry is about the other issues I encountered getting the site up and running. Hopefully some of this information will be useful to others.

  1. Installation and Configuration

    I won’t bore you with the details of getting WordPress up and running because there are several places that describe installation better than I can. I will say though that I use phpMyAdmin to manage my MySql databases. Much easier than managing it through the command line.

  2. Customizing index.php

    index.php is the primary user-visible page in your WordPress site. WordPress generates pages dynamically, and most pages bottleneck through this single page/template, using query parameters to define whether it’s the front door, a single story, a monthly archive, or another page in your site.

    I moved a lot of things around in index.php because I wanted it to look like my old site. I also tweaked the css files quite a bit as well (see next item). In particular I wanted to move the “posted by” info to the bottom of each post, and I didn’t like the way that the side bar was generating sections and links. So, I went in and munged around with the template, added a couple of custom php functions, and a couple of hours later I had a site that looked almost identical to my old site. But better.

    Here’s the index page I finally settled on.

  3. CSS Files

    I searched for “wordpress templates” on Google, and found this site that had lots of example WordPress templates. I liked the look of several of them, so I downloaded them, and took a look at their css files. None of them were exactly what I wanted, but with a couple of hours of prodding and poking, I was able to shoehorn my previous css styles into the WordPress css structure.

    You’ll find my two css files here and here.

  4. Problems with Writing Options

    I started out selecting a couple of writing options (Admin -> Options -> Writing), such as “Formatting: Convert emoticons like :-) and :-P to graphics on display” and “Formatting: WordPress should correct invalidly nested XHTML automatically”. I also liked the looks of the “Search-hilite” plug-in as well as the “Textile 2″ plug-in (both available under Admin -> Plugins).

    They all seemed to work fine with short test posts, but one or the other would barf when the post got long, or when there were lots of html tags in the code. At one point I could see that my posts got entered in the database, but the page would never return. I tracked it down to getting stuck in calls to do_action('publish_post', $post_ID); and do_action('edit_post', $post_ID);.

    Instead of tracking down the actual problems, I simply turned off both “Formatting” options and both plug-ins. Since then the problems seem to have gone away.

  5. Fixing Comment Moderation and the Spam Filter

    Comment spam is a big problem on this little site, I can’t imagine how much of a problem it must be for sites with more traffic. I was excited about trying out the “Comment Moderation” feature (Admin -> Options -> Discussion). I grabbed the spam words from the suggested location and entered them into the Comments Moderation field.

    Then I did a test by adding a couple of comments with spam words. But the comments didn’t get moderated as I thought they should; instead they appeared in the comments section, spam words and all.

    What was wrong I wondered? I re-read the Comment Moderation page and noticed that it said “Separate multiple words with new lines". But the spam words that I had gotten from the link weren’t separated by lines, and I kind of preferred being able to see all the spam words without scrolling, so I went digging in the code.

    I found the offending function, check_comment(), in /wp-includes/functions.php. To have your spam words separated by spaces instead of lines, look for the line that says $words = explode("n", get_settings('moderation_keys')); (that’s a backslash-n or return character) and change it to $words = explode(" ", get_settings('moderation_keys')); (that’s a space character). With this change each of the words in the Comments Moderation field gets checked one at a time, and comments with any of the spam words will be put in the moderation queue.

  6. Permalinks Formatting

    WordPress allows you to specify whether you want Permalinks (more accurately all the links on your site), to be specified using parameters, like this: /index.php?year=2004&monthnum=07&day=07, or using paths, like this: /feed/2004/07/07/. The latter format is preferred because search engines can be touchy about following links with lots of parameters, as it’s harder for them to determine if they’ve gotten into an infinite loop.

    The Permalinks configuration page (Admin -> Options -> Permalinks), is where you specify the permalinks format. The two most common formats are /archives/%year%/%monthnum%/%day%/%hour%/%minute%/%second%/ or /archives/%year%/%monthnum%/%day%/%postname%/.

    The former produces links that look like /archives/2004/07/07/12/01/01/ and the latter produces links that look like /archives/2004/07/07/weblog_entry_title_goes_here/. I chose the former format because it allows me to change the title of a piece without changing the permalink.

    But choosing the format was easy, the real problem is that IIS doesn’t have a built-in rewrite rule handler like Apache does. Luckily I’d looked at rewrite rule handlers for IIS in a former life, and with a quick glance at Google found the one I’d used before – ISAPI_Rewrite. I installed the Lite version (free), and after a little futzing with the rewrite rules, got it working.

    Here’s how I did it:

    1. I downloaded and installed the Lite version from here.

    2. You can configure IIS to use ISAPI_Rewrite on all sites, or you can install it on only individual sites. I chose the latter.

      To install ISAPI_Rewrite on a single site, open the IIS Internet Service Manager. Right-click on the site you want to install it on, and choose “Properties” from the popup menu. Select the “ISAPI Filters” tab, then click the “Add” button. Name it “ISAPI Rewrite", and click “Browse” to find the ISAPI_Rewrite.dll in the install location. Choose the ISAPI_Rewrite.dll, then click “Open” to choose the file. Finally, click “OK” to close your site’s properties panel.

    3. Now you have to add some Rewrite Rules to the ISAPI_Rewrite.dll httpd.ini file. You’ll find this file in the same folder as the ISAPI_Rewrite.dll file.

      A nice WordPress feature is that at the bottom of the Permalinks page they provide the Apache Rewrite Rules for the Permalinks format you’ve chosen. What they provide there is very close to what we need for ISAPI_Rewrite, but we will have to tweak it a bit.

      The first three lines of the WordPress rewrite rules for my Permalinks options looked like this (notice that the last line is wrapped here):

      RewriteEngine On
      RewriteBase /
      RewriteRule ^archives/category/(.*)/(feed|rdf|rss|rss2|atom)/?$ /wp-feed.php?category_name=$1&feed=$2 [QSA]

      I changed them to read as follows (again the last line is wrapped):


      [ISAPI_Rewrite]

      RewriteRule /archives/category/(.*)/(feed|rdf|rss|rss2|atom)/?$ /wp-feed.php?category_name=$1&feed=$2 [I,U,O]

      What did I do? I threw the first two lines away (RewriteEngine and RewriteBase), changed the ^ at the start of the first RewriteRule from a ^ to a /, and changed [QSA] to [I,U,O] at the end of the line. That’s it. Now you can do the same to the rest of the rewrite rules – change the initial ^ to a /, and the final [QSA] to [I,U,O]. Save the file, and you’re done.

      Now, because we’re using the Lite version, we need to restart IIS (in fact you need to do this whenever you make changes to the ISAPI_Rewrite rewrite rules). So, fire up a command window, type “iisreset /restart", hit return, wait for IIS to restart, and you’re in business – Permalinks as they’re supposed to be.

    4. You can see my rewrite rules here.

  7. More Comments Problems

    With the rewrite rules in place, I added a test entry. But something was missing, there were no comments, and no place to add a comment. Hmmm, looks like more digging was going to be required.

    To make a long story short, I had to change code in one place:

    In wp-blog-header.php change

    if (1 == count($posts)) {
       if ($p || $name) {
         $more = 1;
         $single = 1;
       }

    to
    if (1 == count($posts)) {
      $single = 1; // <== add this line here
       if ($p || $name) {
         $more = 1;
         $single = 1;
      }

    This hack forces $single to true when the number of posts = 1. This has the side-affect that comments show on the home page when only one post is showing. But I decided I could live with that until the WordPress guys fixed it properly.

  8. RSS Comments Feed

    The last thing I had to debug was the RSS comments feed. When you’re looking at an entry with comments, there’s a link that says “RSS feed for comments on this post". Clicking on it did nothing on my machine, so I went digging in the code again.

    In the end I needed to make two changes, one a code change, the other a rewrite rule change.

    First the code change. In wp-feed.php change
    if ( (($p != '') && ($p != 'all')) || ($name != '') || ($withcomments == 1) ) {
      require(’wp-commentsrss2.php’);
    }

    to

    if ( (($p != '') && ($p != 'all')) || ($name != '') || ($withcomments == 1) || ($feed == 'rss2_comments') ) {
      require(’wp-commentsrss2.php’);
    }

    Note the addition of || ($feed == 'rss2_comments') to the if statement.

    I think the bug is that $withcomments never gets set anywhere. Instead of trying to figure out where that should be getting set, I simply check the value of $feed.

    Now to the rewrite rule change. You’ll notice that the RSS comments URLs look something like this /archives/2004/07/06/23/15/22/rss2_comments/. Notice the /rss2_comments/ at the end of the URL. We need rewrite rules that handles that case.

    I re-opened the ISAPI_Rewrite httpd.ini file and changed all of the rewrite rules that had this in them (feed|rdf|rss|rss2|atom) to this instead (feed|rdf|rss|rss2|atom|rss2_comments).

    And of course, remember to restart IIS after you make these changes.

    Again, you can see my rewrite rules here.

That’s it. For now. I’m up and running, but I know I haven’t found all possible bugs. And I looked at the latest CVS build, and wow there are a lot of changes. I can see that keeping up with WordPress is going to more work than what I’ve been used to with MovableType. But I can also see that I’m going to get a lot of value for that effort, so I think it will be worth it.

Enjoy!

Posted by: Frank @ 3:16 pm — Filed under:

17 Comments »

  1. Thank You for having the only online documentation of setting up WordPress on WIMP (Windows-IIS-MySQL-PHP). It got it through a tough spot of setting up permalinks.

    I did have one addition that I have to make to my httpd.ini file. Because of this I placed a full reference of your description on blog with my addition. It can be found @ http://blog.bluecrescenttech.com/archives/2004/07/29/wordpress-permalink-formatting-and-wimp/

    Once again, thanks for the documentation!

    Comment by DA Williams — July 29, 2004 @ 6:01 pm

  2. Thanks for the info about isapi_rewrite. I installed in on my server and made my httpd.ini like yours. I rebooted the server. Do i need to do anything in Wordpress? For permalink, i cahnged to /archives/%year%/%monthnum%/%day%/%hour%/%minute%/%second%/

    Anything else i need to do? what about existing blogs? I click on the “read more” and i get file not found. When i create a new blog, the link generated gives me file not found. What am i doing wrong?
    Thanks!

    Comment by loc — September 10, 2004 @ 4:50 pm

  3. Couple of things to try:
    1. Take a look at your server log and see what error it’s recording.
    2. You might need to remove the [I,U,O] at the end of each of the lines, that way your server will record the original request, not the rewrite request
    3. Look in the httpd.parse.errors file (in the ISAPI_Rewrite folder) and see if there’s anything there.

    Let me know…
    – Frank

    Comment by Frank Leahy — September 10, 2004 @ 4:57 pm

  4. figured it out. Your rewrite rules assume wordpress is at root folder. I had to add /blog/……. (so it looks like /blog/archives/…..etc) since wordpress is inside the blog folder of my site. works great now! thanks so much for the information!!!!

    Comment by loc — September 11, 2004 @ 11:57 pm

  5. one thing i noticed about this current web page is that it displays only half of the page (up to #4. Problems with Writing Options) and chops off the rest. I had to refresh the page and it then will load the entire page. This happened on many occasions as I was reading this page.

    Comment by loc — September 12, 2004 @ 12:00 am

  6. Thanks for insight - your instructions couldn’t have been any simpler! Worked a charm - I have a lot of typing ahead of me :)

    Comment by Peter Graves — January 9, 2005 @ 5:27 am

  7. I’m thinking about running Wordpress and MovableType simultaneously on the same Windows 2003 server (ActiveState Perl 5.8/PHP 4.3.11/MySQL/IIS 6.0) until I’m comfortable with the switch. My question is will the ISAPI_Rewrite Lite affect my MovableType blog entries? I’m not familiar how tightly integrated this is with IIS 6.0 and if it will cause URLs to be rewritten on my MT installation as well.

    I think the rewrite rules take care of which URLs are rewritten, but just want to be sure before I install it.

    Any other issues I should be aware of with having Wordpress and MovableType on the same server both accessing MySQL locally?

    p.s. I just blogged about my experience upgrading from Berkeley on MovableType to MySQL running on IIS 6.0 you might find useful.

    Comment by Tom Keating — April 8, 2005 @ 7:01 pm

  8. Just a little suggestion, you should have provided an example ISAPI_Rewrite rule for the following permalink string:

    /archives/%year%/%monthnum%/%day%/%postname%/

    Since you mention it in your Article here, I was trying to use that, but it wouldn’t work because I didnt have the rules, and didn’t have the time to figure out how to do it, so I adapted your other permalink string which includes hours, secs, etc.

    Otherwise great article, I finally got comments and permalinking working after quite a few hours.

    Comment by Takkyu — May 18, 2005 @ 4:19 pm

  9. There’s another (much simpler) way instead of using ISAPI_rewrite.

    Check out http://codex.wordpress.org/User:ringmaster/IIS

    Cheers.

    Patrick

    Comment by Patrick — June 2, 2005 @ 10:46 am

  10. How can I get my ‘pages’ to automatically rewrite?

    Comment by Ethan — June 8, 2005 @ 11:41 am

  11. Anyone have any idea on how to make a rule if your using this format:
    /archives/%year%/%monthnum%/%day%/%postname%/

    Cheers for the awesome info.

    Comment by mainmanc — August 3, 2005 @ 5:26 am

  12. Hi,

    Here’s what I have (just change “articles” to “archives” in the below)

    RewriteRule /articles/category/(.*)/(feed|rdf|rss|rss2|atom|rss2_comments)/?$ /wp-feed.php\?category_name=$1&feed=$2 [I,U,O]
    RewriteRule /articles/category/?(.*) /index.php\?category_name=$1 [I,U,O]
    RewriteRule /articles/author/(.*)/(feed|rdf|rss|rss2|atom|rss2_comments)/?$ /wp-feed.php\?author_name=$1&feed=$2 [I,U,O]
    RewriteRule /articles/author/?(.*) /index.php\?author_name=$1 [I,U,O]
    RewriteRule /articles/([0-9]{4})?/?([0-9]{1,2})?/?([0-9]{1,2})?/?([_0-9a-z-]+)?/?([0-9]+)?/?$ /index.php\?year=$1&monthnum=$2&day=$3&name=$4&page=$5 [I,U,O]
    RewriteRule /articles/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/([_0-9a-z-]+)/(feed|rdf|rss|rss2|atom|rss2_comments)/?$ /wp-feed.php\?year=$1&monthnum=$2&day=$3&name=$4&feed=$5 [I,U,O]
    RewriteRule /articles/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/([_0-9a-z-]+)/trackback/?$ /wp-trackback.php\?year=$1&monthnum=$2&day=$3&name=$4 [I,U,O]

    You’ll notice that this allows A Year In Cornwall to respond to both

    http://cornwall.backtalk.com/articles/2005/2/12/questions-about-cornwall-life/

    and

    http://cornwall.backtalk.com/articles/questions-about-cornwall-life/

    Regards,
    – Frank

    Comment by Frank Leahy — August 3, 2005 @ 10:54 pm

  13. Thanks for the info about isapi_rewrite. I installed in on my server and made my httpd.ini like yours. I rebooted the server. Do i need to do anything in Wordpress? For permalink, i cahnged to /archives/%year%/%monthnum%/%day%/%hour%/%minute%/%second%/ http://www.welmar.pl

    Comment by Welmar — November 15, 2005 @ 1:50 pm

  14. I’ve got the rewrites working for my posts, but not for my pages, any quick tips that could set me on the right track?

    Comment by Michael — June 15, 2006 @ 5:15 am

  15. Thanks for documenting this! I’ve just added a link to this page from the WordPress page at Well Designed URLs Initiative wiki.

    Comment by Mike Schinkel — November 27, 2006 @ 10:52 pm

  16. This tutorial is exactly what I needed to finally get my “pretty” URIs working right!

    All of the links that I have work, except for one. I’m currently thinking that the problem might be related to the fact that I had quotation marks in the title when I created it, and as such the URI tries to incorporate those, which results in having this in the URI: %e2%80%9clost%e2%80%9d

    Is there a way to have Wordpress disregard quotation marks just like an other punctuation mark (such as exclamation and question marks)? Or, is there a rule that I would include in my httpd.ini file to compensate for this?

    Comment by Tim — December 9, 2006 @ 1:49 am

  17. Brilliant post, everything I needed to get my blog running under IIS. I’ve posted an article with about it with a backlink here: http://www.stevekinsey.com/archives/2007/02/16/07/17/10/

    Comment by Stephen Kinsey — March 7, 2007 @ 10:33 am

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

(required)

(required)


In an effort to control spam, please fill in the result of the equation below