Skip to content

Commit 8c0985c

Browse files
fix: resolve nginx 405 on POST and CAE internal mode issues
- Change proxy_set_header Host from $host to $proxy_host so Container Apps ingress routes to the API app (not back to web app) - Add client_max_body_size 100m and proxy timeouts to nginx config - Add safe default for APP_BACKEND_API_URL in env.sh - Revert CAE to external mode (internal: false) - only API ingress is internal; web app remains publicly accessible without App Gateway - Remove unnecessary Private DNS Zone for CAE - Update post-deployment scripts to use web app /api proxy when private networking is enabled (instead of calling unreachable internal API) - Add ENABLE_PRIVATE_NETWORKING bicep output for post-deployment detection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1fd18c7 commit 8c0985c

6 files changed

Lines changed: 55 additions & 63 deletions

File tree

infra/main.bicep

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -931,8 +931,8 @@ module avmContainerAppEnv 'br/public:avm/res/app/managed-environment:0.13.2' = {
931931
}
932932
]
933933
enableTelemetry: enableTelemetry
934-
publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
935-
internal: enablePrivateNetworking ? true : false
934+
publicNetworkAccess: 'Enabled'
935+
internal: false
936936

937937
// <========== WAF related parameters
938938

@@ -945,34 +945,6 @@ module avmContainerAppEnv 'br/public:avm/res/app/managed-environment:0.13.2' = {
945945
}
946946
}
947947

948-
// ========== Private DNS Zone for internal Container App Environment ========== //
949-
// When the CAE is internal, its FQDN is resolvable only within the VNet via this zone.
950-
module caeDnsZone 'br/public:avm/res/network/private-dns-zone:0.8.0' = if (enablePrivateNetworking) {
951-
name: take('avm.res.network.private-dns-zone.cae.${solutionSuffix}', 64)
952-
params: {
953-
name: avmContainerAppEnv.outputs.defaultDomain
954-
tags: tags
955-
enableTelemetry: enableTelemetry
956-
a: [
957-
{
958-
name: '*'
959-
aRecords: [
960-
{
961-
ipv4Address: avmContainerAppEnv.outputs.staticIp
962-
}
963-
]
964-
ttl: 300
965-
}
966-
]
967-
virtualNetworkLinks: [
968-
{
969-
name: take('vnetlink-vnet-${solutionSuffix}-cae', 64)
970-
virtualNetworkResourceId: virtualNetwork!.outputs.resourceId
971-
}
972-
]
973-
}
974-
}
975-
976948
// //=========== Managed Identity for Container Registry ========== //
977949
module avmContainerRegistryReader 'br/public:avm/res/managed-identity/user-assigned-identity:0.5.0' = {
978950
name: take('avm.res.managed-identity.user-assigned-identity.${solutionSuffix}', 64)
@@ -1971,5 +1943,8 @@ output CONTAINER_REGISTRY_LOGIN_SERVER string = avmContainerRegistry.outputs.log
19711943
@description('The name of the Content Understanding AI Services account.')
19721944
output CONTENT_UNDERSTANDING_ACCOUNT_NAME string = avmAiServices_cu.outputs.name
19731945

1946+
@description('Whether private networking (WAF) is enabled.')
1947+
output ENABLE_PRIVATE_NETWORKING bool = enablePrivateNetworking
1948+
19741949
@description('The resource group the resources were deployed into.')
19751950
output AZURE_RESOURCE_GROUP string = resourceGroup().name

infra/main_custom.bicep

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -934,8 +934,8 @@ module avmContainerAppEnv 'br/public:avm/res/app/managed-environment:0.13.2' = {
934934
}
935935
]
936936
enableTelemetry: enableTelemetry
937-
publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled'
938-
internal: enablePrivateNetworking ? true : false
937+
publicNetworkAccess: 'Enabled'
938+
internal: false
939939

940940
// <========== WAF related parameters
941941

@@ -948,34 +948,6 @@ module avmContainerAppEnv 'br/public:avm/res/app/managed-environment:0.13.2' = {
948948
}
949949
}
950950

951-
// ========== Private DNS Zone for internal Container App Environment ========== //
952-
// When the CAE is internal, its FQDN is resolvable only within the VNet via this zone.
953-
module caeDnsZone 'br/public:avm/res/network/private-dns-zone:0.8.0' = if (enablePrivateNetworking) {
954-
name: take('avm.res.network.private-dns-zone.cae.${solutionSuffix}', 64)
955-
params: {
956-
name: avmContainerAppEnv.outputs.defaultDomain
957-
tags: tags
958-
enableTelemetry: enableTelemetry
959-
a: [
960-
{
961-
name: '*'
962-
aRecords: [
963-
{
964-
ipv4Address: avmContainerAppEnv.outputs.staticIp
965-
}
966-
]
967-
ttl: 300
968-
}
969-
]
970-
virtualNetworkLinks: [
971-
{
972-
name: take('vnetlink-vnet-${solutionSuffix}-cae', 64)
973-
virtualNetworkResourceId: virtualNetwork!.outputs.resourceId
974-
}
975-
]
976-
}
977-
}
978-
979951
// //=========== Managed Identity for Container Registry ========== //
980952
module avmContainerRegistryReader 'br/public:avm/res/managed-identity/user-assigned-identity:0.5.0' = {
981953
name: take('avm.res.managed-identity.user-assigned-identity.${solutionSuffix}', 64)
@@ -2012,5 +1984,8 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = avmContainerRegistry.outputs.l
20121984
@description('The name of the Content Understanding AI Services account.')
20131985
output CONTENT_UNDERSTANDING_ACCOUNT_NAME string = avmAiServices_cu.outputs.name
20141986

1987+
@description('Whether private networking (WAF) is enabled.')
1988+
output ENABLE_PRIVATE_NETWORKING bool = enablePrivateNetworking
1989+
20151990
@description('The resource group the resources were deployed into.')
20161991
output AZURE_RESOURCE_GROUP string = resourceGroup().name

infra/scripts/post_deployment.ps1

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,26 @@ Write-Host " [Link] Portal URL: $WORKFLOW_APP_PORTAL_URL"
5050

5151
Write-Host ""
5252
Write-Host "[Package] Registering schemas and creating schema set..."
53+
54+
# Check if private networking (WAF) is enabled
55+
$ENABLE_PRIVATE_NETWORKING = $null
56+
try {
57+
$ENABLE_PRIVATE_NETWORKING = azd env get-value ENABLE_PRIVATE_NETWORKING 2>$null
58+
} catch { }
59+
60+
# When private networking is enabled, the API is internal-only (ingressExternal=false).
61+
# Use the web app's /api proxy to reach the backend through same-origin routing.
62+
if ($ENABLE_PRIVATE_NETWORKING -eq "true") {
63+
Write-Host " [Info] Private networking (WAF) is enabled. Using web app /api proxy to reach backend."
64+
$ApiBaseUrl = "https://$CONTAINER_WEB_APP_FQDN/api"
65+
} else {
66+
$ApiBaseUrl = "https://$CONTAINER_API_APP_FQDN"
67+
}
68+
5369
Write-Host " [Wait] Waiting for API to be ready..."
5470

5571
$MaxRetries = 10
5672
$RetryInterval = 15
57-
$ApiBaseUrl = "https://$CONTAINER_API_APP_FQDN"
5873
$ApiReady = $false
5974

6075
for ($i = 1; $i -le $MaxRetries; $i++) {

infra/scripts/post_deployment.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,23 @@ echo " 🔗 Portal URL: $WORKFLOW_APP_PORTAL_URL"
6060

6161
echo ""
6262
echo "📦 Registering schemas and creating schema set..."
63+
64+
# Check if private networking (WAF) is enabled
65+
ENABLE_PRIVATE_NETWORKING=$(azd env get-value ENABLE_PRIVATE_NETWORKING 2>/dev/null || echo "")
66+
67+
# When private networking is enabled, the API is internal-only (ingressExternal=false).
68+
# Use the web app's /api proxy to reach the backend through same-origin routing.
69+
if [ "$ENABLE_PRIVATE_NETWORKING" = "true" ]; then
70+
echo " ℹ️ Private networking (WAF) is enabled. Using web app /api proxy to reach backend."
71+
API_BASE_URL="https://$CONTAINER_WEB_APP_FQDN/api"
72+
else
73+
API_BASE_URL="https://$CONTAINER_API_APP_FQDN"
74+
fi
75+
6376
echo " ⏳ Waiting for API to be ready..."
6477

6578
MAX_RETRIES=10
6679
RETRY_INTERVAL=15
67-
API_BASE_URL="https://$CONTAINER_API_APP_FQDN"
6880

6981
for i in $(seq 1 $MAX_RETRIES); do
7082
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$API_BASE_URL/schemavault/" 2>/dev/null || echo "000")

src/ContentProcessorWeb/env.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
#!/bin/sh
2+
3+
# Ensure APP_BACKEND_API_URL has a safe default so nginx can always start.
4+
# When not set, the /api/ proxy_pass will point to a non-routable placeholder
5+
# and return 502, which is acceptable — the direct API path still works.
6+
export APP_BACKEND_API_URL="${APP_BACKEND_API_URL:-http://localhost:8080}"
7+
28
for i in $(env | grep ^APP_)
39
do
410
key=$(echo $i | cut -d '=' -f 1)

src/ContentProcessorWeb/nginx-custom.conf

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,28 @@ http {
1414
types_hash_max_size 2048;
1515
types_hash_bucket_size 128;
1616

17+
# Allow large file uploads through the /api proxy
18+
client_max_body_size 100m;
19+
1720
server {
1821
listen 3000;
1922
server_name localhost;
2023

2124
# Route browser API calls through the web container so private backend
2225
# endpoints remain internal-only in WAF/private networking deployments.
26+
# APP_BACKEND_API_URL is substituted at runtime by env.sh; if unset, this
27+
# block is effectively a no-op (returns 502) which is safe for nginx startup.
2328
location /api/ {
2429
proxy_http_version 1.1;
25-
proxy_set_header Host $host;
30+
proxy_set_header Host $proxy_host;
31+
proxy_set_header X-Real-IP $remote_addr;
2632
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
2733
proxy_set_header X-Forwarded-Proto $scheme;
2834
proxy_set_header Upgrade $http_upgrade;
2935
proxy_set_header Connection "upgrade";
36+
proxy_connect_timeout 60s;
37+
proxy_send_timeout 120s;
38+
proxy_read_timeout 120s;
3039
proxy_pass APP_BACKEND_API_URL/;
3140
}
3241

0 commit comments

Comments
 (0)