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

Route rules allow you to define the pattern of URLs that allow access to your application to the web. 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 executed in the order as they appear in the routes array.
  • Rule evaluation stops at the first match - routing rules aren't chained together.
  • You have full control over custom role names.

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.

The default file for static content is the index.html file.

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.

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 under the admin path.
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).
allowedRoles No anonymous Defines a list 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 unauthenticated 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.
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.
statusCode No 200, 301, or 302 for redirects The HTTP status code of the response.
methods No All methods List of request methods which match a route. Available methods include: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, and PATCH.

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

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

redirect, headers, statusCode (modifies response)

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.

Wildcards

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.

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
  • Mapping different HTTP methods to an entire path pattern
  • Enforcing authentication and authorization rules
  • Implement specialized caching rules

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

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 login 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.

Authentication

Example configuration file

{
  "routes": [
    {
      "route": "/profile",
      "allowedRoles": ["authenticated"]
    },
    {
      "route": "/admin/*",
      "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.
/admin/ 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.
/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 100 KB
  • Max of 50 distinct roles

See the Quotas article for general restrictions and limitations.

Next steps