Created
August 13, 2025 10:46
-
-
Save robinebers/4af41a086ed5791c5aec2c28110690b7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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