Globally unique resources that provide access to data management services and serve as the parent namespace for the services.
Capture one failing Storage request’s detailed error code and CallerIpAddress first, because the same SAS succeeds for another user and basic connectivity is already working.
1. Determine where the Blob request originates
Open the failing user’s browser developer tools (F12 → Network), reproduce the failure, and inspect the 403 request URL:
- If the request URL is
https://<account>.blob.core.windows.net/..., the browser is contacting Storage directly. The request originates from that user’s network path, not from App Service. Allowing the App Service VNet therefore does not allow the browser request; Storage network rules and SAS IP restrictions evaluate the client address presented to Storage. Network access and SAS authorization are separate controls.^1^ - If the browser calls only the custom App Service domain, and the application then reads Storage, the request should originate from App Service. App Service VNet integration controls outbound app traffic; private destinations and service endpoints can use the integration subnet, while other traffic can follow the app’s outbound routing configuration.^2^
Using the same VPN does not prove that Storage sees the same source IP. VPN split tunnelling, proxies, or different browser paths can produce different caller addresses.
2. Retrieve the exact Storage error
In the failing browser request, record:
- Response body error
Code, such asAuthorizationFailure,AuthorizationPermissionMismatch, orAuthenticationFailed -
x-ms-request-id - Request timestamp
- Full request URL, with the
sigvalue redacted
A generic HTTP 403 is not enough to identify whether the failure is networking, SAS permissions, expiry, or signature handling. Azure Storage returns a more specific error code for that distinction.^3^
Then query Blob resource logs in the storage account’s Log Analytics workspace:
StorageBlobLogs
| where TimeGenerated > ago(2h)
| where StatusCode == 403
| project TimeGenerated, OperationName, StatusCode,
StatusText, AuthenticationType, CallerIpAddress,
RequesterObjectId, Uri
| order by TimeGenerated desc
Compare a successful request with a failed request, especially CallerIpAddress, AuthenticationType, operation, and URI. These fields are specifically useful for diagnosing denied Storage data-plane operations.^4^
3. Apply the fix that matches the detailed error
-
AuthorizationFailurewith different caller IPs:
Go to Storage account → Networking. If access is limited to selected networks, add the actual egress IP used by the failing clients, or change the design so Blob access occurs server-side through App Service/private connectivity. Storage returns this error when public access, IP, VNet, or firewall rules reject the request.^3^ - The SAS contains
sip=:
Comparesipwith the loggedCallerIpAddress. The SAS is valid only when the request originates from the permitted IP or range. Regenerate it withoutsip, or use the correct stable egress range. Do not broaden the range beyond what is required. -
AuthorizationPermissionMismatch:
Regenerate the SAS with the operation’s requiredsppermission. Reading requires read permission; listing requires list permission. Some write operations require more than one permission.^3^ -
AuthenticationFailed:
Checkst,se,sv, and the signature. For immediate use, omit the start time or allow for clock skew; generate the SAS with an official Azure SDK, Storage Explorer, or the portal. Also verify that the token was not altered while being stored in the App Service setting or appended to the URL—for example, missing?, double encoding, or truncated¶meters.^3^
If users are downloading blobs directly from their browsers, the strongest long-term design is to have App Service issue short-lived, narrowly scoped SAS URLs after authenticating each user, rather than storing and exposing one shared SAS.
References