TileLayer and the CORS Curse

Fig 1 – Sample Bing Maps v8 county TileLayer hosted in Azure blob storage

Bing Maps Ajax v7 API is due to retire June 30, 2017 replaced by the newer and much better Bing Maps Ajax v8 API. I’ve been migrating some Bing Maps v7 API web apps to the new Bing Maps v8 API. Here is Microsoft’s migration guide: https://www.microsoft.com/maps/discon-control-migrat-guide.aspx

Most of the migration is fairly straightforward and the performance boost is quite significant. Large numbers of points, polylines, and complex polygons can now be rendered without impact on map navigation performance. There are many improvements to the v8 API including spatial geometry functions discussed in a previous post, Spatial to the Browser.

However, I ran into a problem with the Microsoft.Maps.TileLayer. It seems that the way Bing Maps v8 handles tiles causes CORS error for sporadic tiles in Chrome, Edge, and Firefox, but not IE. In the sample below png image tiles were stored in Azure Blob storage.

Here is a small test app that illustrated the problem:

<!DOCTYPE html>
        <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>

        <div id='myMap' style='width: 100vw; height: 100vh;'></div>
        <script type='text/javascript'>
            function loadMapScenario() {
                //temp bing key test1
                var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
                    credentials: '<Your Bing Map Key>',
                    center: new Microsoft.Maps.Location(40, -95),
                    zoom: 5

                var tileSource = new Microsoft.Maps.TileSource({
                    uriConstructor: 'http://onterratest.blob.core.windows.net/bingtiles2/county/{quadkey}.png'
                boundaryLayer = new Microsoft.Maps.TileLayer({ mercator: tileSource, animationDisplay: "hide" });
        <script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?branch=experimental&callback=loadMapScenario' async defer></script>


EDGE developer tool doesn’t show the problem even though some tiles are not rendered as seen in Fig 1 above.

Fig 2 – EDGE browser developer tool showing tile requests

Tile 03023.png is accessible and returns an image but TileLayer does not render the image.

Fig 3 – sample tile 03023.png

Switching to Chrome we can see some additional details.

Fig 4 – Chrome browser sample Bing Maps v8 with TileLayer hosted in Azure blob storage

Chrome Developer Tools Network panel.

Fig 5 – Chrome Developer tools Network showing tile requests

Some tile requests are showing a (canceled) status with a CORS policy violation indicated:

Access to Image at ‘http://onterratest.blob.core.windows.net/bingtiles2/county/02301.png’ from origin ‘http://onterrawms.blob.core.windows.net’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://onterrawms.blob.core.windows.net’ is therefore not allowed access.

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.”

Back in 2013 Microsoft added CORS to blob storage tools.

“Beginning with version 2013-08-15, the Azure storage services support Cross-Origin Resource Sharing (CORS) for the Blob, Table, and Queue services.”

AllowedOrigins = new List() { “*” } will allow any other domain to use the blob files without CORS violations.

Here is some sample c# code changing the CORS rules for a blob storage account to allow cross origin access:

CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=<name>;AccountKey=<your azure key>");
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

//set CORS header for png images
var serviceProperties = blobClient.GetServiceProperties();

serviceProperties.Cors.CorsRules.Add(new CorsRule()
AllowedHeaders = new List<string>() { "*" },
AllowedMethods = CorsHttpMethods.Put | CorsHttpMethods.Get,
       AllowedOrigins = new List<string>() { "*" },
       ExposedHeaders = new List<string>() { "*" },
       MaxAgeInSeconds = 1800 // 30 minutes

Using the above CORS settings corrects the CORS errors when using EDGE, Chrome, and Firefox browsers.

Fig 6 – Chrome browser Bing Maps v8 sample using TileLayers from Blob storage with CORS

Another approach to modifying CORS settings for blob storage is Azure PowerShell. Azure PowerShell is a module that provides tools for managing Azure. https://docs.microsoft.com/en-us/azure/storage/storage-powershell-guide-full
Here are the steps for setting CORS using Azure PowerShell:

  1. Run Add-AzureAccount to sign into your account
  2. See your subscriptions in azure Get-AzureSubscription | Format-Table SubscriptionName, IsDefault, IsCurrent, CurrentStorageAccountName
  3. Set desired subscription $SubscriptionName = ‘Your subscription name’
  4. Check your desired blob Get-AzureStorageBlob
  5. Now you need to create authorization context for your blob $ctx = New-AzureStorageContext and enter desired parameters.
  6. You are now ready to get or set CORS rules for your blob. Check current CORS rules Get-AzureStorageCORSRule -ServiceType Blob -Context $ctx
  7. Set current CORS rules for example: $CorsRules = (@{ AllowedHeaders=@(“*”); AllowedOrigins=@(“*”); ExposedHeaders=@(“content-length”); MaxAgeInSeconds=200; AllowedMethods=@(“Get”,”Connect”, “Head”)})
  8. Set-AzureStorageCORSRule -ServiceType Blob -CorsRules $CorsRules -Context $ctx


CORS is a useful mechanism for allowing cross browser access. CORS AllowedOrigins shouldn’t be necessary for images such as png or jpeg, but for the current Bing Maps v8 TileLayer API there are problems with popular browsers: Chrome, EDGE, and Firefox that can be resolved by setting a CORS policy in the blob container.