Routes in Azure Static Web Apps Preview

Routing in Azure Static Web Apps defines back-end routing rules and authorization behavior for both static content and APIs1. The rules are defined as an array of rules in the routes.json file.

  • The routes.json file must exist at the root of app's build artifact folder.
  • Rules are executed in the order as they appear in the routes array.
  • Rule evaluation stops at the first match. Routing rules are not chained together.
  • Roles are defined in the routes.json file and users are associated to roles via invitations.
  • You have full control over role names.

The topic of routing significantly overlaps with authentication and authorization concepts. Make sure to read the authentication and authorization guide along with this article.

See the example route file for details.

Location

The routes.json file must exist at the root of app's build artifact folder. If your web app includes a build step that copies built files from a specific folder to your build artifact folder, then the routes.json file needs to exist in that specific folder.

The following table lists the appropriate location to put your routes.json file for a number of front-end frameworks and libraries.

Framework / library Location
Angular assets
React public
Svelte public
Vue public
Blazor wwwroot

The above table is only representative of a few frameworks and libraries compatible with Azure Static Web Apps. Refer to Configure front-end frameworks and libraries for more information.

Defining routes

Routes are defined in the routes.json file as an array of route rules on the routes property. Each rule is composed of a route pattern, along with one or more of the optional rule properties. See the example route 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.
  • A route's default file is index.html.
serve No n/a Defines the file or path returned from the request. The file path and name can be different from the requested path. If a serve value is not defined, then the requested path is used. Querystring parameters are not supported; serve values must point to actual files.
allowedRoles No anonymous An array of role names.
  • 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.
statusCode No 200 The HTTP status code response for the request.

Securing routes with roles

Routes are secured by adding one or more role names into a rule's allowedRoles array. See the example route 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. 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 an administrator role in the allowedRoles array.

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

Wildcards

Wildcard rules match all requests under a given route pattern. If you define a serve value in your rule, the named file or path is served as the response.

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

{
  "route": "/calendar/*",
  "serve": "/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 also secure routes with wildcards. In the following example, any file requested under the admin path requires an authenticated user who is a member of the administrator role.

{
  "route": "/admin/*",
  "allowedRoles": ["administrator"]
}

Note

Requests for files referenced by a served file are only evaluated for authentication checks. For instance, requests to a CSS file under a wildcard path serve the CSS file, and not what is defined as the serve file. The CSS file is served as long as the user meets the required authentication requirements.

Fallback routes

Single Page Applications, whether they are using front-end JavaScript frameworks or libraries or WebAssembly platforms like Blazor, often rely on client-side routing for web app navigation. 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 locations generated by client-side routing rules, a server-side fallback route is required to serve the appropriate HTML page.

A common fallback route is shown in the following example:

{
  "routes": [
    {
      "route": "/*",
      "serve": "/index.html",
      "statusCode": 200
    }
  ]
}

The fallback route must be listed last in your routing rules, as it catches all requests not caught by previously defined rules.

Redirects

You can use 301 and 302 HTTP status codes to redirect requests from one route to another.

For instance, the following rule creates a 301 redirect from old-page.html to new-page.html.

{
  "route": "/old-page.html",
  "serve": "/new-page.html",
  "statusCode": 301
}

Redirects also work with paths that don't define distinct files.

{
  "route": "/about-us",
  "serve": "/about",
  "statusCode": 301
}

Custom error pages

Users may encounter a number of different situations that may result in an error. Using the platformErrorOverrides array, you can provide a custom experience in response to these errors. Refer to the example route file for placement of the array in the routes.json file.

Note

Once a request makes it to the platform overrides level, route rules are not run again.

The following table lists the available platform error overrides:

Error type HTTP status code Description
NotFound 404 A page is not found on the server.
Unauthenticated 401 The user is not logged in with an authentication provider.
Unauthorized_InsufficientUserInformation 401 The user's account on the authentication provider is not configured to expose required data. This error may happen in situations like when the app asks the authentication provider for the user's email address, but the user chose to restrict access to the email address.
Unauthorized_InvalidInvitationLink 401 An invitation has either expired, or the user followed an invitation link generated for another recipient.
Unauthorized_MissingRoles 401 The user is not a member of a required role.
Unauthorized_TooManyUsers 401 The site has reached the maximum number of users, and the server is limiting further additions. This error is exposed to the client because there's no limit to the number of invitations you can generate, and some users may never accept their invitation.
Unauthorized_Unknown 401 There is an unknown problem while trying to authenticate the user. One cause for this error may be that the user isn't recognized because they didn't grant consent to the application.

Custom mime types

The mimeTypes object, listed at the same level as the routes array, allows you to associate MIME types with file extensions.

{
    "routes": [],
    "mimeTypes": {
        "custom": "text/html"
    }
}

In the example above, all files with the .custom extension are served with the text/html MIME type.

The following considerations are important as you work with MIME types:

  • Keys cannot be null or empty, or more than 50 characters
  • Values cannot be null or empty, or more than 1000 characters

Note

Static Web Apps understands Blazor applications and the expected MIME types for the WASM and DLL files, you do not need to add mappings for those.

Default headers

The defaultHeaders object, listed at the same level as the routes array, allows you to add, modify, or remove response headers.

Providing a value for a header either adds or modifies the header. Providing an empty value, removes the header from being served to the client.

{
    "routes": [],
    "defaultHeaders": {
      "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'",
      "cache-control": "must-revalidate, max-age=6000",
      "x-dns-prefetch-control": ""
    }
}

In the above example, a new content-security-policy header is added, the cache-control modifies the server default value, and the x-dns-prefectch-control header is removed.

The following considerations are important as you work with headers:

  • Keys cannot be null or empty.
  • Null or empty values remove a header from processing.
  • Keys or values cannot exceed 8,000 characters.
  • Defined headers are served with all requests.
  • Headers defined in routes.json only apply to static content. You can customize response headers of a API endpoint in the function's code.

Example route file

The following example shows how to build route rules for static content and APIs in a routes.json file. Some routes use the /.auth system folder that access authentication-related endpoints.

{
  "routes": [
    {
      "route": "/profile",
      "allowedRoles": ["authenticated"]
    },
    {
      "route": "/admin/*",
      "allowedRoles": ["administrator"]
    },
    {
      "route": "/api/admin",
      "allowedRoles": ["administrator"]
    },
    {
      "route": "/customers/contoso",
      "allowedRoles": ["administrator", "customers_contoso"]
    },
    {
      "route": "/login",
      "serve": "/.auth/login/github"
    },
    {
      "route": "/.auth/login/twitter",
      "statusCode": "404"
    },
    {
      "route": "/logout",
      "serve": "/.auth/logout"
    },
    {
      "route": "/calendar/*",
      "serve": "/calendar.html"
    },
    {
      "route": "/specials",
      "serve": "/deals",
      "statusCode": 301
    }
  ],
  "platformErrorOverrides": [
    {
      "errorType": "NotFound",
      "serve": "/custom-404.html"
    },
    {
      "errorType": "Unauthenticated",
      "statusCode": "302",
      "serve": "/login"
    }
  ],
  "defaultHeaders": {
    "content-security-policy": "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'"
  },
  "mimeTypes": {
      "custom": "text/html"
  }
}

The following examples describe what happens when a request matches a rule.

Requests to... Result in...
/profile Authenticated users are served the /profile/index.html file. Unauthenticated users redirected to /login.
/admin/reports Authenticated users in the administrators role are served the /admin/reports/index.html file. Authenticated users not in the administrators role are served a 401 error2. Unauthenticated users redirected to /login.
/api/admin Requests from authenticated users in the administrators role are sent to the API. Authenticated users not in the administrators role and unauthenticated users are served a 401 error.
/customers/contoso Authenticated users who belong to either the administrators or customers_contoso roles are served the /customers/contoso/index.html file2. Authenticated users not in the administrators or customers_contoso roles are served a 401 error. Unauthenticated users redirected to /login.
/login Unauthenticated users are challenged to authenticate with GitHub.
/.auth/login/twitter Authorization with Twitter is disabled. The server responds with a 404 error.
/logout Users are logged out of any authentication provider.
/calendar/2020/01 The browser is served the /calendar.html file.
/specials The browser is redirected to /deals.
/unknown-folder The /custom-404.html file is served.
Files with the .custom extension Are served with the text/html MIME type

All responses include the content-security-policy headers with a value of default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'.

1 Route rules for API functions only support redirects and securing routes with roles.

2 You can provide a custom error page by defining a Unauthorized_MissingRoles rule in the platformErrorOverrides array.

Restrictions

  • The routes.json file cannot be more than 100 KB
  • The routes.json file supports a maximum of 50 distinct roles

See the Quotas article for general restrictions and limitations.

Next steps