Password Protect Static Website in NextJS
Jasser Mark Arioste
Hello, hustlers! In this tutorial, you'll learn how to add password protection to a static website. It will be useful if you have a website under development or if you have preview deployments hosted on Vercel.
How to Add Password Protection #
Here's how we can implement password protection on a NextJS website.
- Define an environment variable
PASSWORD_PROTECT
. We'll use this to check if the user submitted the correct password - Use NextJS middleware to check if the
PASSWORD_PROTECT
environment variable is present. If it's present, we check a cookie if the user has already logged in before. If not, we redirect the user to the/password-protection
page. - Create a
/password-protection
page. Once the user submits the form. we'll set the cookie.
We'll cover these steps in detail in the following sections.
Project Setup #
I created a GitHub repo to kickstart our tutorial. If you have an existing project, you can skip this step. If you're starting from scratch, I recommend using this repo since it already includes TailwindCSS for styling.
To create a NextJS project using the GitHub template, just run this command:
npx create-next-app -e https://github.com/jmarioste/nextjs-password-protect-tutorial nextjs-example
1
To start the local server, run this command:
cd nextjs-example && yarn dev
1
Go to localhost:3000 and you should see the following:
Step 1 - Define PASSWORD_PROTECT
Variable
#
Next, create a .env
file at the root directory of your project and define the PASSWORD_PROTECT
environment variable:
PASSWORD_PROTECT="password"
1
We'll use this variable to check if there's a password protection set for a certain environment or deployment.
Step 2 - Creating Password Protect Page #
Next, let's create the /password-protect
page. We'll use daisyUI to add some styling. Create a file pages/password-protect.tsx
:
import React from "react"; import Image from "next/image"; const PasswordProtectPage = () => { return ( <div className="container"> <div className="grid place-content-center min-h-screen"> <div className="flex flex-col items-center gap-4"> <h1 className="text-2xl">This Page is Under Development... </h1> <Image src="/under-development.svg" alt="under development" width={250} height={250} /> <p>Enter Password:</p> <form action="/api/password-protect" method="post"> <div className="form-control"> <div className="input-group"> <input type="text" name="password" className="input input-bordered" /> <button className="btn">Login</button> </div> </div> </form> </div> </div> </div> ); }; export default PasswordProtectPage;
123456789101112131415161718192021222324252627282930313233
Explanation:
Line 17: This is a very simple form that submits a POST request to the /api/password-protect
endpoint which we'll implement in the next step.
If you go to the /password-protect
route right now, you should have something like this:
Step 3 - Implementing the API endpoint #
Next, we'll implement the api/password-protect
endpoint to check if the password entered is correct. First, let's install the cookie
package to make it easier to write cookies:
yarn add cookie && yarn add -D @types/cookie
Next, create the file pages/api/password-protect.ts
:
// api/password-protect.ts import { NextApiRequest, NextApiResponse } from "next"; import { serialize } from 'cookie'; export default async function handler(req: NextApiRequest, res:NextApiResponse){ if(req.method !== "POST"){ res.status(405).send("Method Not Allowed") } const password = req.body.password; if(process.env.PASSWORD_PROTECT === password){ const cookie = serialize('login', 'true', { path: '/', httpOnly: true }) res.setHeader('Set-Cookie', cookie) res.redirect(302, '/') } else { const url = new URL("/password-protect", req.headers["origin"]) url.searchParams.append("error", "Incorrect Password") res.redirect(url.toString()) } }
123456789101112131415161718192021
Explanation:
Line 8: We limit this endpoint to only accepting POST requests.
Line 9: We get the password from the request body. The body.password
field here corresponds to name="password"
in our form from earlier.
Line 10: Check if the inputted password is the same as our environment variable.
Line 11-15: If the password is correct, we set the login
cookie indicating that the user has already logged in and won't need the password-protect page in the future. Next, we redirect the user to the index page.
Later, we'll use the login
cookie in the middleware function to redirect the user to the password-protect
page.
Line 17-19: If the password is incorrect, we redirect back to the password-protect
page and pass a query parameter error
.
To display the error, modify the password-protect
page and check the router.query
params:
const PasswordProtectPage = () => { const router = useRouter(); const error = router.query.error; ... return ( ... <form action="/api/password-protect" method="post"> <div className="form-control"> {error && ( <label className="label"> <span className="label-text text-error">{error}</span> </label> )} ... </div> </form> ... ) }
1234567891011121314151617181920
Here's a little demo after this step:
Our password-protect page mechanism is working, it redirects to the home page and sets the login
cookie if the correct password is inputted.
Step 4 - Implementing the Middleware #
The last step that we need to do is to redirect every route to password-protect
page if the login
cookie is not set.
Create a middleware.ts
file in your root project directory and add the following code:
import { NextRequest, NextResponse } from "next/server"; const isPasswordEnabled = !!process.env.PASSWORD_PROTECT export async function middleware(req: NextRequest){ const isLoggedIn = req.cookies.has('login'); const isPathPasswordProtect = req.nextUrl.pathname.startsWith("/password-protect") if(isPasswordEnabled && !isLoggedIn && !isPathPasswordProtect){ return NextResponse.redirect(new URL("/password-protect", req.url)) } return NextResponse.next() } export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - api (API routes) * - _next/static (static files) * - favicon.ico (favicon file) */ '/((?!api|_next/static|favicon.ico|under-development.svg).*)', ], }
123456789101112131415161718192021
Explanation:
We'll redirect all pages to the password-protect page if it matches the following criteria:
- If the environment is password protected. We check this via the
PASSWORD_PROTECT
environment variable - If the user is not logged in. We check this via the
login
cookie - If the pathname is not
password-protect
page. We have to check this since it will trigger an infinite redirect if we don't.
That's It! Your website is now password protected, it doesn't matter if it's a static page or dynamic page since we're doing the check inside the middleware.
Conclusion #
We learned how to add password protection to protect a website in NextJS. We used a combination of NextJS Middleware and cookies to implement the logic. We used DaisyUI + TailwindCSS to create an awesome password-protect
page component.
If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.
Further Reading #
Improve your NextJS knowledge by reading our previous tutorials: