Loading...

Advanced routing capabilities using Application Gateway Rewrite Rules

Image

For a vast majority of Application Gateway customers, multi-site hosting and path-based routing options are all you need to expose your applications to the world. In the simplest terms, you have www.contoso.com that needs to serve a specific website or service to your visitors. And you may take that a step further and have a completely different site served for all requests to your online store when visitors browse www.contoso.com/shop.

 

But we know the story does not end there. Application Gateway customers come in all shapes and sizes. There are times when you need to push the envelope and do something more advanced to meet your ever-changing business or technical requirements. Sometimes you need to go beyond what is provided out of the box.

 

There are many situations where more complex routing is required. Below are some of the potential scenarios.

  • Early-access or preview website launches: You want to leverage query string parameters to allow visitors to steer traffic through simple URL changes.
  • API versioning: You want to allow independent deployments of new API releases with client traffic routing through HTTP headers where path-based routing alone does not fit your requirements.
  • A/B testing: Through cookies that are set by your application, you want to steer certain visitors to different backends to allow you to validate performance/effectiveness of the new backend infrastructure, major application upgrades, or design/content changes. If problems are discovered, this setup allows you to quickly route visitors back to the original backend by disabling a ruleset at the gateway without waiting for development changes or new deployments to complete. (This method doesn't entail use of Weighted Round Robin).
  • Load testing heterogenous backend server types: You want to allow rapid testing of varied backends. Useful when validating backend configuration changes, different scaling configurations, or migrations to entirely new backend services (e.g. migrating your application from IaaS to PaaS services). Keep all available for testing without requiring reconfiguration of Application Gateway by utilizing HTTP headers to dynamically steer traffic to the backend under test.

In the past, you may rely on subdomains and multi-site hosting to achieve this, but this is not always the best option for everyone.

What if you could route traffic to different backend pools based on more than the host header or path? What if the presence of a specific HTTP header, cookie, or query string parameter altered which site or service was served to your clients? Sounds ideal? Well, keep reading as this is exactly what we are going to look at here. For the scope of this article, we are going to focus on the first two scenarios above.

 

Scenario

Contoso are going through a major rebranding and modernization. They are about to launch their new website and a shiny new version of their mobile application. The Contoso marketing team want to allow select business partners and news organizations to access a preview of the new website ahead of its public launch. As time is limited, they would rather not go through the process of setting up the new website on a subdomain or messing with DNS records and TLS certificates.

 

Their new mobile application relies on many of the same APIs as the current version, but due to the rebranding certain APIs need to return different information. Therefore, a new version of these APIs has been developed as part of the new website release. As this is a brownfield environment, existing users cannot be forgotten or impacted by these changes though and it is imperative that any Contoso customers running the current app can continue to use it during the transition.

 

Current Configuration

A single listener accepts traffic for www.contoso.com on HTTP port 80 (let’s gloss over this lapse in security for today).

 

An existing HTTP Listener configured for www.contoso.com on port 80An existing HTTP Listener configured for www.contoso.com on port 80

 

This multi-site listener is associated with a single Basic rule that routes all traffic for www.contoso.com to a backend pool called prod-pool that contains the IP addresses of their current public website.

 

An existing routing rule sending traffic from our above listener to an existing production backend poolAn existing routing rule sending traffic from our above listener to an existing production backend pool

 

Quick cURL tests shows that traffic is flowing to the existing website and version 1 of the Contoso mobile API:

 

 

$ curl http://www.contoso.com/ Welcome to our production site $ curl --user-agent "ContosoMobile/v1" http://www.contoso.com/mobile-api/ { status: { apiVersion: 1, branch: "production" } }

 

 

 

Requirement 1: Private preview access to the new website

To achieve this, we are going to use a combination of path-based routing and rewrite rules to redirect traffic to the new website if a specific query string parameter is included in the request. Since Contoso will be sharing this with select people who need to access the site via a browser, query strings make it easy to share links.

 

Currently if we send a request with the planned secret link, we get to the current production site:

 

 

$ curl http://www.contoso.com/?preview=keep-it-secret-please Welcome to our production site

 

 

Let’s make some changes to the gateway configuration:

  1. Create a new backend pool called new-pool containing your new website as the target.
  2. Create a new listener. This is required to allow us to setup a new rule in the following steps. We can change this later to a multi-site listener for www.contoso.com    
    1. Name: new-listener
    2. Port: 80
    3. Listener type: Basic
  3. Create a new routing rule:
    1. Name: http-path-rule
    2. Priority: As required
    3. Listener: The name of the listener created in step 2
    4. Backend targets:
      1. Target type: Backend pool
      2. Backend target: prod-pool (the existing production backend pool and applies to “/” path by default)
      3.  Backend settings: The existing settings, in this case prod-settings
      4. Add a path-based rule:
        • Target type: Backend pool
        • Path: /_new-site/*
        • Name: new-site
        • Backend settings: The existing settings, in this case prod-settings
        • Backend target: new-pool (as created in step 1)

Making a test call to the Application Gateway IP address shows us traffic is being routed correctly to the current site. This is what we want to see as it shows existing traffic to this new Application Gateway listener is being routed to the production website as expected.

 

 

$ curl http://4.158.235.196/ Welcome to our production site

 

 

The original listener http-listener can now safely be removed. This will automatically remove the associated rule http-rule for you. For consistency, you can also update the listener new-listener to be multi-site and add www.contoso.com as its host name to match the original configuration.

 

Note: For greenfield deployments, you will typically have no existing HTTP Listeners or Rules. Any steps referencing existing or original listeners/rules can safely be ignored as these are an artifact of the brownfield scenario played out in this article.

 

The above steps have now exposed a path-based rule so that any requests to this special path /_new-site/ are being sent to the new backend pool. It is hard to test this here because the new Contoso website does not have a page that matches, but we can at least see that the NGINX on our backend virtual machine is returning a 404 response:

 

 

$ curl http://www.contoso.com/_new-site/ <html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>nginx/1.18.0 (Ubuntu)</center> </body> </html>

 

 

The next step is to handle this unwanted /_new-site/ path and let us use a query string. This is where the power of Rewrite Rules comes in. This will be achieved with two rewrite rules:

  1. The first will use the URL rewrite to intercept requests to Application Gateway that contain the “preview=keep-it-secret-please” query string and redirect the traffic to the new backend pool by injecting /_new-site/ in to the URL path. A matching HTTP request is now pointing to the backend target corresponding to the new Contoso website.
  2. Leveraging a feature called Path map re-evaluation, the request can be passed through to our second rule which we will use to strip /_new-site/ from the URL as we don’t want this included when it reaches our website.

An overview of the process flow achieved with the above configuration changes and rulesAn overview of the process flow achieved with the above configuration changes and rules

 

 

Rule 1

Create a new rewrite set to detect the presence of a query string and inject the temporary path prefix:

  1. Name and Association
    1. Name: Add_path_prefix_if_query_string_found
    2. Routing rules: Tick the parent path-based rule as created earlier called http-path-rule
  2. Rewrite rule configuration
    1. Add a new rewrite rule
      1. Name: PreviewQueryString
      2. Rule sequence: 100 (default)
      3. Add a condition:
        • Type: Server variable
        • Server variable: query_string
        • Case-sensitive: No
        • Operator: equal
        • Pattern: preview=keep-it-secret-please
      4. Add an action:
        • Rewrite type: URL
        • Action type: Set
        • Components: URL path
        • URL path value: /_new-site{var_uri_path} (Important: do not include a forward slash after “_new-site”
        • Re-evaluate path map: Checked

How the new rule is configured to inject a temporary path prefix when the secret query string is passed in the URLHow the new rule is configured to inject a temporary path prefix when the secret query string is passed in the URL

 

 

If we request the special preview URL now you will see the same HTTP 404 response for now:

 

 

$ curl http://www.contoso.com/?preview=keep-it-secret-please <html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>nginx/1.18.0 (Ubuntu)</center> </body> </html>

 

 

This demonstrates that the request is reaching webserver of the backend target in the new pool based on the given path. If you review the web server logs, you will see requests for “/_new-site/?preview=keep-it-secret-please”.

 

Rule 2

Create a new rewrite set to detect and remove the path prefix:

  1. Name and Association
    1. Name: Remove_path_prefix_for_preview_backend
    2. Routing rules: Tick the child path-based rule earlier (new-site in this example)
  2. Rewrite rule configuration
    1. Add a new rewrite rule
      1. Name: CleanURL
      2. Rule sequence: 100 (default)
      3. Add a condition:
        • Type: Server variable
        • Server variable: uri_path
        • Case-sensitive: No
        • Operator: equal
        • Pattern: /_new-site/(.*)
      4. Add an action:
        • Rewrite type: URL
        • Action type: Set
        • Components: URL path
        • URL path value: /
        • Re-evaluate path map: Unchecked
Warning: It is critical that the rule action created here DOES NOT re-evaluate the path map. If incorrectly enabled, this will cause a loop situation where the first ruleset executes, and the path prefix is re-added every time it is removed.

 

A new rule configured to match traffic containing the temporary path prefix, and remove it, leaving the original URL requested by the visitorA new rule configured to match traffic containing the temporary path prefix, and remove it, leaving the original URL requested by the visitor

 

Let’s check how this looks now:

 

 

$ curl http://www.contoso.com/?preview=keep-it-secret-please Welcome to the new preview site

 

 

We now have traffic routed to the new site if the query string is present. But let’s check that existing traffic is unaltered, and visitors can still access the current website:

 

 

$ curl http://www.contoso.com/ Welcome to our production site

 

 

Perfect, we have cleanly routed traffic to the new site.

 

You may be thinking “what happens when the visitor clicks on a link on the new homepage that doesn’t include that query string?”. The answer is the new page would be served by the current (non-preview) website due to the query string now missing. One way to address this would be to rely on a HTTP cookie dropped in the browser by the new website. An additional rewrite rule can check for the presence of this cookie in the same way we previously checked for the query string. This is how that rule would look:

A rule showing how it is possible to steer traffic by prefixing the temporary path prefix upon presence of a application-controlled cookie instead of a query string parameter.A rule showing how it is possible to steer traffic by prefixing the temporary path prefix upon presence of a application-controlled cookie instead of a query string parameter.

 

This new rule is being added to the existing ruleset Add_path_prefix_if_query_string_found alongside the existing rule PreviewQueryString.

 

By making a request with and without this cookie, we see the news page returns a different story:

 

 

$ curl http://www.contoso.com/news/ Our new website is coming soon!$ curl --cookie "show-preview-site=anything" http://www.contoso.com/news/ Our new website is here!

 

 

 

Requirement 2: Support for the new mobile API

With the steps taken above this is a quick one to tick off. We will leverage the existing setup and add a new rewrite rule that watches for requests from the new Contoso mobile app.

 

The existing mobile app makes requests like:

 

 

$ curl --user-agent "ContosoMobile/v1" http://www.contoso.com/mobile-api/ { status: { apiVersion: 1, branch: "production" } }

 

 

As the development team kindly set a clear user agent in the requests it provides a great way for Application Gateway to detect the new version. Let’s add a new rule that looks for the User-Agent being ContosoMobile/v2 and inject the same /_new-site URL prefix as before:

Showing the additional rule that redirects traffic from clients that identify with the ContosoMobile/v2 user-agentShowing the additional rule that redirects traffic from clients that identify with the ContosoMobile/v2 user-agent

 

Now, when we make a request using the new user agent we are returned version 2 of the API which also references it is a preview. And just as we expect, requests with the original user agent header (or any other user agent) are unaffected by the change:

 

 

curl --user-agent "ContosoMobile/v2" http://www.contoso.com/mobile-api/ { status: { apiVersion: 2, branch: "preview" } }$ curl --user-agent "ContosoMobile/v1" http://www.contoso.com/mobile-api/ { status: { apiVersion: 1, branch: "production" } }

 

 

 

How far can we push this?

Rewrite rules coupled with the ability to use regular expressions in conditions/actions opens up a lot of possibilities. For example, instead of looking for a fixed query string value, you could match a numerical value, list of values, or other patterns and use this to dynamically alter the URL path and redirect the request to any number of backends.

 

Summary

In this scenario we saw how Application Gateway V2 and the power of rewrite rules allows dynamic backend redirection to enable advanced routing scenarios often desired for website/application launches, API versioning, A/B testing, and load testing. This is achieved through the use of cookie-based routing, request header-based routing, or query string-based routing as required for the particular use-case.

 

We hope this has given you the insight and knowledge to achieve your goals.

 

Learn more
Author image

Azure Networking Blog articles

Azure Networking Blog articles

Share post:

Related

Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!

* Yes, I agree to the privacy policy