Static Content Hosting Pattern

Deploy static content to a cloud-based storage service that can deliver these directly to the client. This pattern can reduce the requirement for potentially expensive compute instances.

Context and Problem

Web applications typically include some elements of static content. This static content may include HTML pages and other resources such as images and documents that are available to the client, either as part of an HTML page (such as inline images, style sheets, and client-side JavaScript files) or as separate downloads (such as PDF documents).

Although web servers are well tuned to optimize requests through efficient dynamic page code execution and output caching, they must still handle requests to download static content. This absorbs processing cycles that could often be put to better use.

Solution

In most cloud hosting environments it is possible to minimize the requirement for compute instances (for example, to use a smaller instance or fewer instances), by locating some of an application’s resources and static pages in a storage service. The cost for cloud-hosted storage is typically much less than for compute instances.

When hosting some parts of an application in a storage service, the main considerations are related to deployment of the application and to securing resources that are not intended to be available to anonymous users.

Issues and Considerations

Consider the following points when deciding how to implement this pattern:

  • The hosted storage service must expose an HTTP endpoint that users can access to download the static resources. Some storage services also support HTTPS, which means that it is possible to host resources in storage service that require the use of SSL.
  • For maximum performance and availability, consider using a content delivery network (where available) to cache the contents of the storage container in multiple datacenters around the world. However, this will incur additional cost for the use of the content delivery network.
  • Storage accounts are often geo-replicated by default to provide resiliency against events that might impact a datacenter. This means that the IP address may change, but the URL will remain the same.
  • When some content is located in a storage account and other content is in a hosted compute instance it becomes more challenging to deploy an application and to update it. It may be necessary to perform separate deployments, and version the application and content in order to manage it more easily—especially when the static content includes script files or UI components. However, if only static resources are to be updated they can simply be uploaded to the storage account without needing to redeploy the application package.
  • Storage services may not support the use of custom domain names. In this case it is necessary to specify the full URL of the resources in links because they will be in a different domain from the dynamically generated content containing the links.
  • The storage containers must be configured for public read access, but it is vital to ensure that they are not configured for public write access to prevent users being able to upload content. Consider using a valet key or token to control access to resources that should not be available anonymously—see Valet Key Pattern for more information.

When to Use this Pattern

This pattern is ideally suited for:

  • Minimizing the hosting cost for websites and applications that contain some static resources.
  • Minimizing the hosting cost for websites that consist of only static content and resources. Depending on the capabilities of the hosting provider’s storage system, it might be possible to host a fully static website in its entirety within a storage account.
  • Exposing static resources and content for applications running in other hosting environments or on-premises servers.
  • Locating content in more than one geographical area by using a content delivery network that caches the contents of the storage account in multiple datacenters around the world.
  • Monitoring costs and bandwidth usage. Using a separate storage account for some or all of the static content allows the costs to be more easily distinguished from hosting and runtime costs.

This pattern might not be suitable in the following situations:

  • The application needs to perform some processing on the static content before delivering it to the client. For example, it may be necessary to add a timestamp to a document.
  • The volume of static content is very small. The overhead of retrieving this content from separate storage may outweigh the cost benefit of separating it out from the compute resources.

Note:

It is sometimes possible to store a complete website that contains only static content such as HTML pages, images, style sheets, client-side JavaScript files, and downloadable documents such as PDF files in a cloud-hosted storage. For more information see An efficient way of deploying a static web site on Microsoft Azure on the Infosys blog.

Example

Static content located in Azure blob storage can be accessed directly by a web browser. Azure provides an HTTP-based interface over storage that can be publicly exposed to clients. For example, content in a Azure blob storage container is exposed using a URL of the form:

http://[ storage-account-name ].blob.core.windows.net/[ container-name ]/[ file-name ]

When uploading the content for the application it is necessary to create one or more blob containers to hold the files and documents. Note that the default permission for a new container is Private, and you must change this to Public to allow clients to access the contents. If it is necessary to protect the content from anonymous access, you can implement the Valet Key pattern so users must present a valid token in order to download the resources.

Note:

The page Blob Service Concepts on the Azure website contains information about blob storage, and the ways that you can access it and use it.

The links in each page will specify the URL of the resource and the client will access this resource directly from the storage service. Figure 1 shows this approach.

Figure 1 - Delivering static parts of an application directly from a storage service

 

The links in the pages delivered to the client must specify the full URL of the blob container and resource. For example, a page that contains a link to an image in a public container might contain the following.

HTML

<img src="http://mystorageaccount.blob.core.windows.net/myresources/image1.png"     alt="My image" />

Note:

If the resources are protected by using a valet key, such as an Azure Shared Access Signature (SAS), this signature must be included in the URLs in the links.

The examples available for this guide contain a solution named StaticContentHosting that demonstrates using external storage for static resources. The StaticContentHosting.Cloud project contains configuration files that specify the storage account and container that holds the static content.

XML

<Setting name="StaticContent.StorageConnectionString"          value="UseDevelopmentStorage=true" /><Setting name="StaticContent.Container" value="static-content" />

The Settings class in the file Settings.cs of the StaticContentHosting.Web project contains methods to extract these values and build a string value containing the cloud storage account container URL.

C#

public class Settings{  public static string StaticContentStorageConnectionString {    get    {      return RoleEnvironment.GetConfigurationSettingValue(                              "StaticContent.StorageConnectionString");    }  }   public static string StaticContentContainer  {    get    {      return RoleEnvironment.GetConfigurationSettingValue("StaticContent.Container");    }  }   public static string StaticContentBaseUrl  {    get    {      var account = CloudStorageAccount.Parse(StaticContentStorageConnectionString);       return string.Format("{0}/{1}", account.BlobEndpoint.ToString().TrimEnd('/'),                                      StaticContentContainer.TrimStart('/'));    }  }}

The StaticContentUrlHtmlHelper class in the file StaticContentUrlHtmlHelper.cs exposes a method named StaticContentUrl that generates a URL containing the path to the cloud storage account if the URL passed to it starts with the ASP.NET root path character (~).

C#

public static class StaticContentUrlHtmlHelper{  public static string StaticContentUrl(this HtmlHelper helper, string contentPath)  {    if (contentPath.StartsWith("~"))    {      contentPath = contentPath.Substring(1);    }     contentPath = string.Format("{0}/{1}", Settings.StaticContentBaseUrl.TrimEnd('/'),                                contentPath.TrimStart('/'));     var url = new UrlHelper(helper.ViewContext.RequestContext);     return url.Content(contentPath);  }}

The file Index.cshtml in the Views\Home folder contains an image element that uses the StaticContentUrl method to create the URL for its src attribute.

HTML

<img src="@Html.StaticContentUrl("~/Images/orderedList1.png")" alt="Test Image" />

Related Patterns and Guidance

The following pattern may also be relevant when implementing this pattern:

Valet Key Pattern. If the target resources are not supposed to be available to anonymous users it is necessary to implement security over the store that holds the static content. The Valet Key pattern describes how to use a token or key that provides clients with restricted direct access to a specific resource or service such as a cloud-hosted storage service.