URL Rewrite 2.0 Performance

Do performance work it is easy when you have the right tools for measuring gains or lost. I will share some thoughts about how to improve performance during rewriting, but please keep in mind that any change you do must be well thought and with performance numbers in hand. If you need advice, please use the IIS forums: https://forums.iis.net/1152.aspx

Inbound Rules

URL Rewrite 1.1. and 2.0 implement an internal cache of rewritten URLs to avoid evaluating the rules every request.

For inbound rules, performance work is more about understanding the rewrite cache and how it works. There are many cache levels in IIS: Output User Mode Cache, Kernel Mode Cache and Rewrite Cache. Please note that application frameworks (e.g. ASP.NET and PHP) can use other caches.

Rewrite Cache Policy

The rewrite cache policy is based on how frequently an URL is hit. If the URL is requested once in a while, the Rewrite Engine will execute all the rules, otherwise it will take the final action from the rewrite cache. One easy way to know if a particular request was served from the cache, is by enabling tracing. For rules resolved from the rewrite cache, you will find events like REWRITE_FROM_CACHE_ACTION, REDIRECT_FROM_CACHE_ACTION, NO_ACTION_FROM_CACHE and NO_MATCH_FROM_CACHE.

IIS determinates if an URL is frequently hit by querying the properties frequentHitThreshold and frequentHitTimePeriod. If the values are 2 and 10 seconds it means that “if the URL is hit 2 times within 10 seconds” it will be marked as frequent hit and the Rewrite Engine will use the rewrite cache. If the time period is increased, the Rewrite Engine will cache more often, that could mean more memory usage, but probably less CPU cycles.

Kernel Mode Cache

The Kernel Mode Cache was introduced a while ago and it allows the Windows Operating System to cache objects without even reaching IIS. You can learn more at https://learn.iis.net/page.aspx/154/walkthrough-iis-70-output-caching/

If your application leverage the kernel mode cache, it is very important to be aware that the Rewrite Engine can potentially disable it and affect throughput. Take a moment to read the section “Interaction with IIS Output Caching” at https://learn.iis.net/page.aspx/465/url-rewrite-module-configuration-reference/

Starting from URL Rewrite 2.0, you can enable tracing and look for the event REWRITE_DISABLED_KERNEL_CACHE. If it is present, analyze the rules’ evaluation and avoid the use of headers and server variables that are not URL derived as described in the “Interaction with IIS Output Caching” section.

Global Rules

Global rules are executed in a very early stage, before the IIS Pipeline is executed indeed. Distributed rules (e.g. at Web Site level) are execute within the pipeline and if a rewrite action happens, it needs to create a child request to re-execute the pipeline with the new URL. Creating a child-request is an expensive operation by nature, thereby one benefit of global rules is that it do avoid the creation of a child request and can have better performance for some scenarios.

The downside of global rules is that they don’t have any rewrite cache at all and the cost of evaluating the rules will be paid every request. If you have just one rule that does a simple matching and rewrite or redirect, it is very likely that it will have better performance than a distributed rule, but if the rule or rules get more complex, it may be better to use distributed rules to leverage the rewrite cache.

For Global Rules is strongly recommended to use Wildcards matching instead of Regex (ECMAScript) since the matching will be done every request.

Linear Evaluation O(N)

Rewrite rules are evaluated linearly, that means that we can use some well known techniques for reducing the cost.

Reorder rules

When your rewrite rules don’t have any kind of workflow, or chaining between rules (i.e. output of Rule#1 is input of Rule#2), you can try to reorder the rules so the most likely to hit rule would be the first one. For example, if the URL “a” is most frequently hit than “x”, you could reorder the rules like:

Rule#1 match “a” rewrite “b” stopProcessing=true

Rule#2 match “x” rewrite “y” stopProcessing=true

Use Rewrite Maps

A rewrite map has almost a constant evaluation O(1). That can help to make quick matches for well known values or even partial values. Read more here: https://learn.iis.net/page.aspx/469/using-rewrite-maps-in-url-rewrite-module/

Using rewrite maps for some strings and substrings look ups can lead to have a really complex rule, make sure that you will have real benefits from it by testing it. For example, a rule set of 3-5 rules in distributed rules, will probably won’t benefit from rewrite maps, because distributed rules can use the rewrite cache that also has an efficient O(1) complexity, but if the rules are global, they may have a benefit.

In the other hand, if the cost of adding a new rule is higher than adding a rewrite map entry (like a rewrite map for user-agents or domain mapping), it may be better to use rewrite maps.

Keep in mind that URL Rewrite 2.0 supports .NET Extensibility in order to create new kind of map functions:

https://code.msdn.microsoft.com/rewriteextensibility

Outbound Rules

Outbound rules can be very expensive because you will be matching and rewriting high volume of data.

Reduce Regex use

Use ExactMatch matching instead of Regex (ECMAScript) whenever is possible. Regex are expensive.

For rules with HTML filters (“a”, “img”, etc.) you can also use Wildcards. For rules that try to match the entire response body you cannot use wildcards because the matching is greedy, instead try to use a simple ExactMatch.

Reduce Evaluation Groups

Evaluation groups is a concept used by URL Rewrite to group rules with HTML filters and without HTML filters. Having the following outbound rule set:

Rule#1 filter=”Img” match=”body.png” rewrite=”images/body.png”

Rule#2 filter=”A” match=”*.html” syntax=”Wildcards” rewrite=”pages/{R:1}.aspx”

Rule#3 filter=”None” match=”internal.contoso.com” rewrite=”contoso.com”

Rule#4 filter=”Script match=”*” syntax=”Wildcards” rewrite=”scripts/{R:1}”

For that rule set, 3 evaluation groups will be created. The first one has both Rule#1 and Rule#2; the second one is just Rule#3 and the last one is Rule#4.

Each evaluation group is evaluated against the entire response and multiple evaluations require to buffer the entire response before sending it to the client. To minimize the number of evaluation groups, reorder the rules (if possible) to group the rules with and without filter:

Rule#1 filter=”Img” match=”body.png” rewrite=”images/body.png”

Rule#2 filter=”A” match=”*.html” syntax=”Wildcards” rewrite=”pages/{R:1}.aspx”

Rule#4 filter=”Script match=”*” syntax=”Wildcards” rewrite=”scripts/{R:1}”

Rule#3 filter=”None” match=”internal.contoso.com” rewrite=”contoso.com”

Rules with filters are by default chained. The output of the Rule#1 will be the input for the Rule#2 and the output of Rule#2 the input of Rule#4. If that is not what you need, set the stop processing property to true.

Rule#1 filter=”Img” match=”body.png” rewrite=”images/body.png” stopProcessing=”true”

Rule#2 filter=”A” match=”*.html” syntax=”Wildcards” rewrite=”pages/{R:1}.aspx” stopProcessing=”true”

Rule#4 filter=”Script match=”*” syntax=”Wildcards” rewrite=”scripts/{R:1}” stopProcessing=”true”

Rule#3 filter=”None” match=”internal.contoso.com” rewrite=”contoso.com”

URL Rewrite 2.0 RTW supports tracing for outbound rules, enable tracing to learn more.

Rewrite before do output caching

Rewrite Module can perform the rewriting of the response body before the IIS Output User Mode Cache saves the response, so next time the rewritten response body can be served from the cache without evaluating any outbound rule. Look for the rewriteBeforeCache property at https://learn.iis.net/page.aspx/665/url-rewrite-module-20-configuration-reference/

This is a huge optimization, but it won’t work for chunked-transfer encoding. The reason is that the entire response body must be available for doing the matching with accuracy.

If you need to know if your application send these response, download wfetch client and look if the response contains the Transfer-Encoding header set to chunked. You can also enable the “Raw View” checkbox to see how the actual response body is encoded.

Conclusion

Hope this information can give you a startup for improving performance of rewriting in high performance scenarios, remember that URL Rewrite is only one piece of your application and even with a highly optimized rewrite configuration, the gain could be marginal when you see the full picture.