Pages

26 August, 2022

IIS 301 Redirects with Cache Control

With IIS rewrite module, we can create 301 redirects. Once the 301 redirect response is sent to the browser, client browser caches it indefinitely until browser cache is cleared or hard reload happens. This can tweaked by setting proper cache control header. 

Sometimes 301 redirects need to be changed (yes, it happens!). Even though we change it in rewrite rule in web.config, browser does not send the request to the server to know whether there is an update in the destination url. We can avoid it by setting cache-control response header. 

The problem with the IIS Rewrite Module is that once the "redirect" rule is hit, it does not process further rule. StopProcessing flag is respected only for Rewrites and not for Redirects. If it is respected, we can add outbound rules to modify the response header whenever the response status code is 301. 

<outboundRules>
<rule name="Require clients to revalidationpermanent redirects">
<match serverVariable="RESPONSE_Cache_Control" pattern=".*" />
<conditions>
<add input="{RESPONSE_STATUS}" pattern="301" />
</conditions>
<action type="Rewrite" value="public, must-revalidate, max-age=0"/>
</rule>
</outboundRules>

A probable solution is to create an intermediate page "redirect.aspx". This page will redirect the request to target URL with proper cache control. Please make sure that instead of Action type as Redirect, we need to change it to Rewrite in IIS Rewrite rule. 


In the aspx page, we can set cache control so that the 301 redirects can be cached in proxy (CDN) for a specific days or hours. In the IIS rewrite rule, we need to change from Redirect to Rewrite. 

<rule name="Rule for Redirects">
<match url=".*" />
<conditions>
<add input="{Redirects:{PATH_INFO}}" pattern="(.+)" />
</conditions>
<action type="Rewrite" url="redirect.aspx?url={C:1}" />
</rule>

Below aspx page will not allow any layer to cache the 301 redirects. Please note that this may increase the traffic to our web server since all the 301 redirects will hit the web server to get the target URL all the time. It is important to set the proper cache control to avoid heavy load on the web server. Based on current infrastructure, we can set cache control header to cache it in CDN or the web server itself.

<%@ Page Language="C#" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>redirecting...</title>
</head>
<body>
</body>
</html>
<script language="c#" runat="server">
string GetUrl(string text, string search, string replace)
{
text = text.Replace("url=", "");
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
public void Page_Load(object sender, EventArgs e)
{
var qs = Request.ServerVariables["QUERY_STRING"];
if (!System.String.IsNullOrEmpty(qs) && !System.String.IsNullOrEmpty(qs.Replace("url=", "")))
{
var url = GetUrl(qs, "&", "?");
if (!System.String.IsNullOrEmpty(url))
{
Sitecore.Diagnostics.Log.Info("IIS Redirect: " + Request.ServerVariables["HTTP_X_ORIGINAL_URL"] + " --> " + url, this);
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.Status = "301 Moved Permanently";
System.Web.HttpContext.Current.Response.StatusCode = 301;
// #### To Cache the redirects in Proxy/CDN for 60 seconds but no cache in Client Browser
System.Web.HttpContext.Current.Response.AppendHeader("Cache-Control", "public, max-age=0, s-maxage=60");
// #### Change the Cache-Control as per your need. No Cache here ####
// System.Web.HttpContext.Current.Response.AppendHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
System.Web.HttpContext.Current.Response.AddHeader("Location", url);
System.Web.HttpContext.Current.Response.End();
}
}
}
</script>
view raw redirect.aspx hosted with ❤ by GitHub



No comments:

Post a Comment

blockquote { margin: 0; } blockquote p { padding: 15px; background: #eee; border-radius: 5px; } blockquote p::before { content: '\201C'; } blockquote p::after { content: '\201D'; }