Troubleshooting the Azure Standard CDN Rules Engine

Azure CDN Code

I was recently updating a few things on this site, including the rewrite/redirect rules that control what content is served. I’d moved this site to an Azure static website a while ago, but had never really set up the redirects properly. I chose to use the standard tier Microsoft CDN, but was frustrated by lack of documentation on how to actually use the rules engine. After pulling my hair out for a few days, I finally realized a way to troubleshoot the rules engine and thought I’d share it here. We’ll cover:

If you want to skip to the meat of the post:

What was I trying to do?

Azure doesn’t offer a free SSL cert for apex domains (i.e. scenic-shop.com), but does offer free SSL certs for subdomains (e.g. www.scenic-shop.com, or calculators.scenic-shop.com). Since I don’t feel like paying for a cert, I’m using subdomains for the site. However, since I want the site to be accessible at the apex domain, we have an additional requirement:

In order to preserve existing links to the site, the links need to stay the same. The blog’s front page is currently hosted at www.scenic-shop.com/blog/index.html and I didn’t want to move it to www.scenic-shop.com/index.html for tooling reasons. Therefore:

Furthermore…

Initial Solution

My initial solution was to check the Microsoft documentation and write some rules. Nothing a little searching couldn’t solve, right? Turns out the documentation for the Azure standard CDN is very sparse. There are one or two examples of actual rules, and the descriptions of conditions and actions are very general. They describe what they are, and what the major portions are, but do not describe very well how they actually work. Likewise, searching out examples of rules on the internet proved frustrating. There were a few, but mostly just the same examples over and over.

So with the documentation in hand, I took a stab at writing four rules that would solve my problems. And after an indeterminate amount of time discovered that they didn’t work. Unfortunately it was impossible to determine which rule was causing what behavior, and which one wasn’t finding a match, or erroring out. As someone who utilizes unit tests when coding, I realized how dumb I was being. A few realizations hit me and we’ll start keeping track of these here…

  1. Write your rules one at a time and test them in isolation.
  2. Keep a log of what rules you’re editing, and the results of tests for that rule. (That way you can always go back).

Part of the frustration is that I kept finding inconsistent results in various browsers on various devices. Azure tells you that endpoint rules engine changes should take effect within 10 minutes. But the behavior I was seeing was all over the map; ten minutes, four hours, it didn’t seem to matter. Oh wait, browsers keep caches right? Another dumb mistake.

  1. Clear your browser cache before testing. (Firefox has a shortcut that makes this quick work: refresh the page with ctrl+shift+R to reload and clear the cache for that page.)

After getting a few rules working, I geared up for the /url/ => /url/index.html redirect rule. But before digging in:

  1. Run the test without the rules in place to determine a baseline behavior.

Turns out I didn’t need to write that rule. It’s already baked into Azure. I’m not sure if it’s in the CDN, static website configuration, or what, but it works out of the box.

Finally, I was at the last rule, I needed to redirect www.scenic-shop.com to www.scenic-shop.com/blog/index.html but it just was not working. I didn’t even know what was working or not. Was the match condition not working? Was the action not working? Was the rule not being applied? I had no idea. I tried several variations on the rule but nothing worked consistently.

After a few days I realized a better way to test this. I’m sure modern web-api developer is rolling their eyes at how long this took me to figure out. In my defense, I’m not a web developer. I was reading up on HTTP standards and realized that you can modify response headers in the rules engine. I could use this to indicate if a rule was being applied or not. I could also use it to indicate if the rule was matching or not. I could even use it to indicate if the rule was being applied at all. This would be the mechanism I could use to troubleshoot the rules.

  1. Use the rules engine to modify response headers to indicate if a rule is being applied, if it’s matching, or if it’s not matching.

There is probably a built-in method to do this with Azure CDN logs, but the header method was a simple way to do it that only took a few minutes to implement.

The Test Setup

To setup this test, delete all existing rule engine rules for the CDN endpoint in question. (Remember to back them up or write them down first so you can recreate them.)

  1. Label your tests with a label for identification. E.g. Test A, Test B, etc. Change this label for every different test you run.

  2. Create a label that acts as a control for your tests. Edit your global rule to append a http header to all responses with a custom header name and label identifying the test. E.g. debug-control: B.

This is incredibly important for reasons I’ll point out shortly.


Global Rule

Action Header Name Value
Append debug-control B

  1. Write a test rule with the action to append a http header indicating the success of the test, as shown below:

Test Rule


Running the Tests

Load the website in a browser, open the developer tools and show the site HTTP headers. Refresh the page with a clean cache, and now you have a way to:

If the debug-control header does not match your current test, you know the rules engine is not running the latest version of your rules. Wait a few minutes and refresh again. One the debug-control header matches your current test, the presence of the debug: header indicates if a match was found.

Results

These are the rules that I’m currently using:

Rule: EnforcedHTTPSv2

Rule: ApexRedirect

Rule: blogIndexRewritev5

Notes:

Conclusion

Since figuring out this workaround for testing the rules engine, it only took me a half an hour or so to get the rules working as I’d hoped. I know there have to be better ways to do this, even to automate it if you want to, but the above manual method sufficed for my needs. A few possibilities come to mind (again… this guy is not a web-api dev; I’m sure there are a lot more):