Building a maintenance page that brings your site back to life when it's ready
Tags: javascript, webdev, frontend
Published: 2024-03-21 17:17:32

Recently, my team at work have been involved in the launch of online [Co-op Member Prices](https://shop.coop.co.uk/member-prices), a set of lower prices for Co-op members on our groceries delivery website.

Part of the launch involved the production site being taken offline for a short period in the early morning while various final checks were made that couldn't have been made in non-production environments.

While those checks were happening I was responsible for turning on our maintenance page – a static HTML page hosted on a blob storage account, served by a CDN that we temporarily pointed our site to. The page stayed up while the main site was being tested by colleagues who were able to bypass the maintenance page based on their IP addresses.

Every moment that customers are seeing our maintenance page instead of the real website costs us potential orders and revenue. We want customers to see the page for as little time as possible. We can't change how long it takes to do the maintenance work itself, but we can do two things to make the page reload by itself (or almost by itself…) so that the user is more likely to see the real site once it is available – even if they don't manually reload the page.

These assume that your maintenance page is on some form of static hosting and is cheap to serve to each user multiple times, and that your maintenance page is lightweight and loads quickly. Don't do this if your maintenance page needs to make a database connection or has a dynamic or server-side element to it that may be overwhelmed by repeated requests.

## Automatic reloading with an 'exponential backoff'

I added a small JavaScript snippet that automatically reloads the page at increasing intervals, for example every 1, 2, 4, 8, 16, 32 and 64 minutes, and so on. We store the number of automatic reloads in [`sessionstorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) so it is automatically cleared when the user closes the tab or browser, and we stop after 8 attempts. This is called an 'exponential backoff' – to avoid swamping the server with requests we progressively increase the delay between retries, up to a maximum value or number of attempts.

Here's the code:

```html
<script>
  function reloadPage() {
    let attempt = parseInt(sessionStorage.getItem('reloadAttempt')) || 0;

    if (attempt < 8) {
      // Exponential backoff starting at 1 minute.
      const backoff = Math.pow(2, attempt) * 1 * 60 * 1000;

      setTimeout(() => {
        window.location.reload();
      }, backoff);

      attempt++;
      sessionStorage.setItem('reloadAttempt', attempt.toString());
    } else {
      // Cleanup and prevent further attempts, unless user manually 
      // reloads and the counter restarts.
      sessionStorage.removeItem('reloadAttempt');
    }
  }

  reloadPage();
</script>
```

If you didn't care about the exponential backoff and the limit then you could do something similar with no JavaScript. This example would refresh every 5 minutes (300 seconds):

```html
<meta http-equiv="refresh" content="300" >
```

### A note on accessibility

An automatic reload [violates three WCAG success criteria](https://www.w3.org/WAI/WCAG21/Techniques/failures/F41.html). On normal pages it's a bad idea as users can lose their place on the page, but with a maintenance page only having perhaps one or two sentences of text I considered this to be less of an issue. If this wasn't acceptable for you then you could remove the automatic reload and instead make the refresh behaviour controllable by the user, for example adding a call-to-action button to the page that reloads the page on click. Another option would be to add some UI that allows the automatic reload behaviour to be stopped or paused – this is what I plan to do in the next iteration of our maintenance page.

## Automatic reloading on the `visibilitychange` event

The [`visibilitychange`](https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event) event fires when a user brings a previously inactive tab back into view, or reopens their mobile browser to your page. It's a useful event to use for things like refetching data in the background, but you'd never usually use it to refresh a page as the user might lose their scroll progress on the page, lose information they had entered into a form, or be annoyed by the flicker, layout shift, or other UI changes of a slow page loading.

Luckily, a maintenance page usually has no forms, not much content to scroll, and loads very quickly as it is served from a static file host or CDN. This means we can get away with reloading the page whenever it becomes visible. This means that a user who revisits the tab after the maintenance page has been lifted will see the page spring back to life without them needing to bother to refresh the page manually. If the maintenance page hasn't been lifted then it's no problem, the page refresh will happen so quickly that they are unlikely to notice the difference.

Here's how I did it:

```html
<script>
  function handleVisibilityChange() {
    if (document.visibilityState === 'visible') {
      // Restart the backoff counter so that multiple visibilitychange 
      // events firing in quick succession don't burn through the attempts 
      // quota. 
      sessionStorage.removeItem('reloadAttempt');
      window.location.reload();
    }
  }

  document.addEventListener('visibilitychange', handleVisibilityChange);
</script>
```

## An 'under construction' emoji favicon

At the last minute I realised our maintenance page didn't have a favicon. Rather than use a branded one, I used a 'under construction' emoji using this neat trick:

```html
<link
  rel="icon"
  href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🚧</text></svg>"
/>
```

![Screenshot of a tab in Chrome using a 🚧 emoji as a favicon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q6xn3q9pv3lwp1wozbba.png)


An emoji favicon is nicely self contained, it saved me adding a `favicon.ico` or `favicon.svg` to our maintenance page bucket, and it means that if the maintenance page auto-refreshed while the user is looking at a different tab they may still notice the site coming back online as the inactive tab's favicon in their browser UI will update when the maintenance page is lifted and the normal favicon appears. It's also a cute nod to the [internet of yesteryear](http://textfiles.com/underconstruction/).