Skip to content

Instantly share code, notes, and snippets.

@robinebers
Created August 13, 2025 10:46
Show Gist options
  • Select an option

  • Save robinebers/4af41a086ed5791c5aec2c28110690b7 to your computer and use it in GitHub Desktop.

Select an option

Save robinebers/4af41a086ed5791c5aec2c28110690b7 to your computer and use it in GitHub Desktop.
// middleware.ts
import { NextResponse, NextRequest } from "next/server";
const BYPASS_COOKIE = "maint_bypass";
const BYPASS_SECRET = "123456"; // optional, let's you bypass it
export function middleware(req: NextRequest) {
const { pathname, searchParams } = req.nextUrl;
// Allow critical paths & assets (adjust to your app)
const allow =
pathname.startsWith("/_next") ||
pathname.startsWith("/favicon") ||
pathname.startsWith("/fonts") ||
pathname.startsWith("/images") ||
pathname.startsWith("/api/health") || // keep uptime monitors alive
pathname.startsWith("/api/webhooks"); // don't break webhooks
if (allow) return NextResponse.next();
// Optional: grant bypass if URL contains ?bypass=SECRET
const urlSecret = searchParams.get("bypass");
if (urlSecret && BYPASS_SECRET && urlSecret === BYPASS_SECRET) {
const res = NextResponse.next();
res.cookies.set(BYPASS_COOKIE, "1", { httpOnly: true, path: "/", maxAge: 60 * 60 });
return res;
}
// If maintenance OFF or you have a bypass cookie, continue
const hasBypass = req.cookies.get(BYPASS_COOKIE)?.value === "1";
if (hasBypass) return NextResponse.next();
// Serve a minimal 503 page (inline for simplicity)
const html = `<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex">
<title>Down for Maintenance</title>
<style>
body{margin:0;display:grid;place-items:center;height:100vh;font:16px system-ui;background:#0b1220;color:#e7e9ee}
.card{max-width:560px;padding:28px;border-radius:16px;background:#111827;border:1px solid #1f2937;box-shadow:0 10px 30px rgba(0,0,0,.35)}
h1{margin:0 0 10px;font-size:24px}
p{opacity:.9;line-height:1.6}
</style></head>
<body><div class="card">
<h1>We’ll be right back</h1>
<p>We’re doing a quick update. Please check back soon.</p>
</div></body></html>`;
return new NextResponse(html, {
status: 503,
headers: {
"Content-Type": "text/html; charset=utf-8",
"Retry-After": "3600"
}
});
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment