Redirect vs URL rewrite in an API gateway
ngrok’s Traffic Policy engine, which allows you to utilize ngrok as an API gateway, boasts a variety of actions that can be taken at the gateway level to enable you to modify the behavior of traffic flowing through your endpoints. Actions such as adding headers, compressing responses, and JWT validation are some more straightforward examples that need little explanation about what they do. However, what’s the difference between actions like URL rewriting and redirection?
A common use case for considering either of these actions is when migrating to a new version of an API. In my case, I have an API running at <code>https://api.ngrokpaperscissors.com</code> with all of my original endpoints in a directory called <code>/v1/</code>. Those endpoints are expecting a query string that includes parameters for <code>accountId</code> and <code>active</code>. However, the next version of the API will be getting the accountId in a different way, so that parameter is no longer needed. I’m ready to migrate incoming traffic from the <code>v1</code> directory to the new code now running in the <code>/v2/</code> directory. Rather than ask all of the users of my API to change their code to access the new <code>/v2/</code> endpoints and manually remove the <code>accountId</code> query parameter, I can configure ngrok’s Traffic Policy engine to automatically route traffic to the new services with the correct parameters.
Looking at the Traffic Policy documentation, I see two actions appear to route traffic for me: <code>redirect</code> and <code>url-rewrite</code>. But which do I use?
In this blog post, I’ll explore these two actions, how to best use them, and answer the question of when to use them. First, I’ll look at <code>redirect</code>.
Redirect
The redirect action enables incoming requests to be redirected to new URLs by modifying the original URLs with regular expressions. This redirection is done by setting the <code>Location</code> header of the response, requiring the client to make a second API call to the URL specified in that header.
For example, when I create a <code>redirect</code> action in my Traffic Policy configuration, the expression would look something like this:
inbound:
- actions:
- type: redirect
config:
from: /v1/(.+)\?(.+)
to: /v2/$1?active=true
name: API Route Migration Redirect
The expression takes all requests calling <code>/v1/</code> endpoints and redirects them to the corresponding endpoint in the <code>/v2/</code> directory of the API. The expression also adds a <code>active=true</code> query string parameter, which is needed by the endpoints.
When a client makes a request to the following API endpoint <code>https://api.ngrokpaperscissors.com/v1/list</code>, the request is redirected to <code>https://api.ngrokpaperscissors.com/v2/list?active=true</code>.
Using a command line utility like HTTPie the request would look something like this:
% http 'https://api.ngrokpaperscissors.com/v1/list?accountId=12345&active=true'
HTTP/1.1 302 Found
Content-Length: 0
Date: Thu, 08 Aug 2024 01:46:25 GMT
Location: https://api.ngrokpaperscissors.com/v2/list?active=true
The GET request went to <code>https://api.ngrokpaperscissors.com/v1/list</code>, and the ngrok API gateway returned a <code>302</code> status response along with the redirect URL as part of the <code>Location</code> header. It’s up to the client making the call to decide how to handle the redirect. Most of the time, the client will need to make a second call to the new URL. However, if this call were done in a web browser by default the browser would likely make the second call and open the new page automatically.
Redirect considerations
A <code>redirect</code> response also allows me, as the API owner, to communicate additional information to the client through HTTP status codes. For instance, the request shown above returned a very general <code>302</code>, which tells the client the request was <code>Found</code>. It’s also the default response code that ngrok returns for the <code>redirect</code> action. It’s much more helpful to clients if a more specific status code is returned.
The most common actions to communicate to the client are whether the redirection is a permanent or temporary change. If this is a permanent redirect, I would return a <code>301</code> status code which communicates a <code>Moved Permanently</code> message. If the change is only temporary, I could keep the default <code>302</code> or use <code>307</code>, which is specifically used to communicate this is a <code>Temporary Redirect</code>.
In this case, I’ll communicate that the redirect is permanent by setting the <code>status_code: 301</code> in the <code>config</code> object of my Traffic Policy action, like this:
inbound:
- actions:
- type: redirect
config:
status_code: 301
from: /v1/(.+)\?(.+)
to: /v2/$1?active=true
name: API Route Migration Redirect
URL rewrite
URL rewriting looks very similar to redirects, except it provides a slightly more subtle user experience. Rather than modifying the URL of the request at the client level, a URL rewrite modifies the URL before the request reaches the upstream server. The client will receive no indication that the URL was modified.
As an example, I’ll change my existing <code>API Route Migration</code> expression to be a <code>url-rewrite</code> action. To do that, all I’ll need to do is change the value of <code>type</code> to <code>url-rewrite</code> and remove the <code>status_code</code> since the action doesn’t allow for setting the <code>status_code</code>. The YAML will now look like this:
inbound:
- name: API Route Migration Rewrite
actions:
- type: url-rewrite
config:
from: /v1/(.+)\?(.+)
to: /v2/$1?active=true
Now when I make a call to an endpoint in the <code>/v1/</code> directory, the response is sent from the <code>/v2/</code> endpoint. In the example below, the <code>list</code> endpoint returns a JSON object of all the requests received by that endpoint. Notice the requested URL was <code>/v1/list?accountId=12345&active=true</code>, but the JSON response shows that <code>/v2/list</code> processed the request and sent a response with a <code>200</code> status code.
% http 'https://api.ngrokpaperscissors.com/v1/list?accountId=12345&active=true'
HTTP/1.1 200 OK
Content-Length: 60
Content-Type: application/json; charset=utf-8
Date: Thu, 08 Aug 2024 00:48:57 GMT
X-Powered-By: Express
{
"requests": [
{
"method": "GET",
"url": "/v2/list?active=true"
}
]
}
When to use one or the other
In situations where the clients and users need to be notified of changes to the endpoint URLs, it’s appropriate to use redirects with the corresponding HTTP status code. API consumers then have the choice to check for those 300-level status codes and act on any <code>Location</code> headers that are passed. Making additional calls to the API for a redirect may seem less efficient. However, extra processing may be necessary to inform the clients of changes.
If there’s no reason for the client to know the location of new backend services at new endpoints, then <code>url-rewrite</code> may be the way to go. The client's user experience is seamless as the services change, as long as the data returned by the new service is still in a compatible format.
Learn more about ngrok and our traffic policy engine
URL rewrite and redirection are only two actions you can configure in your API gateway powered by our Traffic Policy engine. To learn more about Traffic Policy, take a look at these resources:
- API gateway gallery: Drop-in API policy management examples
- Traffic Policy Engine - What are CEL variables?
- Traffic Policy support for the Kubernetes Gateway API
Getting started with ngrok is simple! Sign up for a free account today. We’d also love to hear from you in our new ngrok Community Repo: the best place for all discussions around ngrok, including bug reports and product feedback.