Skip to content
thinkdjPublic

About

Temporary maintenance page for your website | For "503 Service Unavailable"

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OopsKit

Maintenance pages for your website

One dependency-free HTML file that tells visitors what's happening, when you'll be back, and sends them back automatically once you are. Its centerpiece is a clock set to your ETA, like the sign on a shop door. Light and dark, with zero external requests, so nothing on the page can break while your stack is down.

Page preview

Features

  • One file, no dependencies. No fonts, CDNs, or frameworks. Works when everything else is off.
  • Configure from the URL. Use the hosted copy with ?site=…&eta=…, no fork needed.
  • A clock set to your ETA. The wait is shaded on the dial and spelled out below it, in the visitor's own timezone. If you overrun, it turns orange and says so plainly instead of showing a negative timer.
  • Auto-return. Checks your site every 30 seconds and sends visitors back to the page they were on the moment it's up. The clock pings on every check, and its hands swing into a check mark when you're back.
  • Status page and contact links, shown only when set.
  • Light and dark follow the OS, with a toggle that remembers the choice. No flash on load.
  • Accessible. Screen-reader friendly status updates, keyboard focus kept, reduced motion respected, readable without JavaScript.

Quick start (hosted copy)

Point visitors at the GitHub Pages copy and pass your details as query parameters:

https://thinkdj.github.io/oopsKit/?site=example.com&eta=2026-10-24T03:00:00Z&status=https://status.example.com
Parameter Example What it does
site example.com Shown above the headline. Turns on auto-return.
from /pricing?plan=pro Path to send the visitor back to. Defaults to /.
eta 2026-10-24T03:00:00Z When you expect to be back. ISO 8601 with Z or an offset, or Unix seconds.
status https://status.example.com Adds a "Status page" link. https only.
contact hello@example.com Adds a "Contact us" link. Email or https URL.
title Upgrading our database. Replaces the headline. Plain text, 120 characters.
msg Orders placed before 9am are safe. Replaces the body text. Plain text, 280 characters.
probe /img/logo.png Image used to check if you're back. Defaults to /favicon.ico.
retry 0 Turns auto-return off. "Try again" then simply opens your site.
theme dark or light Forces the initial theme.
demo true Sets the ETA to 2.5 hours from page load, overriding eta. For demos.

Every parameter is optional and validated. Invalid values are ignored with a warning in the browser console, so check there if something doesn't show up.

Test it in ten seconds

While your site is up, open https://thinkdj.github.io/oopsKit/?site=yoursite.com. Within a second it should say Back online. That proves the page can see your site. If it keeps saying it's checking, see Troubleshooting.

Usage with Cloudflare

Redirect your whole site to the maintenance page while you work, with a 302 so search engines don't index the maintenance URL in place of your pages.

Redirect Rules (recommended)

  1. Cloudflare dashboard, your zone, Rules, Redirect Rules, Create rule.

  2. When incoming requests match: All incoming requests. Exclude your own IP here if your team needs to see the real site.

  3. URL redirect: Dynamic, with this expression:

    concat("https://thinkdj.github.io/oopsKit/?site=", http.host, "&from=", http.request.uri.path, "&eta=2026-10-24T03:00:00Z")
    
  4. Status code 302. Save. Disable the rule when you're done, and visitors come back on their own.

Page Rules (legacy)

  1. Rules, Page Rules, Create Page Rule.

  2. URL: *yoursite.com/*

  3. Setting: Forwarding URL, 302, destination:

    https://thinkdj.github.io/oopsKit/?site=$1yoursite.com&from=/$2
    

Cloudflare Page Rule

Self-hosting

Copy index.html to your server and set site in the config block at the top. Setting it there pins it: ?site= in the URL is then ignored, so your copy can never be used to redirect people to someone else's site.

Serve it with HTTP 503 and a Retry-After header, never a 200, so crawlers come back later instead of indexing the maintenance page. When the page is served at your real URLs, visitors return to the exact URL they asked for.

nginx

server {
    # ...
    root /var/www/oopskit;
    error_page 503 /index.html;
    add_header Retry-After 3600 always;

    location = /index.html { internal; }
    location / { return 503; }
}

Apache (.htaccess)

ErrorDocument 503 /index.html
Header always set Retry-After "3600"
RewriteEngine On
RewriteCond %{ENV:REDIRECT_STATUS} !=503
RewriteRule ^ - [R=503,L]

Both return 503 for everything, including /favicon.ico. That's what lets the page tell you're still down. To let your team through, add an allow for your office IP in nginx or a RewriteCond %{REMOTE_ADDR} exception in Apache.

How auto-return decides

  • The page loads your probe image every 30 seconds. While you're down, the probe hits your 503 or your redirect and fails. Once you're back, it loads.
  • Visitors are only redirected after the page has seen your site down at least once. If the probe loads on the very first try, the page shows a "Go to yoursite.com" button instead. This prevents redirect loops when the probe isn't covered by your redirect.
  • Hidden tabs don't check. Switching back to the tab, reconnecting to the internet, or the ETA passing all trigger an immediate check.
  • If the visitor is offline, the page says so instead of reporting your site as down.

Troubleshooting

  • It never sends visitors back. The probe image can't load from another site. Usual causes are a missing /favicon.ico, hotlink protection, or a Cross-Origin-Resource-Policy: same-origin header. Point probe at an image without those.
  • It says "Back online" while you're still down. Your redirect or 503 doesn't cover the probe path, for example a rule that skips static files. Pick a probe path the rule covers.
  • The clock is off by hours. The eta has no timezone, so each visitor reads it in their own. Add Z or an offset like +05:30.
  • The countdown doesn't show and the hands just turn. The eta is invalid, or more than 24 hours in the past. A stale ETA is hidden rather than shown as days overdue.

Customization

Everything lives in index.html.

  • Try settings live: open index.html locally (file:// or localhost) and click the sliders button at the bottom right. Every option is there and applies as you type, with ETA presets and a preview of the back-online state. Copy link gives the hosted-copy URL, Copy config the block for your file. The panel only exists on local copies. Visitors never get it.
  • Text, ETA, links, probe: edit the window.OOPS_CONFIG block at the top of <head>. URL parameters override these, except a pinned site.
  • Look: the design system is a block of tokens in :root. Each color is defined once with light-dark(light, dark). See DESIGN.md for the tokens and the reasoning.

Development

node test.mjs

Tests the parsing, validation, countdown, and clock logic in the page's core script. No dependencies. The GitHub Pages deploy runs it first and won't publish if it fails.

File structure

├── index.html                       # The page (styles and script embedded)
├── test.mjs                         # Tests for the page's core logic
├── DESIGN.md                        # Design system: principles, tokens, components
├── assets/                          # README images
└── .github/workflows/               # Tests, then deploys to GitHub Pages on push

Browser support

Evergreen browsers. Dark mode needs CSS light-dark(), from 2024 on. Older browsers get the light theme without the toggle. Without JavaScript, visitors still see the full message.

License

Free to use for your projects. Attribution is appreciated but not required.

🕊️

About

Temporary maintenance page for your website | For "503 Service Unavailable"

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Used by

Contributors

Languages