Problema
En entornos de producción es frecuente que una Azure Function expuesta vía HTTP reciba archivos desde un cliente web. Cuando el tamaño del payload supera los 30 MB, los desarrolladores observan respuestas intermitentes: a veces la carga finaliza con éxito, otras veces la petición termina con error 500 o 408. El comportamiento es aleatorio, no depende del cliente ni del tipo de archivo, y ocurre solo en producción; en local la función acepta archivos de 100 MB sin inconvenientes. Este patrón indica que algo en la cadena de ejecución (plano de Azure Functions, red, autenticación o Blob Storage) está imponiendo restricciones que no se reflejan en el entorno de desarrollo.
Causa
1. Límite de tamaño de petición HTTP en Azure Functions
En el plan Consumption y Premium, la plataforma impone un límite de 100 MB para la carga completa del cuerpo HTTP. Sin embargo, el gateway de Azure Front Door / Application Gateway que suele estar delante de la función puede tener un límite menor (por ejemplo 30 MB). Si la arquitectura incluye un Azure API Management o App Service Front Door, su política de tamaño máximo sobrescribe el límite de la función y genera errores cuando se supera.
2. Timeout de ejecución
Una función que procesa el stream del archivo y lo escribe en Blob Storage puede tardar varios segundos. En Consumption el tiempo máximo es 5 min (configurable a 10 min) y en Premium el límite es 60 min. Si la función está configurada con el valor por defecto (5 min) y la red o el cliente son lentos, la ejecución se corta antes de completar la escritura, provocando fallos intermitentes.
3. Configuración de host.json para http y maxConcurrentRequests
El archivo host.json controla el comportamiento del runtime. Si maxRequestBodySize está definido (por ejemplo 30 MB) o si maxConcurrentRequests está bajo, la función rechaza o retrasa peticiones grandes bajo carga.
4. Credenciales y acceso a Blob Storage
Cuando la función usa DefaultAzureCredential, el token se renueva automáticamente. En entornos con Managed Identity restringido, un refresco fallido del token durante una carga larga puede producir un 401 o 403 inesperado. En local, la credencial de desarrollador siempre está disponible, lo que explica la diferencia de comportamiento.
5. Configuración de red del Storage Account
Si el Storage Account tiene firewall o private endpoints y la función está en una VNet sin acceso directo, el intento de escritura puede fallar después de varios segundos, generando errores que aparecen solo en producción.
Solución
Paso 1: Verificar y ampliar el límite de tamaño en el gateway
- Si usas Azure Front Door, revisa la política
frontendEndpoint→customHttpsConfiguration→maxRequestBodySize. Aumenta el valor a 100 MB o más. - En API Management, revisa la política
<set-body>o el atributomax-request-body-sizey ajústalo. - En App Service (cuando la Function se ejecuta bajo un App Service Plan), modifica la configuración
WEBSITES_CONTAINER_START_TIME_LIMITyWEBSITES_MAX_HTTP_CONTENT_LENGTHmediante Azure CLI.
az functionapp config appsettings set \
--name MyFunctionApp \
--resource-group MyRG \
--settings WEBSITES_MAX_HTTP_CONTENT_LENGTH=104857600
Paso 2: Ajustar timeout de la función
En el plan Consumption, habilita el timeout extendido (máximo 10 min). En Premium, define un timeout mayor si la carga es lenta.
az functionapp config set \
--name MyFunctionApp \
--resource-group MyRG \
--timeout 600 # 600 segundos = 10 minutos
Paso 3: Configurar host.json para aceptar cuerpos grandes
Añade o modifica la sección http:
{
"version": "2.0",
"extensions": {
"http": {
"maxRequestBodySize": 104857600,
"routePrefix": "api"
}
}
}
Esto eleva el límite interno del runtime a 100 MB.
Paso 4: Implementar streaming directo a Blob Storage
En lugar de cargar el archivo completo en memoria, usa BlobClient.UploadAsync(stream, cancellationToken) con TransferOptions que habilitan multipart streaming. De este modo la función solo mantiene un buffer pequeño y reduce la probabilidad de timeout.
var blobClient = new BlobContainerClient(storageConnection, "uploads")
.GetBlobClient(fileName);
var options = new BlobUploadOptions
{
TransferOptions = new StorageTransferOptions
{
MaximumConcurrency = 4,
MaximumTransferSize = 4 * 1024 * 1024 // 4 MiB
}
};
await blobClient.UploadAsync(request.Body, options, cancellationToken);
Paso 5: Asegurar que la Managed Identity tenga permisos persistentes
Verifica que la identidad asignada al Function App tenga el rol Storage Blob Data Contributor en el scope del storage account. Además, habilita el token cache para evitar refrescos durante la operación.
az role assignment create \
--assignee <principalId> \
--role "Storage Blob Data Contributor" \
--scope /subscriptions/<subId>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<account>
Paso 6: Revisar la red del Storage Account
Si el storage está detrás de un private endpoint, confirma que la VNet de la Function App tiene la ruta correcta y que los NSG permiten tráfico saliente a *.blob.core.windows.net en puerto 443.
Cuándo aplicar esta solución
- Síntomas: errores 500/408 al subir archivos >30 MB, comportamiento aleatorio, logs de Function indican “Request body too large” o “Function timeout”.
- Entorno: Azure Functions en HTTP trigger, con Front Door, API Management o App Service como front‑end, y Blob Storage como destino.
- No aplica: cuando el problema se origina en el cliente (por ejemplo, navegadores que limitan
FormData) o cuando el archivo nunca supera el límite de 30 MB.
Código
# 1. Aumentar límite de cuerpo HTTP en la Function App
az functionapp config appsettings set \
--name MyFunctionApp \
--resource-group MyRG \
--settings WEBSITES_MAX_HTTP_CONTENT_LENGTH=104857600
# 2. Extender timeout (solo Consumption)
az functionapp config set \
--name MyFunctionApp \
--resource-group MyRG \
--timeout 600
# 3. Asignar rol de acceso al storage
az role assignment create \
--assignee $(az functionapp identity show -g MyRG -n MyFunctionApp --query principalId -o tsv) \
--role "Storage Blob Data Contributor" \
--scope /subscriptions/<subId>/resourceGroups/MyRG/providers/Microsoft.Storage/storageAccounts/myaccount
Verificación
- Prueba de carga: usa
curlcon--data-binary @bigfile.biny verifica que la respuesta sea 200 en menos del timeout configurado. - Logs de Application Insights: busca eventos
FunctionExecutionFailedy revisa la propiedadexceptionMessage. Debe desaparecer la referencia a “Request body too large” o “Operation timed out”. - Métricas de Storage: confirma que el número de
PutBlobaumenta y que no aparecen errores de autorización. - Gateway: revisa en el portal que la política de tamaño de cuerpo muestra el nuevo valor.
Notas adicionales
- En planes Premium o Dedicated, el límite de 100 MB sigue vigente; si necesitas superar ese umbral, considera usar Azure Blob Storage SAS URL y que el cliente suba directamente, evitando pasar por la Function.
- El uso de Azure Front Door con WAF puede bloquear peticiones grandes si hay una regla personalizada; revisa las reglas activas.
- Cuando la Function está bajo alta concurrencia, el parámetro
maxConcurrentRequestsenhost.jsonpuede provocar throttling. Ajusta según la capacidad de tu plan. - Mantén la versión del runtime en al menos 4.x para que
maxRequestBodySizesea respetado; versiones anteriores ignoran la configuración.