Azure Static Web Apps
Overview
Azure Static Web Apps (SWA) is a service that automatically builds and deploys full-stack web apps from a GitHub or Azure DevOps repository. It provides globally distributed hosting for static content (HTML, CSS, JavaScript, images), integrated serverless API backends via Azure Functions, built-in authentication/authorization, PR preview environments, and custom domain support with free SSL.
SWA is designed for modern web frameworks: React, Angular, Vue.js, Svelte, Next.js (hybrid), Nuxt, Gatsby, Blazor, and vanilla HTML/JS. The Free tier includes 100 GB bandwidth/month and 2 custom domains.
ARM REST API Endpoints
Base URL: https://management.azure.com
API Version: 2023-12-01
Static Sites
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/staticSites |
List SWA resources in resource group |
| PUT | /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/staticSites/{name} |
Create or update a SWA |
| GET | /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/staticSites/{name} |
Get SWA details |
| DELETE | /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/staticSites/{name} |
Delete a SWA |
Custom Domains
| Method | Endpoint | Purpose |
|---|---|---|
| PUT | /.../staticSites/{name}/customDomains/{domainName} |
Add custom domain |
| GET | /.../staticSites/{name}/customDomains |
List custom domains |
| DELETE | /.../staticSites/{name}/customDomains/{domainName} |
Remove custom domain |
App Settings
| Method | Endpoint | Purpose |
|---|---|---|
| PUT | /.../staticSites/{name}/config/appsettings |
Update app settings |
| POST | /.../staticSites/{name}/listAppSettings |
List app settings |
Linked Backends
| Method | Endpoint | Purpose |
|---|---|---|
| PUT | /.../staticSites/{name}/linkedBackends/{backendName} |
Link an existing Functions app or Container App |
| GET | /.../staticSites/{name}/linkedBackends |
List linked backends |
JSON Request Bodies
Create Static Web App (with GitHub)
{
"location": "eastus2",
"sku": { "name": "Free" },
"properties": {
"repositoryUrl": "https://github.com/org/repo",
"branch": "main",
"repositoryToken": "<github-pat>",
"buildProperties": {
"appLocation": "/",
"apiLocation": "api",
"outputLocation": "build"
}
}
}
Create Static Web App (standalone)
{
"location": "eastus2",
"sku": { "name": "Standard" },
"properties": {}
}
Update App Settings
{
"properties": {
"AAD_CLIENT_ID": "xxx-xxx",
"AAD_CLIENT_SECRET": "xxx-xxx",
"API_KEY": "my-secret-key"
}
}
staticwebapp.config.json
The staticwebapp.config.json file in the app root controls routing, authentication, headers, and platform settings. This is the most important configuration file for SWA.
Routes
{
"routes": [
{
"route": "/admin/*",
"allowedRoles": ["admin"],
"methods": ["GET", "POST"]
},
{
"route": "/api/public/*",
"allowedRoles": ["anonymous"]
},
{
"route": "/dashboard",
"allowedRoles": ["authenticated"],
"redirect": "/.auth/login/aad"
},
{
"route": "/old-page",
"redirect": "/new-page",
"statusCode": 301
},
{
"route": "/api/internal/*",
"rewrite": "/api/handler"
}
]
}
Route properties:
route: URL pattern (supports*wildcard for path segments)allowedRoles: Array of roles (anonymous,authenticated, or custom roles)methods: HTTP methods allowed (default: all)redirect: URL to redirect to (302 by default, usestatusCode: 301for permanent)rewrite: Internal rewrite target (URL stays the same in browser)statusCode: HTTP status for redirects (301 or 302)
Navigation Fallback (SPA)
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/images/*.{png,jpg,gif}", "/css/*", "/api/*"]
}
}
Essential for single-page apps — ensures client-side routing works. The exclude array prevents static assets and API calls from being rewritten.
Response Overrides
{
"responseOverrides": {
"401": {
"redirect": "/.auth/login/aad",
"statusCode": 302
},
"404": {
"rewrite": "/404.html"
}
}
}
Global Headers
{
"globalHeaders": {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Content-Security-Policy": "default-src 'self'; script-src 'self'",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "camera=(), microphone=(), geolocation=()"
}
}
Networking
{
"networking": {
"allowedIpRanges": ["203.0.113.0/24", "AzureFrontDoor.Backend"]
}
}
Platform Settings
{
"platform": {
"apiRuntime": "node:20"
}
}
Authentication
SWA provides built-in authentication with zero configuration for several providers.
Built-in Providers
| Provider | Login URL | Notes |
|---|---|---|
| Azure AD (Entra ID) | /.auth/login/aad |
Requires custom config for tenant restriction |
| GitHub | /.auth/login/github |
Works out of the box |
/.auth/login/twitter |
Works out of the box | |
/.auth/login/google |
Requires custom OIDC config on Standard tier | |
| Apple | /.auth/login/apple |
Requires custom OIDC config on Standard tier |
| Custom OIDC | /.auth/login/{provider-name} |
Any OIDC-compliant provider |
Auth Endpoints
| Endpoint | Purpose |
|---|---|
/.auth/login/{provider} |
Initiate login flow |
/.auth/logout |
Log out and clear session |
/.auth/me |
Get current user claims (JSON) |
/.auth/purge/{provider} |
Remove stored auth data |
/.auth/me Response
{
"clientPrincipal": {
"identityProvider": "aad",
"userId": "abc123",
"userDetails": "user@contoso.com",
"userRoles": ["authenticated", "admin"],
"claims": [
{ "typ": "name", "val": "John Doe" },
{ "typ": "email", "val": "user@contoso.com" }
]
}
}
Custom Azure AD Configuration
{
"auth": {
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"clientIdSettingName": "AAD_CLIENT_ID",
"clientSecretSettingName": "AAD_CLIENT_SECRET"
}
}
}
}
}
Store AAD_CLIENT_ID and AAD_CLIENT_SECRET in SWA app settings (not in the config file).
Role Assignment via API
Custom roles are assigned via an invitations API or a custom role assignment function at /api/roles:
{
"roles": ["admin", "editor"]
}
The function receives the client principal in the x-ms-client-principal header (base64-encoded).
Managed Functions API
SWA includes built-in serverless API support via Azure Functions.
Directory Structure
project/
src/ # Frontend app
api/ # Managed Functions API
hello/
function.json
index.js
package.json
function.json Example
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get", "post"]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
Handler Example (JavaScript)
module.exports = async function (context, req) {
// Access client principal from header
const header = req.headers["x-ms-client-principal"];
let user = null;
if (header) {
const encoded = Buffer.from(header, "base64");
user = JSON.parse(encoded.toString("utf8"));
}
context.res = {
body: { message: "Hello", user: user?.userDetails }
};
};
API Route Convention
All API functions are accessible at /api/{function-name}. The api/ prefix is mandatory and automatically routed to the managed Functions backend.
PR Preview Environments
SWA automatically creates preview environments for pull requests.
How It Works
- Developer opens a PR against the configured branch.
- GitHub Actions builds and deploys the PR to a unique preview URL.
- The preview URL is posted as a comment on the PR.
- Each push to the PR branch redeploys the preview.
- When the PR is merged or closed, the preview environment is automatically deleted.
Preview URLs
Format: https://<random-hash>-<pr-number>.<region>.azurestaticapps.net
Considerations
- Preview environments share the same app settings as production (be careful with API keys).
- Each SWA resource supports up to 10 preview environments on Standard tier (3 on Free).
- Preview environments have their own Functions API instances.
SWA CLI
The Azure Static Web Apps CLI provides local development and deployment tools.
| Command | Purpose |
|---|---|
swa init |
Initialize a new SWA project with framework detection |
swa start |
Start local development server with API proxy |
swa build |
Build the app using detected framework |
swa deploy |
Deploy to Azure (requires deployment token or login) |
swa login |
Authenticate with Azure |
swa link |
Link local project to an Azure SWA resource |
Local Development
# Start with both frontend and API
swa start --app-location ./src --api-location ./api
# Start with custom dev server (e.g., Vite)
swa start http://localhost:5173 --api-location ./api
The SWA CLI proxies authentication locally — /.auth/login/{provider} returns mock auth data for testing.
Permissions / Scopes
| Scope / Requirement | Purpose |
|---|---|
https://management.azure.com/.default |
ARM REST API for creating/managing SWA resources |
| GitHub PAT (repo scope) | Required for linking SWA to a GitHub repository |
| SWA deployment token | Used by CI/CD to deploy without Azure credentials |
| Contributor RBAC role | Create and manage SWA resources in the resource group |
Error Handling
ARM API Errors
| Status | Meaning | Action |
|---|---|---|
| 400 | Bad Request — invalid buildProperties or missing required field | Verify appLocation, apiLocation, outputLocation |
| 403 | Forbidden — insufficient RBAC permissions | Verify Contributor role on resource group |
| 404 | Not Found — SWA resource does not exist | Confirm resource name and subscription |
| 409 | Conflict — resource name taken or deployment in progress | SWA names are globally unique; wait for deployment |
| 429 | Too Many Requests | Retry after Retry-After header |
Config Validation Errors
| Error | Cause | Fix |
|---|---|---|
Invalid route pattern |
Route contains unsupported characters or syntax | Use * for wildcards, not regex |
Unknown role |
Role in route not defined in auth config | Add custom role to identity providers or use built-in roles |
Duplicate route |
Two routes with the same pattern | Remove duplicate; routes are evaluated in order |
Navigation fallback conflict |
Fallback rewrite conflicts with an explicit route | Move explicit routes above the fallback |
Common Patterns
React SPA with Azure AD Auth
Build a React SPA with Azure AD authentication:
- Create a React app with
npx create-react-app my-app. - Add
staticwebapp.config.jsonwith navigation fallback:"rewrite": "/index.html". - Configure Azure AD provider in the auth section.
- Protect
/dashboard/*routes with"allowedRoles": ["authenticated"]. - Set response override for 401: redirect to
/.auth/login/aad. - In React, fetch
/.auth/meon app load to get the current user. - Use the
clientPrincipal.userRolesarray for conditional rendering.
Next.js Hybrid with API Routes
Deploy a Next.js hybrid app (SSG + SSR + API):
- Configure
next.config.jswithoutput: "standalone"for hybrid rendering. - Set
buildProperties.appLocation: "/"andoutputLocation: ".next". - Use SWA's linked backends feature to connect a separate Azure Functions app for custom API routes.
- For static pages, Next.js generates HTML at build time.
- For dynamic pages, use client-side fetching from the managed Functions API.
- Add
staticwebapp.config.jsonwith route rules for auth and API access.
PR Preview Environments for Team Review
Set up a team review workflow with PR previews:
- Create the SWA resource linked to the GitHub repository.
- Enable the GitHub Actions workflow (auto-generated by SWA).
- Team members open PRs — preview environments are auto-created.
- Add a
statusCheckto require PR review before merge. - Preview URLs are posted as PR comments for easy access.
- On merge, preview is cleaned up and main branch is deployed to production.
Best Practices
- Navigation fallback: Always configure for SPAs to prevent 404s on client-side routes.
- Security headers: Add
globalHeadersfor CSP, X-Frame-Options, and other security headers. - Role-based access: Use custom roles for fine-grained authorization beyond
authenticated. - App settings for secrets: Never put secrets in
staticwebapp.config.json— use app settings. - SWA CLI for local dev: Use
swa startfor local development with auth mocking. - Exclude static assets: In navigation fallback, exclude images, CSS, and API calls from rewriting.
- Standard tier for production: Free tier has rate limits and fewer features; use Standard for production.
Reference Files
| Reference | Path | Content |
|---|---|---|
| Config Schema | references/config-schema.md |
Complete staticwebapp.config.json reference |
| Auth Providers | references/auth-providers.md |
Built-in and custom OIDC provider setup |
| SWA CLI | references/swa-cli.md |
CLI commands, local dev, and deployment |
| ARM API | references/arm-api.md |
ARM REST API for Static Sites management |
Example Files
| Example | Path | Content |
|---|---|---|
| React SPA + AAD | examples/react-spa-aad.md |
React app with Azure AD auth and protected routes |
| Next.js Hybrid | examples/nextjs-hybrid.md |
Next.js with SSG, API routes, and linked backend |
| PR Preview Setup | examples/pr-preview.md |
Team workflow with GitHub Actions and preview environments |
| Full Config | examples/full-config.md |
Complete staticwebapp.config.json for production |
Azure CLI Reference
Resource Management
# Show SWA details
az staticwebapp show --name <app> --resource-group <rg>
az staticwebapp show --name <app> --resource-group <rg> --query "{DefaultHostname:defaultHostname, SKU:sku.name, Branch:repositoryUrl}"
# List SWA apps
az staticwebapp list --resource-group <rg> --output table
az staticwebapp list --output table # all in subscription
# Delete SWA
az staticwebapp delete --name <app> --resource-group <rg> --yes
Custom Domain Management
# Add custom domain
az staticwebapp custom-domain create --name <app> --resource-group <rg> --hostname www.contoso.com
az staticwebapp custom-domain create --name <app> --resource-group <rg> --hostname contoso.com --validation-method dns-txt-token
# Show custom domain (check validation status)
az staticwebapp custom-domain show --name <app> --resource-group <rg> --hostname www.contoso.com
# List custom domains
az staticwebapp custom-domain list --name <app> --resource-group <rg> --output table
# Delete custom domain
az staticwebapp custom-domain delete --name <app> --resource-group <rg> --hostname www.contoso.com --yes
Functions Linking
# Link Azure Functions backend
az staticwebapp functions link --name <app> --resource-group <rg> --function-resource-id <function-app-resource-id>
# Show linked functions
az staticwebapp functions show --name <app> --resource-group <rg>
# Unlink functions
az staticwebapp functions unlink --name <app> --resource-group <rg>
App Settings
# Delete app setting
az staticwebapp appsettings delete --name <app> --resource-group <rg> --setting-names KEY1 KEY2
User Management
# List users and roles
az staticwebapp users list --name <app> --resource-group <rg> --output table
# Update user role
az staticwebapp users update --name <app> --resource-group <rg> --user-id <user-id> --roles "reader,contributor"
# Invite user
az staticwebapp users invite --name <app> --resource-group <rg> --domain contoso.com --provider aad --user-details user@contoso.com --role admin --invitation-expiration-in-hours 72
Deployment Tokens
# Get deployment token
az staticwebapp secrets list --name <app> --resource-group <rg>
# Reset deployment token
az staticwebapp secrets reset-api-key --name <app> --resource-group <rg>
SWA CLI
# Link local project to Azure resource
swa link --resource-group <rg> --app-name <app>
# Build with options
swa build --app-location ./src --api-location ./api --output-location ./dist
Progressive Disclosure — Reference Files
| Topic | File |
|---|---|
| staticwebapp.config.json structure; routing rules; response overrides; navigation fallback; header customization; platform settings | references/deployment-config.md |
Built-in providers (AAD, GitHub, Twitter, Google); custom OpenID Connect; role-based access; /.auth/me endpoint; role assignment function |
references/auth-providers.md |
| Managed functions vs linked backends; API routes; environment variables; local dev with SWA CLI; CORS configuration | references/api-functions-integration.md |
| GitHub Actions workflow; preview environment lifecycle; quality gates; branch policies; Azure DevOps alternative | references/pr-preview-envs.md |