Sometimes you want to “turn off” a site for a while: you’re reworking content, fixing a broken deployment, or you just don’t want the public to hit it.

With Azure Static Web Apps (SWA) this can be trickier than expected, especially on the Free plan.

Why this is needed on the Free plan

Two common requirements are:

  1. Restrict access to your own IP (an allow list)
  2. Temporarily disable the site entirely

On the Free plan, SWA does not support the “allowed IP range restrictions” feature (it’s available on Standard). That means a configuration like networking.allowedIpRanges won’t be enforced on Free.

So if you need a quick “shutdown switch” without upgrading plans, you can use a simple, reversible trick: send all traffic to a maintenance page.

The approach: redirect everything to a maintenance page (temporary)

1) Add a maintenance page

Create a small static HTML file in the deployed site output (the same folder that contains your index.html).

Example: maintenance.html

  • Keep it self-contained (inline CSS)
  • Avoid external assets (no fonts/CDNs)
  • Add noindex so it doesn’t get indexed
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Maintenance</title>
    <meta name="robots" content="noindex, nofollow" />
    <style>
      :root { color-scheme: light dark; }
      body {
        margin: 0;
        font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
        display: grid;
        min-height: 100vh;
        place-items: center;
        padding: 24px;
      }
      .card {
        max-width: 720px;
        width: 100%;
        border: 1px solid rgba(127,127,127,.35);
        border-radius: 16px;
        padding: 20px 22px;
        background: rgba(127,127,127,.06);
      }
      h1 { margin: 0 0 8px; font-size: 20px; }
      p { margin: 0; line-height: 1.45; opacity: .9; }
      code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
    </style>
  </head>
  <body>
    <main class="card">
      <h1>Site temporarily unavailable</h1>
      <p>This site is currently disabled for maintenance.</p>
      <p style="margin-top:10px">If you expected to see content here, try again later.</p>
    </main>
  </body>
</html>

2) Rewrite all routes to that page

In your staticwebapp.config.json, add a catch-all route that redirects every request to /maintenance.html.

This effectively disables the app because:

  • Deep links don’t resolve to real content
  • Your normal HTML/CSS/JS bundle won’t be served
  • Users always see the maintenance message

Here’s the recommended pattern we ended up with:

{
  "routes": [
    {
      "route": "/maintenance.html",
      "headers": {
        "Cache-Control": "no-store",
        "Retry-After": "3600",
        "X-Robots-Tag": "noindex, nofollow, noarchive"
      }
    },
    {
      "route": "/*",
      "redirect": "/maintenance.html",
      "statusCode": 302,
      "headers": {
        "Cache-Control": "no-store",
        "Retry-After": "3600",
        "X-Robots-Tag": "noindex, nofollow, noarchive"
      }
    }
  ]
}

Notes:

  • The explicit /maintenance.html route prevents a redirect loop.
  • 302 is a temporary redirect, which is a better hint to crawlers than a permanent redirect.
  • Retry-After nudges crawlers to come back later (here: 1 hour).
  • X-Robots-Tag is an extra, server-side “don’t index this” hint (in addition to the HTML <meta name="robots">).

Why Cache-Control: no-store?

  • CDNs and browsers love caching
  • Without this, you can easily get “half-updated” behavior during rollouts

Gotchas we ran into

Don’t use a wildcard 404 + response override for maintenance

A tempting pattern is:

  • Force all requests to 404
  • Override 404 to rewrite to the maintenance page

This can lead to odd behavior in browsers (for example: HTML appears, but CSS/JS gets blocked or behaves inconsistently) because the browser sees the response as an error path.

A direct rewrite that returns a normal success response is more reliable.

Redirect vs rewrite (and why this still isn’t a perfect “shutdown”)

If you only care about human visitors, a catch-all rewrite to maintenance.html is often fine.

If you also care about crawlers and want to signal “temporary”, a 302 redirect plus Retry-After is a reasonable compromise.

However, the best-practice maintenance response on the web is usually an HTTP 503 Service Unavailable with a Retry-After header.

SWA route rules don’t give you a clean way to return a real 503 for all requests while serving a custom HTML page from configuration alone. If you truly need proper 503 behavior, put a proxy/CDN in front (Cloudflare, Azure Front Door, etc.) and have that layer return a 503.

Route ordering matters

SWA stops on the first matching route.

If you do keep specific routes (like /go/*) in your config, make sure they appear before the /* wildcard, otherwise SWA will complain the route is unreachable.

How to re-enable the site

To bring the site back:

  1. Remove (or comment out) the /* maintenance redirect route
  2. Redeploy

If you want a safer workflow, keep two configs:

  • staticwebapp.config.json (normal)
  • staticwebapp.maintenance.json (maintenance)

…and swap them in your build pipeline depending on whether maintenance mode is on.

If you actually need IP restriction

If you truly need “only my IP can access this”, your options are:

  • Upgrade the SWA to Standard and use networking.allowedIpRanges (supports /32 for a single IP)
  • Put something in front of SWA that can enforce an allow list:
    • Cloudflare (Access / WAF)
    • Azure Front Door + WAF IP restriction rules

TL;DR

On a Free Azure Static Web App you can’t rely on IP allow-listing, and there isn’t a one-click shutdown. A practical workaround is to rewrite all routes to a self-contained maintenance.html using staticwebapp.config.json.

Happy Coding!