URL rewrites override each other

Hi,

I am trying to apply multiple dynamic URL rewrite rules sequentially. They each work correctly individually, but with multiple rules enabled, only the last one takes effect.

The first rule matches if:
(http.host eq "en.example.com")
And rewrites the URL path to (the query string is preserved):
concat("/en", http.request.uri.path)

The second rule matches if:
any(http.request.uri.args.names[*] == "page")
And rewrites the URL path to (the query string is emptied):
concat(http.request.uri.path, "/", http.request.uri.args["page"][0])

My expectation was that a URL like en.example.com/something?page=3 would become en.example.com/en/something/3.

Instead, only the last rule takes effect, and I end up with en.example.com/en/something or en.example.com/something/3 depending on the order of the rules.

As you can see, both rules alter the URL path, referencing http.request.uri.path. Since, per the documentation, the rules are applied in sequence, and in my testing only the last rule ends up taking effect, this leads me to believe that http.request.uri.path contains the original request value, even after going through transform rules. Is this indeed the case?

If so, is there any way I can get around this issue, and combine the results of the transformations? Additionally, if this is an intended limitation, what is the intended use-case for sequential transform rules?

Thank you.

Thats correct, the values are immutable for rules in the same phase. i.e. if you rewrite uri.path to ‘a’, it wont be set to ‘a’ until we have evaluated all rules. That means if your first rule rewrites the path to ‘a’, the second rule wont see a URI path of ‘a’. BUT, you can have one rule change the URI Path to ‘a’, and a second rule set the URI Query to ‘b’.
More here: https://developers.cloudflare.com/ruleset-engine/about/rules/#rule-evaluation-and-field-values

Have you looked at consolidating the rules, e.g. to get from en.example.com/something?page=3 to en.example.com/en/something/3:

Match on (http.host eq "en.example.com") AND any(http.request.uri.args.names[*] == "page")
Dynamic rewrite of concat("/en/",http.request.uri.path,"/",http.request.uri.args["page"][0]).

You could also look at using the http.request.accepted_languages field to make it even more dynamic, e.g. a dynamic rewrite rule of:
concat("/",http.request.accepted_languages[0],"/",http.request.uri.path,"/",http.request.uri.args["page"][0])
Would rewrite to /en-GB/something/3 for me, or potentially /de/something/3 for a visitor with a browser language of German, etc.

Thanks for the clarification, and for the documentation link. I had missed that part of the docs.

Yeah, I did consider joining my rules together as a workaround, though my example here was a bit simplified. In my actual scenario, I’ll need several more rules to account for all combinations of matches. In any case, my question has been answered.

I’ll just leave here the feedback that it would be extremely useful to have access to the transformed URI within the rules. It would greatly increase the flexibility of the (already very useful) Transform Rules system, and make it incredibly powerful. To maintain backwards compatibility, this functionality could be implemented with a new set of fields, say transformed.http.request.uri.*, similarly to how raw.http.request.uri.* currently represents another variant of http.request.uri.*.

Thanks again for the reply, and the tips. Let me add that the Transform Rules system is extremely useful and highly appreciated, and I hope to see it becoming even more powerful in the future.

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.