Configure Azure storage to return proper response headers for blob GETs

In the early days of Azure blob storage, the response headers for HTTP GET requests were not quite right. The ETag header wasn't escaped with double-quotes, which is not correct, and HTTP clients may not cache correctly without them; and the Accept-Ranges header wasn't returned, which doesn't play nicely with clients wanting to support interrupted & resumed downloads.

That was fixed with an update to the storage service that came way back in 2011, but Azure has a very cautious approach to widespread changes like this and you have to opt-in to get the latest features. That's still the case, so unless you explicitly set it up, you won't get those headers for storage accounts and containers you create today.

Here's the default header response for a brand-new container in a new storage account:

curl -I https://xyz.blob.core.windows.net/my-file.zip

HTTP/1.1 200 OK Content-Length: 97071076 Content-Type: application/octet-stream Content-MD5: JHZsasfd1G2AY14g58iCU6Q== Last-Modified: Mon, 01 Sep 2014 09:37:09 GMT ETag: 0x8D193DF6FABC5262 Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: 4c658e1b-0001-0012-0175-ab43d5000000 x-ms-version: 2009-09-19 x-ms-lease-status: unlocked x-ms-blob-type: BlockBlob Date: Fri, 26 Sep 2014 14:29:15 GMT

Note the ETag is non-compliant, there's no Accept-Ranges, and the x-ms-version header which tells you the version of storage service that supplied the response, is stuck back in 2009.

You can explicitly request a different version of the storage service by supplying that header in the request. Make the same request and specify an Azure service version later than 2011 with the x-ms-version header (2014-02-14 here):

curl -I -H x-ms-version:2014-02-14 https://xyz.blob.core.windows.net/my-file.zip

HTTP/1.1 200 OK Content-Length: 97071076 Content-Type: application/octet-stream Content-MD5: JHZsasfd1G2AY14g58iCU6Q== Last-Modified: Mon, 01 Sep 2014 09:37:09 GMT Accept-Ranges: bytes ETag: "0x8D193DF6FABC5262" Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: 4ab79588-0001-0015-36f2-199808000000 x-ms-version: 2014-02-14 x-ms-lease-status: unlocked x-ms-lease-state: available x-ms-blob-type: BlockBlob Date: Mon, 29 Sep 2014 07:15:23 GMT

And you get the improved headers, with Accept-Ranges and a compliant ETag.

You could argue that new storage accounts should default to using the latest API version, but that would be a breaking change for any systems expecting the old headers - if you swapped an old storage account for an existing service to a new one, you'd expect it to behave in the same way.

You can specify which API version the services should default to at the storage account level, but you can't do it through the management portal - you can have fun generating a signature for the Blob Service REST API, or you can do it in a few lines of code with the WindowsAzure.Storage NuGet package:

var connectionString = "DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey}";
var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var props = blobClient.GetServiceProperties();
props.DefaultServiceVersion = "2014-02-14";
blobClient.SetServiceProperties(props);

Now any requests for blobs which don't specify an explicit service version will default to 2014-02-14 and get the enhanced response headers.

This article is part of the GWB Archives. Original Author: Elton Stoneman

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.