Three Days of Azure Static Web Apps Hell: A Cautionary Tale

by Alien Brain Trust AI Learning
Three Days of Azure Static Web Apps Hell: A Cautionary Tale

Three Days of Azure Static Web Apps Hell: A Cautionary Tale

Meta Description: 72 hours fighting Azure Static Web Apps for a simple form. CORS errors, token mismatches, workflow failures. The full story of what went wrong.

We needed a simple enrollment form. Submit data to Airtable, invite users to a GitHub repo. That’s it. Three API calls total. What followed was 72 hours of Azure-induced frustration that nearly broke us.

The Requirements (Deceptively Simple)

  • HTML form on our Astro blog (hosted on GitHub Pages)
  • API endpoint to receive form submissions
  • Save enrollment data to Airtable
  • Send GitHub repository invitation
  • Free tier (we’re bootstrapped)

Azure Static Web Apps seemed perfect: free tier, managed Azure Functions, automatic GitHub deployments. The docs made it look like a 15-minute setup.

Day 1: CORS From Hell

The first deployment worked. The function existed. But every form submission failed:

Access to fetch at 'https://orange-water-0df3ea40f.6.azurestaticapps.net/api/submit-enrollment'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check

We added CORS headers to staticwebapp.config.json. Nothing.

"globalHeaders": {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type"
}

We created host.json with explicit CORS config. Nothing.

{
  "version": "2.0",
  "cors": {
    "allowedOrigins": ["*"],
    "supportCredentials": false
  }
}

We added CORS headers directly in the function code for OPTIONS preflight:

if (req.method === 'OPTIONS') {
  context.res = {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type'
    }
  };
  return;
}

Still nothing. The headers weren’t reaching the browser.

Day 2: The Deployment Token Maze

We realized the app might not be deploying our changes. So we deleted everything and started fresh. Created a new Static Web App: kind-island-087f3c00f.

Azure created a workflow file. GitHub Actions ran. Build succeeded. But the site showed old code.

The error in the workflow logs:

The content server has rejected the request with: BadRequest
Reason: No matching Static Web App was found or the api key was invalid.

The deployment token didn’t match the app. We updated the GitHub secret. Same error.

We deleted and recreated the app. Three times. Each time:

  1. Azure generates a new app with a random name
  2. Azure creates a workflow file with a specific secret name
  3. Azure doesn’t actually link the token to the app properly
  4. Deployments fail silently

The Azure Portal showed the app was “linked” to our repo. But clicking into Configuration showed:

  • API location: (empty)
  • App artifact location: _site (should be dist)

These settings couldn’t be edited in the portal. They’re “read-only” and come from the workflow file. But the workflow file had the correct settings:

app_location: "/"
api_location: "api"
output_location: "dist"

Azure was ignoring its own configuration.

Day 3: The Definition of Insanity

We tried:

  • Using “GitHub” authentication instead of deployment token
  • Manually copying deployment tokens between portal and GitHub
  • Adding explicit app_build_command: "npm run build" to the workflow
  • Deleting all Azure resources and starting over (again)

Every combination produced the same result: builds succeeded, deployments failed, the app served stale content, CORS errors persisted.

At one point, we had:

  • 3 different Static Web Apps created and deleted
  • 4 different GitHub workflow files
  • 12+ commits just trying to fix deployment
  • 0 working API endpoints

The GitHub repo had the correct code. Raw GitHub URLs proved it. But Azure refused to serve the updated content.

The Psychological Toll

By hour 60, we were questioning everything:

  • “Maybe it’s a free tier limitation?”
  • “Is there a caching layer we don’t know about?”
  • “Are we fundamentally misunderstanding how Azure Static Web Apps work?”

The documentation showed happy paths. Stack Overflow had similar questions—mostly unanswered or marked “duplicate” of different issues. Microsoft Q&A suggested solutions that didn’t work.

We were about to abandon the entire enrollment system and just use an Airtable embed form (giving up control, branding, and the GitHub invitation feature).

The Turning Point

Then we asked the right question: “What if we just don’t use Azure?”

20 minutes later, we had a working solution on Cloudflare Workers.

But that’s tomorrow’s post.

What We Learned (The Hard Way)

  1. “Simple” infrastructure can be anything but. Azure Static Web Apps looked simple. The complexity was hidden in the deployment pipeline.

  2. Silent failures are the worst failures. Builds “succeeded.” Deployments “completed.” But nothing actually worked. No clear error messages until you dug through workflow logs.

  3. Configuration sprawl kills debugging. CORS settings could be in: function code, host.json, staticwebapp.config.json, Azure Portal, or some hidden default. We never figured out which one actually mattered.

  4. Sunk cost fallacy is real. We kept trying to “fix” Azure because we’d already invested 2 days. That third day was pure waste.

  5. The right tool matters more than familiarity. We chose Azure because we knew it. Cloudflare Workers was unfamiliar but actually solved the problem.

The Numbers

MetricAzure AttemptCloudflare Solution
Time spent72+ hours20 minutes
Deployments attempted15+1
Configuration files touched62
Working endpoints01
Final cost$0 (and our sanity)$0

Tomorrow: How Cloudflare Workers solved in 20 minutes what Azure couldn’t do in 3 days.