Redirecting legacy URL's to new domains using AWS Application Load Balancer and Nginx
Seamlessly migrate legacy environment URL`s to new cloud estate while keeping the lights on with no interruptions
Due to a variety of reasons, we can find ourselves wanting to do a 0 downtime migration from a legacy URL or domain (legacydomain.com) to a new one (newdomain.com) without any clients noticing the change and in this post we will do just that. As the legacy environment is to be decommissioned ASAP while the legacy URL’s are still to be used by clients for the foreseeable months due to low change velocity at their end, we will need to do a few extra steps in the migration.
We will do this while touching upon several important topics that might cause unexpected gotcha’s to happen during your digital transformation which could hamper your smooth transition if not careful:
How to redirect website URL’s where the first request is always a HTTP GET.
How to redirect API URL’s where requests are isolated and can be any HTTP verb.
Gotcha’s with Authentication headers and redirects.
HTTP 30x redirect codes and details of each.
Move all traffic over from legacy environment to new location and completely shut off the legacy environment, effectively serving all traffic, both traffic going to the old domain and traffic going to the new domain, from the new environment.
In essence, we will go from this where users reach your legacy.com
domain
To this where users access your legacy.com
domain but end up accessing new.com
:
As we can see, we must go in the DNS registrar and update our entry for legacy.com
to point to the same A
/CNAME
as the new.com
entry so we can handle both legacy redirects and proper application traffic from the same load balancer, removing entirely any traffic or dependency to the old environment, allowing us to decommission the entire infrastructure of legacy while still serving legacy URL traffic.
1. Redirect website applications
To first complete the low hanging fruits of this migration, we configure the AWS Application Load Balancer HTTPS Listener to redirect based on Host Header
from legacy.com
domain to new.com
domain. As AWS ALB only supports HTTP 301 & 302 redirect, we will use the 301
code, as that is Permanent Redirect
, allowing browsers to cache the resolution, compared to 302
, which is a Temporary Redirect
. Note you can make 302 cacheable as well using Cache-Control
or Expires
response headers.
The sequence diagram for the above actions would be as follows:
2. Redirect API applications
With the less than 5 minutes that it takes for the above, armed with enthusiasm and confidence in solving the entire task in less than 10 minutes, we go ahead and attempt the same exact Host Header
redirect on the ALB.
Once we attempt to access a fictional URL at an API hosted on https://api.legacy.com/api/v1/data
on an API server that only serves POST
requests there, we will encounter a 404 Not Found
error.
The sequence of events of this would look as follows:
The first time I tried the API and saw the 404 Not Found error I was confused as to what was happening, why the 404 given that I know my API has POST, I checked the Listener rules which were correct. Upon investigating the API logs I saw the request reaching the new.com
domain was a HTTP GET which led me down the rabbit hole to investigate HTTP Redirect codes in detail, as the knowledge up to then of 30x codes being all redirects did not suffice any longer. This research spike led me to discover the following distilled information:
The original HTTP 1.0
spec did not have 307 Temporary Redirect
and 308 Permanent Redirect
, as these roles were meant to be filled by 301 Moved Permanently
and 302 Found
.
However, as most clients changed the HTTP request method from POST to GET for 301
and 302
redirect responses, despite the HTTP specification not allowing the clients to do so. This behavior necessitated the introduction of the stricter 307 Temporary Redirect
and 308 Permanent Redirect
status codes in the HTTP/1.1 update.
I then did a quick and easy test to confirm using curl with verbose logging to see the steps taken by the client:
As we can see in the above verbose curl logging statement, our request get altered based on the 301
code.
Therefore, we can easily fix the 404 Not Found
by leveraging 308
response code to achieve the following sequence of events:
Finding the cause and solution to the redirect is not the end of our troubles as the AWS ALB only supports HTTP 301 and HTTP 302 as per the documentation.
For various reasons AWS has elected to not permit more fine grained redirect codes effectively requiring us to roll out our own load balancer to satisfy all cases or to simply host a minimum .25 CPU
Fargate Nginx
container to perform the HTTP 308
redirects.
For which we will need a minimal Nginx
Dockerfile
to load this custom configuration from the environment variables, perform base64 decoding on it and place it at the expected location so we can have the same base image running on all environments:
With the following docker-entrypoint.sh
:
With all of this new routing in place, we test our verbose curl once more, and we can see a successful response.
We can notice that while now we stopped getting 404
, we are now getting 403
, which is expected, as this is an authenticated endpoint that is expecting an Authorization
header. We easily add it to the request but we still notice that the redirected request has no Authorization header:
This prompted a new research spike to investigate the cause of it, which resulted in the conclusion that due to security concerns when doing a redirect from one domain to another which could be exploited for malicious purposes, clients drop the Authorization header in HTTP 30x requests.
In order to work with this security limitation, we have to drop the “redirect” concept entirely as it is unfit for purpose, we must perform a seamless transition while not adding in security vulnerabilities therefore our Nginx
server is taking on a greater role, that of a forwarding proxy
from legacy URL’s to the new URL’s with the following configuration:
Now the Nginx
server acts as a proxy from the old to the new, acting as a bridge linking the two worlds.
3. Conclusion
In conclusion, leveraging AWS Application Load Balancer for URL redirects offers a powerful solution for seamlessly transitioning users from legacy URLs to new URLs, mainly if it for website servers only. While ALB's built-in HTTP 301/302 redirects are ideal and suffice for websites, additional considerations are needed for APIs, where custom Nginx
servers can ensure the correct HTTP actions are maintained post-redirect along with the correct, complete header set. By following these guidelines, you can effectively manage URL redirections and enhance user experience across your applications with seamless migrations.