Speed up WordPress on IIS 7.0
This post has been updated to ensure that instructions are correct for the latest versions of the WP Super Cache plugin and IIS URL Rewrite Module.
The performance of WordPress may be sufficient for an average blog that gets a few page hits per minute. However, if your blog post suddenly shows up on digg.com or any other social networking site, it may become challenging for server to handle such huge spike in traffic. A few options exist to help server to cope with flood of requests:
- IIS Output Caching
- WP Cache plugin for WordPress
- WP Super Cache plugin for WordPress
In this post I will explain the benefits and drawbacks of each option and walk through the steps for configuring IIS and WordPress to use those options.
Prerequisites
Before performing the configuration steps described in this post, it is necessary to complete the following prerequisites:
- Install WordPress (Follow the instructions in this article or use the instructions from the official WordPress site).
- Install Microsoft URL Rewrite Module 1.1 for IIS 7
- Enable “Pretty Permalinks” in WordPress by following steps described in this article.
IIS Output Caching
IIS 7.0 has a very powerful output caching infrastructure, which can cache response data in kernel-mode (http.sys) and in user-mode memory space (IIS worker process). The Output Caching can be used with static web resources as well as with semi-dynamic web applications, where the content is generated dynamically, but is not likely to change from request to request. A blog engine is a perfect example of semi-dynamic web application.
To enable IIS Output Caching for WordPress you can use IIS Manager UI:
Or add this configuration section into the web.config file located at the root folder of the WordPress site:
<caching> <profiles> <add extension="*" policy="CacheForTimePeriod" kernelCachePolicy="CacheForTimePeriod" duration="00:00:30" /> </profiles> </caching>
With this configuration IIS will cache the response generated by WordPress in memory for 30 seconds, thus avoiding heavy script processing and database I/O for all subsequent requests. This would increase the throughput significantly – for example on my Virtual PC the WordPress throughput grew from 10 requests per seconds to more than 2500 requests per seconds after enabling Output Caching.
However, there are certain drawbacks from using Output Caching. Some of the dynamic blog features are lost when cached page is served instead of a page generated by WordPress. For example, when a visitor submits a comment for a blog post there will be no confirmation message shown to indicate that the comment has been submitted. This is because the page shown to the user after comment submission is served from cache. This may cause visitor to submit same comment several times. This problem can be somewhat mitigated by reducing the cache timeout, but there is still a chance that stale page may be served to a visitor after comment submission. To reliable prevent this from happening a more customized solution for WordPress is necessary, which is a WP Super Cache plugin.
WP Cache and WP Super Cache
WP Super Cache is a very popular plugin that generates temporary static html pages from dynamic WordPress blog. After a html file is generated the server will serve that file instead of processing the comparatively heavier and more expensive WordPress PHP script. Since this is a WordPress-customized solution, it is able to smartly handle such situations such submission of comments or serving pages to logged on users. Specifically, the plug in will serve static html pages only to:
- Users who are not logged in
- Users who have not left a comment
- Or users who have not viewed a password protected post.
Typically 99% of the visitors do not do any of the above actions, so the static html pages will be served most of the time, thus increasing server throughput significantly.
WP Super Cache plugin has two modes of operation: WP-Cache and WP-SuperCache. With WP-Cache the static files are still generated but every request will require loading of PHP engine to serve those files. WP-SuperCache relies on URL rewriting to bypass PHP altogether when serving cached static files.
To configure WP Super Cache on IIS 7.0 follow these steps:
Step 0: Make sure that wp-content folder is writable by web server
Step 1: Download the WP Super Cache plugin and unpack it into the WordPress plugins folder “wp-content\plugins”. This will create “wp-content\plugins\wp-super-cache” folder.
Step 2: Copy the file “wp-content\plugins\wp-super-cache\wp-cache-phase1.php” to “wp-content\advanced-cache.php”
Step 3: Add the following rewrite rule to the web.config file located at the root folder of WordPress site. Make sure that the “WP Super Cache” rule is before the WordPress rule for pretty permalinks. Typically the <rewrite> section will look as below
<rewrite>
<rules>
<rule name="wp super cache" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_METHOD}" pattern="^POST$" negate="true" />
<add input="{QUERY_STRING}" pattern=".*=.*" negate="true" />
<add input="{QUERY_STRING}" pattern=".*attachment_id=.*" negate="true" />
<add input="{HTTP_COOKIE}" pattern="^.*(comment_author_|wordpress|wp-postpass_).*$" negate="true" />
<add input="{HTTP_USER_AGENT}" pattern="^.*(2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800).*" negate="true" />
<add input="{DOCUMENT_ROOT}/wp-content/cache/supercache/{HTTP_HOST}/{R:1}index.html"
matchType="IsFile" />
</conditions>
<action type="Rewrite" url="wp-content/cache/supercache/{HTTP_HOST}/{R:1}index.html" />
</rule>
<rule name="Wordpress Pretty Permalinks" patternSyntax="Wildcard">
<match url="*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
Step 4: Add the following line into the wp-config.php file above the “require_once(ABSPATH.’wp-settings.php’);” line:
define( 'WP_CACHE', true );
Step 5: Login to WordPress and activate the plugin by going to “Plugins” tab. Then go to “Settings” tab and click on “WP Super Cache”. If everything was configured correctly you should see the plugin’s configuration page.
Step 6: Go to “wp-content\cache” folder, which should have been created when plugin was activated. Create a new web.config file there, open it and paste the following content inside:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="Cache-Control" value="max-age=300, must-revalidate" /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
This configures IIS to send Cache-Control response header for any of the cached html pages. That header will force the browser to expire the locally cached response after 5 minutes.
To check that the WP Super Cache actually works, set its status to ON in the plugin configuration page, then clear all the browser cookies and make a request to the blog page. Alternatively you can use WFetch to make a request. If WP Super Cache works then when you make a request to http://wordpress/2008/04/hello-world/, the response will be cached as a static file in \wp-content\cache\supercache\wordpress\2008\04\hello-world\index.html. When next time you request the same page, the rewrite rule “WP Super Cache” will rewrite the URL to the “wp-content\cache\supercache” folder, thus IIS will serve static html file without even invoking PHP engine.
Conclusion
Here is the comparison of server throughput in requests per second with three output caching solutions for WordPress when requesting http://wordpress/2008/04/hello-world:
The performance of WP Super Cache is comparable with performance of IIS Output Caching because with WP Super Cache IIS is just serving static files, which is what IIS is very good at.
So, when to use which option? Here are the pros and cons of each caching solution:
| Pros | Cons | |
|---|---|---|
| IIS Output Caching |
|
|
| WP-Cache |
|
|
| WP-SuperCache |
|
|



Very nice article!
But “That header will force the browser to expire the locally cached response after 5 seconds.” must be “… after 5 minutes.”
Thanks for correction, rukeba! I’ve updated the post.
Thank you for this post. Is it good or bad to use both methods of caching? Or is it just unnecessary?
In addition – I run a private blog that doesn’t get much traffic. Would IIS caching improve performance where Super-Cache won’t?
I guess both methods – IIS Output Caching and WP Super Cache – can be used at the same time, but I do not think there will be any benefit from using them together.
On a low traffic blog you would not probably notice any benefit from using IIS output caching. Typically, to keep the dynamic nature of the blog you would need to set the cache expiration time to relatively short period, e.g. 30 seconds. If your blog gets one visitor in 5 minutes, the cache will be almost never used.
Right, that makes total sense. I’m just trying to improve performance. Much of that in my case will probably come down to reducing http request and database calls, and Wordpress has somewhat of a reputation of being heavy on that end.
Thanks for your articles on this front by the way, I am impressed with how well it works. Now all I need for IIS is a built-in (probably never going to happen) Java server or an EASY way to tie into Jetty for my JSP pages.
excellent article! thanks for sharing.
In step 4 of “configure WP Super Cache on IIS 7.0″, seems we should add … as format below
…
…
gosh, seems I can’t add code in comments. what I say above is adding “rules” and “/rules” for step 4 of enabling wp-super-cache on IIS7.
My Wordpress permalinks is something as http://site/2009/01/100.html (/%year%/%monthnum%/%post_id%.html). After following your steps and changing the matchurl (step4) to “^(\d{4})/(\d{2})/(\d+).html$”, I noticed in the supercache folder only have folders but not cache files.
For example, by visiting URL http://site/2009/01/100.html, I can find folder \wp-content\cache\supercache\site\20091\100.html (yes, 100.html is a folder name but not file!!). However, no files in this folder. I supposed the plugin should create folder “100″ instead of “100.html”. Do you think it’s the root cause? Did I miss some steps?
Thanks in advanced!
Thanks eddieliu, I’ve fixed the rules example code as you mentioned.
Regarding the permalink structure. I have not tried wp-super-cache with permalinks that have file extensions at the end but from looking at the plugin implementation I suspect it always uses index.html (or index.html.gzip) as a file name.
The cache folder may be empty because you are making request from the browser that already has a wordpress cookie saved. In that case the plugin will not create index.html file, even though it will create a folder. One thing you could try is to clean up all the cookies in the browser and then request the URL on your blog. Or use something like WFetch to make a request. See if in that case the index.html file gets created.
Haa… super-cache worked if I accessed from another client… Cool. Thank you, Ruslan.
At first thank you for the useful article.
I did everything that you’ve explained but when I “ON” wp superscache, i get the error below
Warning: include(C:\inetpub\vhosts\filmgani.com\httpdocs/wp-contentC:/inetpub/vhosts/filmgani.com/httpdocs/wp-content/plugins/wp-super-cache/wp-cache-base.php) [function.include]: failed to open stream: Invalid argument in C:\inetpub\vhosts\filmgani.com\httpdocs\wp-content\plugins\wp-super-cache\wp-cache.php on line 49
Warning: include() [function.include]: Failed opening ‘C:\inetpub\vhosts\filmgani.com\httpdocs/wp-contentC:/inetpub/vhosts/filmgani.com/httpdocs/wp-content/plugins/wp-super-cache/wp-cache-base.php’ for inclusion (include_path=’.;./includes;./pear’) in C:\inetpub\vhosts\filmgani.com\httpdocs\wp-content\plugins\wp-super-cache\wp-cache.php on line 49
I couldn’t find any solution. Could you please help me? I really want to use it. Thank you.
Cenk, it looks like the WPCACHEHOME constant is defined incorrectly.
Open the wp-content\wp-cache-config.php and check what is the WPCACHEHOME set to. It sould look something like this:
define(‘WPCACHEHOME’, WP_CONTENT_DIR.’/plugins/wp-super-cache/’);
Hey thanks for the fast reply!
I’ve added the string as you said but I only see a blank page at the moment. Whole site is blank. Here’s my wp-cache-config.php
$wp_cache_mutex_disabled = 0; //Added by WP-Cache Manager
$wp_cache_clear_on_post_edit = 0; //Added by WP-Cache Manager
$wp_cache_hello_world = 0; //Added by WP-Cache Manager
define(’WPCACHEHOME’, WP_CONTENT_DIR.’/plugins/wp-super-cache/’);
$cache_compression = 0; // Super cache compression
$cache_enabled = true; //Added by WP-Cache Manager
$super_cache_enabled = true; //Added by WP-Cache Manager
$cache_max_time = 3600; //in seconds
//$use_flock = true; // Set it true or false if you know what to use
$cache_path = WP_CONTENT_DIR . ‘/cache/’;
$file_prefix = ‘wp-cache-’;
Anything that I’m skipping?
Ok I’ve fixed it but here’s the new error
Warning: Division by zero in C:\inetpub\vhosts\filmgani.com\httpdocs\wp-content\wp-cache-config.php on line 11
Warning: Division by zero in C:\inetpub\vhosts\filmgani.com\httpdocs\wp-content\wp-cache-config.php on line 11
Warning: Division by zero in C:\inetpub\vhosts\filmgani.com\httpdocs\wp-content\wp-cache-config.php on line 11
can you please help?
Sir I’ve fixed everything. The plugin works without any problem but when I put the code step 4. I get error from the whole site as
500 – Internal server error.
There is a problem with the resource you are looking for, and it cannot be displayed.
This is the last step that I can’t skip. could you please help?
Is it possible for you to enable detailed error messages in IIS and check the error description reported with 500 status code?
Do you have the URL rewrite module installed?
Yes Ive already rewrite module installed. I didnt put the code step 4. from 01 to 19. WpCache works fine but wp supercache dont.
WP-Cache
* 3 Cached Pages
* 0 Expired Pages
WP-Super-Cache
* 0 Cached Pages
* 0 Expired Pages
if you web site is filmgani.com then it does not look like you have enabled pretty permalinks, which is necessary for super cache to work correctly.
He is the most helpful person i’ve ever seen! He fixed all of my permalink and supercache problems on my windows 2008 server! I don’t know how to thank you! Thank you millions of time!
Hi,
Great post! I’m trying to follow the installation instructions for the WP-SuperCache plugin and I have a couple of questions.
1. Instructions in Step 2 say to adjust WPCACHEHOME points to the right place. HOW would one do that? What part of that code line do you change?
define(’WPCACHEHOME’, WP_CONTENT_DIR.’/plugins/wp-super-cache/’);
My blog is in a subfolder called: devsite
Do I need to adjust that line of code at all?
If so, would this be correct?
define( ‘WPCACHEHOME’, WP_CONTENT_DIR . ‘devsite/wp-content/plugins/wp-super-cache/’ );
Question 2:
Step 4 says that if our pretty permalinks don’t use the same format as yours, we have to adjust the Rewrite code for that. How would one do that? What line of code? Is it this line?
If so, and my permalinks use the following format:
/index.php/%category%/%postname%/ then would this be the correct way to change it:
Gail, I would recommend first to leave the WPCACHEHOME as is. That worked for me. The WP_CONTENT_DIR should take care of the fact that your blog is in subfolder.
As with regards to the permalink structure – first you need to configure permalinks to not have index.php in them. Use this article to learn how to do it: http://learn.iis.net/page.aspx/466/enabling-pretty-permalinks-in-wordpress/.
After you have done this your permalink structure would probably be /%category%/%postname%/. In that case the rule would look as below:
<rule name="WP Super Cache" stopProcessing="true"><match url="^(\w+)/(\w+)/?$" ignoreCase="false" />
<conditions>
<add input="{REQUEST_METHOD}" negate="true" pattern="POST" ignoreCase="false" />
<add input="{QUERY_STRING}" negate="true" pattern=".*=.*" ignoreCase="false" />
<add input="{QUERY_STRING}" negate="true"
pattern=".*attachment_id=.*" ignoreCase="false" />
<add input="{HTTP_COOKIE}" negate="true"
pattern="^.*(comment_author_|wordpress|wp-postpass_).*$"
ignoreCase="false" />
<add
input="{DOCUMENT_ROOT}\wp-content\cache\supercache\{HTTP_HOST}\{R:1}\{R:2}\index.html"
matchType="IsFile" />
</conditions>
<action type="Rewrite"
url="wp-content/cache/supercache/{HTTP_HOST}/{R:1}/{R:2}/index.html" />
</rule>
Hi, very helpful post!
After following your instructions I managed to get super cache working however I encounter 500 errors when attempting to save or preview a new unpublished draft post. If I publish the post instead of trying to save or preview everything seems ok.
When I turn off super cache I am able to save and preview the post.
Looking at the server logs it seems like it has something to do with the way the url is being handled since it doesn’t appear as the pretty permalink in the error detail but instead as “…..blog//p=61″.
Just wondering if you have run across this problem or have any ideas on getting things straightened out?
Thanks!
Cory, what are the error details that you get with 500 error?
Just now I have tried to save the draft post and yes, I got 500 error. The reason I got it was because I had PHP setting open_basedir in effect and when the post URL was //?p=10 that somehow caused a violation of open_basedir location. If I disable open_basedir then the error does not show up.
It would be good if super cache was able to handle this error gracefully.
Yep, that’s exactly what’s happening to me too. I don’t really want to disable open_basedir so I’m currently using super cache in half on mode and it works ok.
Definitely something that super cache needs to be able to deal with before I switch it back on.
I just wasn’t sure if it was my setup or a limitation of super cache. But since you were able to duplicate it seems to be the latter.
Thanks.
I’m seeing the same thing. Any fix for this open_basedir issue?
Donncha O Caoimh, who is the author of the plugin, has replied to me about that and suggested to try the dev version of the plugin, which does not do any caching when previewing posts. That may fix this specific issue.
Thanks you so much for all your help. Every time I need help with Wordpress and IIS7 the answer is here!
So far you have helped me set up wordpress properly for IIS7, configure per site php ini files and now get caching working too!
I have configured Wp super cache on IIS 7 according to this article but I am not sure it is actually working.
When I request a page , it has the “Generated by WP-Super-Cache” html , also Cache Pages count increases in the Wp-Super cache folder and are being created.
However MySql Administrator Monitoring tools , shows 30 databases queries being made during each page request. That is the same number of requests when Super-Cache is not on.
I have tried using your Web.Config rule for Super Cache, also tried the using the two rules from the latest Super Cache version – dumped below. Either way queried are still made.
# BEGIN WPSuperCache
RewriteEngine On
AddDefaultCharset UTF-8
RewriteCond %{REQUEST_URI} !^.*[^/]$
RewriteCond %{REQUEST_URI} !^.*//.*$
RewriteCond %{REQUEST_METHOD} !=POST
RewriteCond %{QUERY_STRING} !.*=.*
RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress|wp-postpass_).*$
RewriteCond %{HTTP_user_agent} !^.*(Android|2.0\ MMP|240×320|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb |hiptop|IEMobile|iPhone|iPod|KYOCERA/WX310K|LG/U990|MIDP-2.0|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|Playstation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|Windows\ CE|WinWAP).*
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html.gz [L]
RewriteCond %{REQUEST_URI} !^.*[^/]$
RewriteCond %{REQUEST_URI} !^.*//.*$
RewriteCond %{REQUEST_METHOD} !=POST
RewriteCond %{QUERY_STRING} !.*=.*
RewriteCond %{HTTP:Cookie} !^.*(comment_author_|wordpress|wp-postpass_).*$
RewriteCond %{HTTP_user_agent} !^.*(Android|2.0\ MMP|240×320|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb |hiptop|IEMobile|iPhone|iPod|KYOCERA/WX310K|LG/U990|MIDP-2.0|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|Playstation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|Windows\ CE|WinWAP).*
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1/index.html [L]
Which version of the URL Rewrite Module do you have? If you have 1.0 then it is better to upgrade to 1.1, because the rules in this post will only work if you have 1.1.
it suddenly decided it should start to work, dont ask me how.
Just wanted to say IIS 6 , and 7 is the best platform out there , and what I setup in iis 7 in 2 days took me 2 weeks on command-interface LAMP. Sure some people love lamp , but for me nothing is giving me back these 2 weeks of googling commands.
If I use IIS way , how much time it will take till it caches the website
I tried the IIS way
But after that each time I tried to enter my website , it keeps showing mt\y different page of my website , is that a regular thing
Whenever a request for a web page or a blog post is made, it creates a cached copy. It should start caching right away.
If you see that different pages are served for the same request, then most probably the configuration is incorrect.
I did exactly as in the picture of IIS Output Caching
what could I done wrong
I found solution for it , I only needed to choose Advanced and put “*” in the string
To improve performance of your weblog you can also use Web Optimizer – http://wordpress.org/extend/plugins/web-optimizer/ . It increase client side load speed, applies gzip, merging, minify, caching, and even more for your static assets.
We have permalink structure containing “index.php” in it and we cannot remove that text. If we remove it, the super-cache works well. Is there any way by which we can have “index.php” and still super-cache works?
please help.
@amar: I do not think the super-cache will work with permalink structures that have index.php.
What is the reason not to use the standard permalink structure?
If we remove “index.php” from our blog URL it will affect our tweet count of each post.