Cloud file storage is now a standard requirement for modern web applications. Whether you are building a social media platform, blogging system, e-commerce site, or enterprise dashboard, users expect fast and secure file uploads.
One of the best ways to securely upload files to Azure Blob Storage is by using Presigned URLs, commonly known in Azure as SAS (Shared Access Signature) URLs.
Instead of uploading files through your server, the backend generates a temporary secure upload URL, and the frontend uploads directly to Azure Blob Storage.
This approach improves:
- Performance
- Security
- Scalability
- Server resource usage
According to Microsoft Learn Azure SAS Documentation, SAS provides delegated and time-limited access to Azure Storage resources.
What is a Presigned URL (SAS URL)?
A SAS URL is a temporary URL that grants limited access to a blob or container.
The URL contains:
- Expiration time
- Allowed operations
- Resource path
- Security signature
Example:
https://myaccount.blob.core.windows.net/uploads/image.png?
sv=2024-01-01&
sr=b&
sig=xxxxx
The frontend can use this URL to upload files directly without exposing Azure credentials.
Why Use SAS URLs?
Traditional Upload Flow
Browser → Your Server → Azure Blob Storage
Problems:
- Large server bandwidth usage
- Slow uploads
- High server memory consumption
- Difficult scaling
SAS Upload Flow
Browser → Azure Blob Storage
↑
SAS URL from API
Benefits:
- Faster uploads
- Reduced server load
- Better scalability
- Secure temporary access
- Ideal for large files
Azure Blob Storage Architecture
Frontend (JavaScript)
↓
ASP.NET Core API
↓
Generate SAS URL
↓
Frontend uploads directly
↓
Azure Blob Storage
Step 1: Create Azure Storage Account
Go to:
Create:
- Storage Account
- Blob Container
- Enable Blob Storage
Example container:
uploads
Step 2: Install NuGet Package
Install Azure Blob SDK.
dotnet add package Azure.Storage.Blobs
Step 3: Add Configuration
appsettings.json
{
"AzureBlob": {
"ConnectionString": "YOUR_CONNECTION_STRING",
"ContainerName": "uploads",
"AccountName": "youraccount",
"AccountKey": "yourkey"
}
}
Step 4: Create SAS Service
BlobStorageService.cs
using Azure.Storage;
using Azure.Storage.Blobs;
using Azure.Storage.Sas;
public class BlobStorageService
{
private readonly IConfiguration _configuration;
public BlobStorageService(IConfiguration configuration)
{
_configuration = configuration;
}
public string GenerateUploadSasUrl(string fileName)
{
var accountName = _configuration["AzureBlob:AccountName"];
var accountKey = _configuration["AzureBlob:AccountKey"];
var containerName = _configuration["AzureBlob:ContainerName"];
var credential = new StorageSharedKeyCredential(
accountName,
accountKey
);
var blobUri = new Uri(
$"https://{accountName}.blob.core.windows.net/{containerName}/{fileName}"
);
BlobSasBuilder sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerName,
BlobName = fileName,
Resource = "b",
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(15)
};
sasBuilder.SetPermissions(BlobSasPermissions.Create |
BlobSasPermissions.Write);
var sasToken = sasBuilder.ToSasQueryParameters(credential).ToString();
return $"{blobUri}?{sasToken}";
}
}
Step 5: Create API Endpoint
UploadController.cs
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
private readonly BlobStorageService _blobStorageService;
public UploadController(BlobStorageService blobStorageService)
{
_blobStorageService = blobStorageService;
}
[HttpGet("sas-url")]
public IActionResult GetSasUrl(string fileName)
{
var sasUrl = _blobStorageService.GenerateUploadSasUrl(fileName);
return Ok(new
{
uploadUrl = sasUrl
});
}
}
Step 6: Register Service
Program.cs
builder.Services.AddScoped<BlobStorageService>();
Step 7: Frontend JavaScript Upload
HTML
<input type="file" id="fileInput" />
<button onclick="uploadFile()">Upload</button>
JavaScript
async function uploadFile() {
const fileInput = document.getElementById("fileInput");
const file = fileInput.files[0];
if (!file) {
alert("Select file");
return;
}
// Step 1: Get SAS URL
const response = await fetch(
`/api/upload/sas-url?fileName=${encodeURIComponent(file.name)}`
);
const data = await response.json();
// Step 2: Upload directly to Azure
const uploadResponse = await fetch(data.uploadUrl, {
method: "PUT",
headers: {
"x-ms-blob-type": "BlockBlob",
"Content-Type": file.type
},
body: file
});
if (uploadResponse.ok) {
alert("File uploaded successfully");
} else {
alert("Upload failed");
}
}
How It Works
Backend
- Generates secure temporary URL
- Defines permissions
- Sets expiration time
Frontend
- Requests upload URL
- Uploads file directly to Azure Blob Storage
- No file passes through your API server.
Important SAS Permissions
| Permission | Meaning |
|---|---|
| Read | Download file |
| Write | Modify blob |
| Create | Upload new blob |
| Delete | Remove blob |
| List | List container files |
Use minimum required permissions.
Microsoft recommends limiting permissions and expiration scope for security.
Production Security Best Practices
1. Use Short Expiration
Good:
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(10)
Bad:
ExpiresOn = DateTimeOffset.UtcNow.AddYears(1)
2. Generate URLs Server-Side Only
Never expose:
- Storage account key
- Connection string
3. Validate File Types
Example:
string[] allowedExtensions = { ".jpg", ".png", ".pdf" };
4. Use Random File Names
Avoid collisions.
var fileName = $"{Guid.NewGuid()}{extension}";
5. Restrict Permissions
Only grant required access.
Example:
BlobSasPermissions.Create | BlobSasPermissions.Write
NOT:
BlobSasPermissions.All
Large File Upload Optimization
For large files:
- Use chunk uploads
- Use parallel uploads
- Use resumable uploads
Azure Blob Storage supports block-based uploads efficiently.
Common Errors
1. Signature Did Not Match
Usually caused by:
- Wrong account key
- Invalid blob path
- Expired SAS token
This is a common developer issue discussed in Azure community forums.
2. CORS Error
Configure Azure Storage CORS.
Azure Portal:
Storage Account
→ Resource Sharing (CORS)
Add:
Allowed Origins: *
Allowed Methods: PUT, GET
Allowed Headers: *
3. Authentication Failed
Check:
- SAS expiration
- Blob permissions
- Container name
Advanced Production Architecture
Client
↓
API Gateway
↓
Auth Validation
↓
Generate SAS URL
↓
Azure Blob Storage
Optional additions:
- CDN
- Virus scanning
- Queue processing
- Thumbnail generation
User Delegation SAS (Recommended)
Microsoft recommends using User Delegation SAS instead of account-key SAS because it uses Microsoft Entra credentials for better security.
Benefits:
- Better security
- No direct account key exposure
- RBAC integration
Real-World Use Cases
- Social Media Platforms
- Image uploads
- Video uploads
- Blogging Systems
- Thumbnail uploads
- PDF attachments
- E-Commerce
- Product image uploads
- Enterprise Apps
- Document management
- Report uploads
Performance Benefits
| Traditional Upload | SAS Upload |
|---|---|
| High server load | Minimal server load |
| Slow | Faster |
| Expensive scaling | Better scalability |
| Large memory usage | Direct cloud upload |
Conclusion
Using Azure Blob Storage with SAS URLs is one of the best approaches for modern scalable applications.
It provides:
- Secure uploads
- Faster performance
- Reduced backend load
- Better scalability
- Cloud-native architecture
The combination of:
- ASP.NET Core backend
- JavaScript frontend
- Azure Blob Storage
- creates a highly scalable and production-ready upload system suitable for enterprise applications.
For official Azure documentation and SDK examples, see:
Azure SAS Overview Documentation