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