Search code examples
asp.netcachingcache-controloutputcache

Can I force caching on a 302 temporary redirect in ASP.NET?


I have an ASPX page that issues a Response.Redirect that points to an image file.

The redirect response headers look like this:

HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: https://www.site.com/folder/file.jpg
Server: Microsoft-IIS/8.0
Date: Tue, 29 Apr 2014 08:29:58 GMT
Content-Length: 241

Is it possible to force the client and any proxy servers to cache this response for say 30 days? Should I do this with Cache-Control, ETags or both? If so, how?


Solution

  • I have figured this out and tested it. The following code adds the ETags and cache-control:

    protected void Page_Load(object sender, EventArgs e)
    {
        var absoluteUrl = GetUrlFromDatabase(Request["fileId"]);
        CacheResponse(absoluteUrl);
        Response.Redirect(absoluteUrl);
    }
    
    private static void CacheResponse(string absoluteLocation)
    {
        // you need to clear the headers that ASP.NET automatically adds
        HttpContext.Current.Response.ClearHeaders();
    
        // now get the etag (hash the 
        var etag = GetETag(absoluteLocation);
    
        // see if the etag matches what was sent
        var requestedETag = HttpContext.Current.Request.Headers["If-None-Match"];
        if (requestedETag == etag)
        {
            HttpContext.Current.Response.Status = "304 Not Modified";
            HttpContext.Current.ApplicationInstance.CompleteRequest();
    
            return;
        }
    
        // otherwise set cacheability and etag.
        HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
        HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
        HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.AddMonths(1));
        HttpContext.Current.Response.Cache.SetLastModified(DateTime.UtcNow);
        HttpContext.Current.Response.Cache.SetETag("\"" + etag + "\"");
    }
    
    private static string GetETag(string url)
    {
        var guid = StringToGuid(url);
        var etag = new ShortGuid(guid); // see reference to ShortGuid below
        return etag.Value.Replace("-", string.Empty);
    }
    
    private static Guid StringToGuid(string value)
    {
        // Create a new instance of the MD5CryptoServiceProvider object.
        var md5Hasher = MD5.Create();
    
        // Convert the input string to a byte array and compute the hash.
        var data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(value));
        return new Guid(data);
    }
    

    Reference: ShortGuid.

    The initial HTTP response headers are now:

    HTTP/1.1 302 Found
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    Expires: Thu, 29 May 2014 09:07:41 GMT
    Last-Modified: Tue, 29 Apr 2014 09:07:41 GMT
    ETag: "k28kbGNuxkWzP6gmLO2xQ"
    Location: https://www.site.com/folder/file.jpg
    Date: Tue, 29 Apr 2014 09:07:41 GMT
    Content-Length: 241