Maintenance mode lets a platform administrator temporarily take the website pages offline from Admin Dashboard → Settings. Visitors are redirected to a localized maintenance screen after the shared status cache refreshes; no environment-variable change, rebuild, or redeploy is required.
The flag is disabled by default and stored in app_settings. New installations use the current full SQL schema and provisioned application runtime credentials.
API routes, signed payment-provider webhooks, background jobs, health probes, static assets, and the offline fallback remain available by design. This keeps billing events, scheduled work, monitoring, and administrator recovery operational. Stop those systems separately when maintenance requires a complete backend outage.
Enable or disable maintenance mode
- Sign in as a platform administrator.
- Open
/admin-dashboard/settingsin the current locale. - Find Maintenance mode and change the switch.
- Review the confirmation dialog and confirm the change.
Changing this setting is a sensitive administrator action. The API requires a recent authenticated session; if the session is too old, sign in again and repeat the change. Enabling and disabling both produce an admin_logs audit row.
The Settings control displays the current 60-second cache window. The switch and audit log update as soon as the database write succeeds, but visitor routing is not immediate: enabling or disabling maintenance can continue using the previous status until the shared server cache refreshes.
The public maintenance page remains directly available at /[locale]/maintenance while the flag is off, so operators can preview every configured locale before an incident.
Request behavior
| Request | Maintenance behavior |
|---|---|
| Localized website page | Temporary 307 redirect to /[locale]/maintenance |
/docs and documentation pages | Redirect to the maintenance page using the locale cookie or negotiated browser language |
/[locale]/maintenance | Always renders; marked noindex, nofollow |
/[locale]/admin-dashboard/** | Remains reachable and keeps its normal authentication/administrator checks |
| Login, callback, and password-recovery pages | Remain reachable so an administrator cannot be locked out |
| Non-GET request to a page route | 503 JSON response with code MAINTENANCE_MODE and Retry-After: 300 |
/api/**, webhooks, jobs, and /api/health | Continue through their existing security and availability controls |
/_next/**, public assets, /offline | Remain available so the maintenance page and recovery surfaces can render |
Requests without a locale prefix first follow the normal permanent locale redirect, then enter maintenance mode. The maintenance response is no-store, carries Retry-After: 300, and is excluded from search indexing.
Security and audit model
The browser never writes app_settings directly. The control sends a strict boolean to PATCH /api/admin/settings through csrfFetch().
The request passes all of these gates before the database changes:
apiSecurity.admin()authentication and platform-admin authorization- the strict administrator rate limit
- Double Submit Cookie CSRF validation
- strict Zod validation for
{ key: 'maintenance_mode', value: boolean } - the configured recent-authentication requirement
The shared Prisma repository uses the verified canonical application identity. Its private write function rechecks active administrator authority under database locks, then updates the flag and inserts the admin_logs row in one transaction. The setting cannot change without its audit record. A malformed write result rolls back both changes; an unavailable canonical identity returns 503 before writing. The runtime cannot assume the function owner's role or write directly to either table.
Public page enforcement receives only a boolean from a separate private read function through Prisma. Its read-only transaction clears any prior actor context. The function's owner can read only the maintenance setting's key and value; other settings and audit records stay private. The old public RPC wrappers and provider-specific adapters are removed.
Availability and performance
The proxy reads the scalar flag through a shared Next.js server cache. getMaintenanceMode() uses the common Prisma repository inside a module-scope unstable_cache() reader, so requests reuse the result instead of querying the database for every eligible page. The cache key and 60-second revalidation window live in config/maintenance.ts. Database work uses the shared bounded pool and SQL deadlines.
The admin mutation deliberately does not invalidate the cached value. This reduces database traffic on the global request path at the cost of delayed activation and restoration, which the Settings UI discloses. On routes that also resolve a user, the cached maintenance lookup and session verification still run concurrently.
See Caching & Performance for the shared data-cache inventory and cache-safety rules.
If the lookup fails because the database or network is unavailable, the proxy deliberately fails open and logs the maintenance/status_lookup_failed event. Invalid database results also propagate as failures instead of becoming cached false values. A database incident must not silently turn into an unrelated global maintenance outage. Use infrastructure-level routing or your hosting provider's maintenance controls when the application database itself is unavailable and traffic must fail closed.
Deployment checklist
- Initialize a fresh database with the current full SQL schema and application runtime preflight.
- Deploy the application code.
- Open each
/[locale]/maintenancepage directly and review the translations. - Enable the flag in a staging environment and verify a public page and
/docsredirect. - Verify the admin dashboard, login/callback flow,
/api/health, webhooks, and jobs remain reachable. - Disable the flag, wait for the displayed cache window, and confirm public pages are restored.
No maintenance-specific environment variable is required. Fresh installations seed the flag as false.