Introduction

Azure Blob Storage enforces CORS (Cross-Origin Resource Sharing) policies that control which domains can access blob data from browser-based applications. When CORS is misconfigured, browsers block requests with errors like "No 'Access-Control-Allow-Origin' header is present on the requested resource." This commonly affects SPAs, web apps, and browser-based upload/download tools.

Symptoms

  • Browser console shows: Access to XMLHttpRequest at 'https://account.blob.core.windows.net/container/blob' from origin 'https://myapp.com' has been blocked by CORS policy
  • HTTP OPTIONS preflight request returns 403 Forbidden or 404 Not Found
  • Blob downloads work with curl but fail in browser JavaScript
  • Azure Storage Explorer can access blobs but web application cannot

Common Causes

  • CORS rules not configured on the storage account (disabled by default)
  • Allowed origins list doesn't include the application domain
  • Allowed methods missing OPTIONS for preflight requests
  • Allowed headers don't include x-ms-* headers required by Azure Storage SDK
  • Max age too short causing frequent preflight requests

Step-by-Step Fix

  1. 1.Check current CORS rules:
  2. 2.```bash
  3. 3.az storage cors list --services b --account-name mystorage --account-key $KEY
  4. 4.`
  5. 5.Add CORS rules for browser access:
  6. 6.```bash
  7. 7.az storage cors add --services b \
  8. 8.--account-name mystorage \
  9. 9.--account-key $KEY \
  10. 10.--origins "https://myapp.com" "https://www.myapp.com" "http://localhost:3000" \
  11. 11.--methods GET PUT POST OPTIONS DELETE \
  12. 12.--allowed-headers "x-ms-*" "Content-Type" "Authorization" \
  13. 13.--exposed-headers "x-ms-*" "Content-Length" "ETag" \
  14. 14.--max-age 3600
  15. 15.`
  16. 16.For Azure Storage SDK JavaScript, ensure all required headers:
  17. 17.```javascript
  18. 18.const { BlobServiceClient } = require("@azure/storage-blob");
  19. 19.const blobServiceClient = new BlobServiceClient(
  20. 20.https://mystorage.blob.core.windows.net,
  21. 21.sasToken
  22. 22.);
  23. 23.// The SDK automatically adds x-ms-* headers that must be in allowed-headers
  24. 24.`
  25. 25.Verify CORS with preflight request:
  26. 26.```bash
  27. 27.curl -X OPTIONS \
  28. 28.-H "Origin: https://myapp.com" \
  29. 29.-H "Access-Control-Request-Method: GET" \
  30. 30.-H "Access-Control-Request-Headers: x-ms-blob-type" \
  31. 31.-I https://mystorage.blob.core.windows.net/container/blob
  32. 32.`
  33. 33.Response should include:
  34. 34.`
  35. 35.Access-Control-Allow-Origin: https://myapp.com
  36. 36.Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS, DELETE
  37. 37.Access-Control-Max-Age: 3600
  38. 38.`
  39. 39.For wildcard development (NOT production):
  40. 40.```bash
  41. 41.az storage cors add --services b \
  42. 42.--account-name mystorage --account-key $KEY \
  43. 43.--origins "*" \
  44. 44.--methods GET PUT POST OPTIONS \
  45. 45.--allowed-headers "*" \
  46. 46.--exposed-headers "*" \
  47. 47.--max-age 3600
  48. 48.`

Prevention

  • Configure CORS during infrastructure provisioning with IaC
  • Include all deployment environment origins (staging, production, localhost)
  • Set max-age to 3600 seconds or higher to reduce preflight overhead
  • Monitor CORS-related 403 errors with Azure Monitor alerts
  • Use Azure Front Door or CDN for centralized CORS management