Free discovery callFree discovery call

How to add Basic Auth to NextJS 14 app on Vercel with next-intl

Without any extra spending. 

DevelopmentLast updated: 10 Jul 20242 min read

By Sebastijan Dumančić

Recently I’ve needed to add a Basic Auth for deployment protection to a NextJS app that we have deployed on Vercel, but got quite shocked that Vercel bundles up this simple feature in their Advanced Deployment Protection addon, which they charge $150/mo per project. Talk about steep.

Thankfully, NextJS gives us the option to write a custom middleware, and this means we can implement a Basic Auth request before we let people click around our app.

There are already a couple of guides online for this, but it’s unclear how to bundle this functionality with other middleware features, such as next-intl route rewrites.

Follow these steps to get both up and running, and enable Basic Auth on NextJS 14 with App router.

First, here’s a full middleware to copy/paste because that’s what you are here for:

import { DEFAULT_LANGUAGE, LOCALES, pathnames } from '@modules/i18n';
import createMiddleware from 'next-intl/middleware';
import { NextRequest, NextResponse } from 'next/server';

export const intlMiddleware = createMiddleware({
  defaultLocale: DEFAULT_LANGUAGE,
  locales: LOCALES,
  pathnames,
  localePrefix: 'as-needed',
});

export default async function middleware(request: NextRequest) {
  const basicAuth = request.headers.get('authorization');

  /** Request authorization */
  if (!basicAuth) {
    request.nextUrl.pathname = '/api/basicauth';

    return NextResponse.rewrite(request.nextUrl);
  }

  const authValue = basicAuth.split(' ')[1];
  const [user, pwd] = atob(authValue).split(':');

  const validUser = process.env.BASIC_AUTH_USER;
  const validPassWord = process.env.BASIC_AUTH_PASSWORD;

  if (user === validUser && pwd === validPassWord) {
    /** Handle Intl */
    const Response = intlMiddleware(request);

    return Response;
  }
}

export const config = {
  // Match only internationalized pathnames
  matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'],
};

You can see that we first do Basic Auth checks, after which the intlMiddleware is handled. First, we check if the user has previously entered a username and password, and if he did, we take the basicAuth data and use atob to decode that data from base64-encoded string.

We can then compare the entered data with the username and password that we consider valid, which can be saved in the .env file. If both are valid, we forward the request to the rest of the middleware actions.

In case there’s no Basic Auth set, we can redirect a user to an error page placed at /api/basicauth/route.ts:

export async function GET(request: Request) {
  console.log('GET /api/basicauth/route.ts');
  return new Response('Authentication Required!', {
    status: 401,
    headers: {
      'WWW-Authenticate': "Basic realm='private_pages'",
    },
  });
}

And that’s it! You will get prompted to enter a username and password before entering the website for the first time. Future visits will not ask for auth.

Struggling with NextJS? Reach out!

With these easy-to-follow steps, you can secure the app without having to pay additional costs — and that's a win in my book.

Of course, if you have any questions or comments, I'd love to hear them! Feel free to reach out at hello@prototyp.digital.

And if you found this post helpful, please share it with your network. Let's keep the conversation going and support each other in making development a little bit easier.

Related ArticlesTechnology x Design

View all articlesView all articles
( 01 )Get started

Start a Project