Configure Azure Static Web Apps

Configuration for Azure Static Web Apps is defined in the staticwebapp.config.json file, which controls the following settings:

  • Routing
  • Authentication
  • Authorization
  • Fallback rules
  • HTTP response overrides
  • Global HTTP header definitions
  • Custom MIME types
  • Networking

Note

routes.json that was previously used to configure routing is deprecated. Use staticwebapp.config.json as described in this article to configure routing and other settings for your static web app.

This document is regarding Azure Static Web Apps, which is a standalone product and separate from the static website hosting feature of Azure Storage.

File location

The recommended location for the staticwebapp.config.json is in the folder set as the app_location in the workflow file. However, the file may be placed in any subfolder within the folder set as the app_location.

See the example configuration file for details.

Important

The deprecated routes.json file is ignored if a staticwebapp.config.json exists.

Routes

You can define rules for one or more routes in your static web app. Route rules allow you to restrict access to users in specific roles or perform actions such as redirect or rewrite. Routes are defined as an array of routing rules. See the example configuration file for usage examples.

  • Rules are defined in the routes array, even if you only have one route.
  • Rules are evaluated in the order as they appear in the routes array.
  • Rule evaluation stops at the first match. A match occurs when the route property and a value in the methods array (if specified) match the request. Each request can match at most one rule.

The routing concerns significantly overlap with authentication (identifying the user) and authorization (assigning abilities to the user) concepts. Make sure to read the authentication and authorization guide along with this article.

Defining routes

Each rule is composed of a route pattern, along with one or more of the optional rule properties. Route rules are defined in the routes array. See the example configuration file for usage examples.

Important

Only the route and methods (if specified) properties are used to determine whether a rule matches a request.

Rule property Required Default value Comment
route Yes n/a The route pattern requested by the caller.
  • Wildcards are supported at the end of route paths.
    • For instance, the route /admin* matches any route beginning with /admin.
methods No All methods Defines an array of request methods which match a route. Available methods include: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, and PATCH.
rewrite No n/a Defines the file or path returned from the request.
  • Is mutually exclusive to a redirect rule.
  • Rewrite rules don't change the browser's location.
  • Values must be relative to the root of the app.
redirect No n/a Defines the file or path redirect destination for a request.
  • Is mutually exclusive to a rewrite rule.
  • Redirect rules change the browser's location.
  • Default response code is a 302 (temporary redirect), but you can override with a 301 (permanent redirect).
statusCode No 301 or 302 for redirects The HTTP status code of the response.
headers No n/a Set of HTTP headers added to the response.
  • Route-specific headers override globalHeaders when the route-specific header is the same as the global header is in the response.
  • To remove a header, set the value to an empty string.
allowedRoles No anonymous Defines an array of role names required to access a route.
  • Valid characters include a-z, A-Z, 0-9, and _.
  • The built-in role, anonymous, applies to all users.
  • The built-in role, authenticated, applies to any logged-in user.
  • Users must belong to at least one role.
  • Roles are matched on an OR basis.
    • If a user is in any of the listed roles, then access is granted.
  • Individual users are associated to roles through invitations.

Each property has a specific purpose in the request/response pipeline.

Purpose Properties
Match routes route, methods
Process after a rule is matched and authorized rewrite (modifies request)

redirect, headers, statusCode (modifies response)
Authorize after a route is matched allowedRoles

Specifying route patterns

The route property can be an exact route or a wildcard pattern.

Exact route

To define an exact route, place the full path of the file in the route property.

{
  "route": "/profile/index.html",
  "allowedRoles": ["authenticated"]
}

This rule matches requests for the file /profile/index.html. Because index.html is the default file, the rule also matches requests for the folder (/profile or /profile/).

Important

If you use a folder path (/profile or /profile/) in the route property, it won't match requests for the file /profile/index.html. When protecting a route that serves a file, always use the full path of the file such as /profile/index.html.

Wildcard pattern

Wildcard rules match all requests in a route pattern, are only supported at the end of a path, and may be filtered by file extension. See the example configuration file for usage examples.

For instance, to implement routes for a calendar application, you can rewrite all URLs that fall under the calendar route to serve a single file.

{
  "route": "/calendar*",
  "rewrite": "/calendar.html"
}

The calendar.html file can then use client-side routing to serve a different view for URL variations like /calendar/january/1, /calendar/2020, and /calendar/overview.

Note

A route pattern of /calendar/* matches all requests under the /calendar/ path. However, it will not match requests for the paths /calendar or /calendar.html. Use /calendar* to match all requests that begin with /calendar.

You can filter wildcard matches by file extension. For instance, if you wanted to add a rule that only matches HTML files in a given path you could create the following rule:

{
  "route": "/articles/*.html",
  "headers": {
    "Cache-Control": "public, max-age=604800, immutable"
  }
}

To filter on multiple file extensions, you include the options in curly braces, as shown in this example:

{
  "route": "/images/thumbnails/*.{png,jpg,gif}",
  "headers": {
    "Cache-Control": "public, max-age=604800, immutable"
  }
}

Common uses cases for wildcard routes include:

  • Serving a specific file for an entire path pattern
  • Enforcing authentication and authorization rules
  • Implementing specialized caching rules

Securing routes with roles

Routes are secured by adding one or more role names into a rule's allowedRoles array. See the example configuration file for usage examples.

By default, every user belongs to the built-in anonymous role, and all logged-in users are members of the authenticated role. Optionally, users are associated to custom roles via invitations.

For instance, to restrict a route to only authenticated users, add the built-in authenticated role to the allowedRoles array.

{
  "route": "/profile*",
  "allowedRoles": ["authenticated"]
}

You can create new roles as needed in the allowedRoles array. To restrict a route to only administrators, you could define your own role named administrator, in the allowedRoles array.

{
  "route": "/admin*",
  "allowedRoles": ["administrator"]
}
  • You have full control over role names; there's no list to which your roles must adhere.
  • Individual users are associated to roles through invitations.

Important

When securing content, specify exact files when possible. If you have many files to secure, use wildcards after a shared prefix. For example: /profile* secures all possible routes that start with /profile, including /profile.

Fallback routes

Single Page Applications often rely on client-side routing. These client-side routing rules update the browser's window location without making requests back to the server. If you refresh the page, or navigate directly to URLs generated by client-side routing rules, a server-side fallback route is required to serve the appropriate HTML page (which is generally the index.html for your client-side app).

You can define a fallback rule by adding a navigationFallback section. The following example returns /index.html for all static file requests that do not match a deployed file.

{
  "navigationFallback": {
    "rewrite": "/index.html"
  }
}

You can control which requests return the fallback file by defining a filter. In the following example, requests for certain routes in the /images folder and all files in the /css folder are excluded from returning the fallback file.

{
  "navigationFallback": {
    "rewrite": "/index.html",
    "exclude": ["/images/*.{png,jpg,gif}", "/css/*"]
  }
}

The example file structure below, the following outcomes are possible with this rule.

├── images
│   ├── logo.png
│   ├── headshot.jpg
│   └── screenshot.gif
│
├── css
│   └── global.css
│
└── index.html
Requests to... returns... with the status...
/about/ The /index.html file 200
/images/logo.png The image file 200
/images/icon.svg The /index.html file - since the svg file extension is not listed in the /images/*.{png,jpg,gif} filter 200
/images/unknown.png File not found error 404
/css/unknown.css File not found error 404
/css/global.css The stylesheet file 200
Any other file outside the /images or /css folders The /index.html file 200

Important

If you are migrating from the deprecated routes.json file, do not include the legacy fallback route ("route": "/*") in the routing rules.

Global headers

The globalHeaders section provides a set of HTTP headers applied to each response, unless overridden by a route header rule, otherwise the union of both the headers from the route and the global headers is returned.

See the example configuration file for usage examples.

To remove a header, set the value to an empty string ("").

Some common use cases for global headers include:

  • Custom caching rules
  • Enforcing security policies
  • Encoding settings
  • Configuring cross-origin resource sharing (CORS)

The following example implements a custom CORS configuration.

{
  "globalHeaders": {
    "Access-Control-Allow-Origin": "https://example.com",
    "Access-Control-Allow-Methods": "POST, GET, OPTIONS"
  }
}

Response overrides

The responseOverrides section provides an opportunity to define a custom response when the server would otherwise return an error code. See the example configuration file for usage examples.

The following HTTP codes are available to override:

Status Code Meaning Possible cause
400 Bad request Invalid invitation link
401 Unauthorized Request to restricted pages while unauthenticated
403 Forbidden
  • User is logged in but doesn't have the roles required to view the page.
  • User is logged in but the runtime cannot get the user details from their identity claims.
  • There are too many users logged in to the site with custom roles, therefore the runtime can't log in the user.
404 Not found File not found

The following example configuration demonstrates how to override an error code.

{
  "responseOverrides": {
    "400": {
      "rewrite": "/invalid-invitation-error.html"
    },
    "401": {
      "statusCode": 302,
      "redirect": "/login"
    },
    "403": {
      "rewrite": "/custom-forbidden-page.html"
    },
    "404": {
      "rewrite": "/custom-404.html"
    }
  }
}

Networking

The networking section controls the network configuration of your static web app. To restrict access to your app, specify a list of allowed IP address blocks in allowedIpRanges.

Note

Networking configuration is only available in the Azure Static Web Apps Standard plan.

Define each IPv4 address block in Classless Inter-Domain Routing (CIDR) notation. To learn more about CIDR notation, see Classless Inter-Domain Routing. Each IPv4 address block can denote either a public or private address space. If you only want to allow access from a single IP Address, you can use the /32 CIDR block.

{
  "networking": {
    "allowedIpRanges": [
      "10.0.0.0/24",
      "100.0.0.0/32",
      "192.168.100.0/22"
    ]
  }
}

When one or more IP address blocks are specified, requests originating from IP addresses that do not match a value in allowedIpRanges are denied access.

In addition to IP address blocks, you can also specify service tags in the allowedIpRanges array to restrict traffic to certain Azure services.

"networking": {
  "allowedIpRanges": ["AzureFrontDoor.Backend"]
}

Authentication

Disable cache for authenticated paths

If you have enabled enterprise-grade edge, or set up manual integration with Azure Front Door, you may want to disable caching for your secured routes.

To disable Azure Front Door caching for secured routes, add "Cache-Control": "no-store" to the route header definition.

For example:

{
    "route": "/members",
    "allowedRoles": ["authenticated, members"],
    "headers": {
        "Cache-Control": "no-store"
    }
}

Forwarding gateway

The forwardingGateway section configures how a static web app is accessed from a forwarding gateway such as a CDN or Azure Front Door.

Note

Forwarding gateway configuration is only available in the Azure Static Web Apps Standard plan.

Allowed Forwarded Hosts

The allowedForwardedHosts list specifies which hostnames to accept in the X-Forwarded-Host header. If a matching domain is in the list, Static Web Apps uses the X-Forwarded-Host value when constructing redirect URLs, such as after a successful login.

For Static Web Apps to function correctly behind a forwarding gateway, the request from the gateway must include the correct hostname in the X-Forwarded-Host header and the same hostname must be listed in allowedForwardedHosts.

"forwardingGateway": {
  "allowedForwardedHosts": [
    "example.org",
    "www.example.org",
    "staging.example.org"
  ]
}

If the X-Forwarded-Host header doesn't match a value in the list, the requests still succeed, but the header isn't used in the response.

Required headers

Required headers are HTTP headers that must be sent with each request to your site. One use of required headers is to deny access to a site unless all of the required headers are present in each request.

For example, the following configuration shows how you can add a unique identifier for Azure Front Door that limits access to your site from a specific Azure Front Door instance. See the Configure Azure Front Door tutorial for full details.

"forwardingGateway": {
  "requiredHeaders": {
    "X-Azure-FDID" : "692a448c-2b5d-4e4d-9fcc-2bc4a6e2335f"
  }
}
  • Key/value pairs can be any set of arbitrary strings
  • Keys are case insensitive
  • Values are case-sensitive

Example configuration file

{
  "routes": [
    {
      "route": "/profile*",
      "allowedRoles": ["authenticated"]
    },
    {
      "route": "/admin/index.html",
      "allowedRoles": ["administrator"]
    },
    {
      "route": "/images/*",
      "headers": {
        "cache-control": "must-revalidate, max-age=15770000"
      }
    },
    {
      "route": "/api/*",
      "methods": ["GET"],
      "allowedRoles": ["registeredusers"]
    },
    {
      "route": "/api/*",
      "methods": ["PUT", "POST", "PATCH", "DELETE"],
      "allowedRoles": ["administrator"]
    },
    {
      "route": "/api/*",
      "allowedRoles": ["authenticated"]
    },
    {
      "route": "/customers/contoso*",
      "allowedRoles": ["administrator", "customers_contoso"]
    },
    {
      "route": "/login",
      "rewrite": "/.auth/login/github"
    },
    {
      "route": "/.auth/login/twitter",
      "statusCode": 404
    },
    {
      "route": "/logout",
      "redirect": "/.auth/logout"
    },
    {
      "route": "/calendar*",
      "rewrite": "/calendar.html"
    },
    {
      "route": "/specials",
      "redirect": "/deals",
      "statusCode": 301
    }
  ],
  "navigationFallback": {
    "rewrite": "index.html",
    "exclude": ["/images/*.{png,jpg,gif}", "/css/*"]
  },
  "responseOverrides": {
    "400": {
      "rewrite": "/invalid-invitation-error.html"
    },
    "401": {
      "redirect": "/login",
      "statusCode": 302
    },
    "403": {
      "rewrite": "/custom-forbidden-page.html"
    },
    "404": {
      "rewrite": "/404.html"
    }
  },
  "globalHeaders": {
    "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
  },
  "mimeTypes": {
    ".json": "text/json"
  }
}

Based on the above configuration, review the following scenarios.

Requests to... results in...
/profile Authenticated users are served the /profile/index.html file. Unauthenticated users are redirected to /login by the 401 response override rule.
/admin, /admin/, or /admin/index.html Authenticated users in the administrator role are served the /admin/index.html file. Authenticated users not in the administrator role are served a 403 error1. Unauthenticated users are redirected to /login
/images/logo.png Serves the image with a custom cache rule where the max age is a little over 182 days (15,770,000 seconds).
/api/admin GET requests from authenticated users in the registeredusers role are sent to the API. Authenticated users not in the registeredusers role and unauthenticated users are served a 401 error.

POST, PUT, PATCH, and DELETE requests from authenticated users in the administrator role are sent to the API. Authenticated users not in the administrator role and unauthenticated users are served a 401 error.
/customers/contoso Authenticated users who belong to either the administrator or customers_contoso roles are served the /customers/contoso/index.html file. Authenticated users not in the administrator or customers_contoso roles are served a 403 error1. Unauthenticated users are redirected to /login.
/login Unauthenticated users are challenged to authenticate with GitHub.
/.auth/login/twitter As authorization with Twitter is disabled by the route rule, 404 error is returned, which falls back to serving /index.html with a 200 status code.
/logout Users are logged out of any authentication provider.
/calendar/2021/01 The browser is served the /calendar.html file.
/specials The browser is permanently redirected to /deals.
/data.json The file served with the text/json MIME type.
/about, or any folder that matches client side routing patterns The /index.html file is served with a 200 status code.
An non-existent file in the /images/ folder A 404 error.

1 You can provide a custom error page by using a response override rule.

Restrictions

The following restrictions exist for the staticwebapp.config.json file.

  • Max file size is 20 KB
  • Max of 50 distinct roles

See the Quotas article for general restrictions and limitations.

Next steps