Password-protecting Netlify Deploy Previews with Gatsby

Entry 10 May 28, 2021 03 min read

Netlify Deploy Preview showing Basic Auth login dialog with username "not the most secure, but better than nothing!"

I deploy most of my sites (including this one!) on Netlify because it’s so easy. One click here, one click there, and your site is ready to go, with tons of nifty features to boot.

Deploy Previews are one of those nifty features, and I often use them as “staging” environments to test changes made with a PR before merging into main and deploying to production.

But Deploy Previews (and Branch deploys, if enabled) are forever. They persist after PRs are merged or closed … and never go away. I wouldn’t say this is problematic per se, and Jen from Netlify explains how this can actually be useful on Netlify’s community support forum.

But to me, this still feels weird. I understand why production deploys should be immutable, especially because this allows you to instantly “roll-back” to a previously-published version of your site (trust me, this is a real life-saver). I’m not sure if the same logic applies to Deploy Previews, which are created from PRs. If a PR is merged into main, that triggers a production build, which persists forever. So why do we still need the original Deploy Preview? What if a PR is closed, presumably because we don’t want to deploy those changes to production? In that case, shouldn’t the Deploy Preview just disappear?

It doesn’t help that Deploy Preview URLs aren’t obfuscated. If you know a Netlify site’s name (which isn’t considered sensitive), you can easily increment through all of that site’s Deploy Previews. For example, this site’s Netlify name is yihwan, and my first Deploy Preview is available at deploy-preview-1— Can you guess the others? 😉

Like I said, I don’t think this is necessarily problematic. It just feels a little weird. It feels rather exposed, like making your staging site available to anyone roaming the Internet. That brings us to the main point of this post:

Adding a “password” to your Netlify Deploy Preview with Gatsby

I use quotes quite intentionally here, because this “password” (hereafter without quotes because that would be annoying) uses Basic Auth, which isn’t really considered “secure”. However, it does provide a nice check against unintentional access, which sounds perfect for a staging environment.

I found a lovely post by Chris at Netlify that addresses this very topic. But this post discusses tooling at a high-level, and it doesn’t specifically address the Gatsby use case. As I was reading this post towards the end of the day, well past my peak “thinking” hours, figuring out how to password-protect Deploy Previews built with Gatsby took me slightly longer than I care to admit. So I hope this post is helpful for anyone working with Gatsby specifically!

You don’t need gatsby-plugin-netlify

First things first: you do not need gatsby-plugin-netlify. This is a wonderful officially-supported plugin that makes it easy to generate a _headers and _redirects file in your Gatsby project. But you do not need it to password-protect Deploy Previews. Knowing this would have saved me a good half hour of unnecessarily complicated imports and parsing through mysteriously-failing builds. 😅

Create two files in the root directory

Once I realized I didn’t need gatsby-plugin-netlify, the rest was super simple! You just need to create two files in the root directory of your Gatsby project:

  • netlify.toml
  • netlify_headers (you can name this plain-text file whatever you like, but I followed Chris’ example)

The netlify.toml file is essentially Netlify’s config file, which allows you to customize many, many things across different deploy contexts. For our purposes though, our netlify.toml file is short and sweet:

command = "npm run build && cp netlify_headers public/_headers"

That’s it! We don’t need to explicitly set command (or any other vars) for the production context because we can just inherit those from what’s set in Netlify’s UI.

All we’re doing here is running the default build script (npm run build in my case, which in turn runs gatsby build) and copying the values we will soon set in netlify_headers into the public publish directory. Gatsby generates this public directory for us on every build, so we don’t want to add this manually.

This will of course fail because we haven’t added anything to our netlify_headers plaintext file yet. So let’s do that now:

Basic-Auth: username:password

You might want to change username and password to be something slightly more unique, but again this isn’t so much for “security” purposes (after all, I checked this file into version control) but just to make sure people don’t accidentally stumble across your Deploy Preview or staging site.

You should also avoid sending your username and password over http because that would make them available to anyone with access to your network traffic. You can learn more about this and other best practices on MDN. Again though, I wouldn’t consider Basic Auth to be “secure” even over https, and Basic Auth should not be relied upon for any critical or sensitive flows.

That’s it! Now the netlify_headers file will be copied into your public/_headers file for every build created in the Deploy Preview context. You can learn more about the _headers file here. Super easy.

Netlify Deploy Preview showing Basic Auth login dialog with username "you did it!"

It’s worth noting that only Deploy Previews made on or after these changes will show the Basic Auth login dialog. So in the above example, you can see that I set this password in PR #8. However, PRs #1 to #7 remain unprotected.

As of writing, it seems like the only way to get rid of all Deploy Previews permanently is to delete the site. Hopefully this changes soon!

A new blog