Password ProtectStatic Sites with PageCrypt

By Chris Castle

Render is a Zero DevOps Cloud Platform that helps developers and businesses leverage their most precious resources – time and talent – to build better products faster, delivering more value to their customers with every commit.

Password protecting static sites is tricky. You could use window.prompt() to ask a site visitor to enter a password before the site content is revealed, but a resourceful visitor will quickly get around this with right-click, view source (or curl or numerous other ways). All of the site’s HTML, CSS, and JavaScript are served to the web browser, and they constitute the entirety of the site.

If you are using a backend API that the static site pulls data from (like many single-page apps do these days), you could require authentication for API requests, but this doesn’t protect the HTML, CSS, and JavaScript.

In trying to find a good solution for this for Render users, we discovered PageCrypt. It’s a free, open source library that allows you to password protect these static assets securely. Let’s investigate how PageCrypt works and test it out!

What’s it good for?

  • Protecting a static site
  • Cases where you don’t need (or can’t run) backend code

What are the drawbacks?

  • Only encrypts a single HTML file by default
  • Only supports a single shared password (no per-user passwords)

What is it, and how does it work?

PageCrypt is a novel solution to password protecting HTML without a backend. It’s a library you can use as part of your site’s build step or as a command line tool. It uses the Web Crypto API — currently supported by all major browsers — and a password to encrypt an HTML page, which you can then host on any static hosting platform, including Render! An HTML page encrypted with PageCrypt prompts the viewer for a password. Upon entering the correct password, the page is decrypted and its content replaces the password prompt.

Example static site password protected with PageCrypt

One potential concern with PageCrypt is that it only encrypts an HTML file by default. If you want to encrypt your CSS and JavaScript files, you’ll have to inline them in the HTML file. The same applies to images and any other binary assets; you’ll have to inline them as Data URIs. As with any authentication and authorization solution, you’ll want to determine what’s acceptable for your security requirements. Maybe you’re comfortable with the risk of images leaking but have higher security requirements for your JavaScript. In that case, the HTML page can link to the image files but should contain all your JavaScript. You can use many static site build tools to automate inlining assets in HTML. Webpack, Gulp, or Grunt are just a few that might be useful.

PageCrypt also doesn’t allow users to have individual usernames and passwords. It only works with a single, shared password. If you need the more fine-grained control provided by user accounts, check out a service like Auth0.

Try it out

Adding PageCrypt to the build step of a Hello World static site was straightforward. The instructions in the project’s README provide clear guidance on how to use PageCrypt as a library with browser-executed JavaScript, Node.js, or Deno, and how to use it as a CLI tool. I used the CLI in the build step of my example static site.

Add PageCrypt to your project as a dependency:

yarn add pagecrypt

Then update the build step in package.json to use the CLI:

{
...<snip>...
  "scripts": {
    "build": "pagecrypt src/index.html dist/index.html $PASSWORD && cp -R src/css dist/"
  },
...<snip>...
}

The password is set using the $PASSWORD environment variable since we don’t want to store that in the code. Using an environment variable also allows you to change the password and rebuild the site quickly.

Here’s an example deployment of the site. The password is s3cr3t.

To get a deeper understanding of how PageCrypt works, try entering an incorrect password. Then right-click and view the source of the page. PageCrypt generated the contents of this page during the build step. Your original page content is encrypted inside a hidden <pre> element at the bottom of the HTML document.

A screenshot of a PageCrypt-encrypted HTML page in Google Chrome's elements inspector.
Encrypted HTML page inside the <pre> element.

After the correct password is entered, your page content is decrypted and shown.

To make it easier for users to access password protected pages, PageCrypt also allows you to create a “magic link” that decrypts the page without prompting the user for a password. The format for the magic link is https://<link-to-your-page>#<password>, placing the password in a URI fragment. Check out the project’s README section about magic links to better understand the security implications. When your browser goes to a URL containing a URI fragment, the fragment isn’t sent across the internet, but it does remain in the browser’s history.

Extend

It would be interesting to extend PageCrypt to do a number of things:

  • Automate the inlining of CSS, JavaScript, and image files
  • Encrypt multiple HTML files
  • Encrypt multiple files, including CSS, JavaScript, and image files

If you do end up extending it in your build process, let me know!

Explore

This version of PageCrypt is a rewrite of an older version of PageCrypt. That older version also inspired a few spin-offs that you might find useful:

Now try it out yourself! You can deploy the code to Render for free. The README provides step-by-step instructions.

Interested in working with Chris at Render? Check out our open roles!

Chris Castle

Chris Castle heads up Developer Relations for Render.

Discover More

  1. Building a Developer Experience with RedwoodJS and Render

    Building with Redwood on Render lets Pullflow focus on winning over teams, user by user, to upgrade their developer experience. Learn how they build product based on empathy....

    - Rosalind Benoit

  2. Render, Remix, and Strapi: Let's Build a Productivity Tips App

    Did you miss our StrapiConf 2022 Workshop about deploying Strapi and Remix together on Render? The workshop attendees created their own Productivity Tips app that they can use to…

    - Chris Castle

  3. Expanding Global Reach: APAC & U.S. East

    Today, we announce the General Availability of Render’s expansion into two new regions to best serve our current and future customers: local hosting for U.S. East in Ohio and for A...

    - Rosalind Benoit

  4. Render Redis

    Today, we're announcing the General Availability of Render Redis. You can now set up a Redis instance in a few clicks and then let Render handle the heavy lifting running it reliab...

    - Kelley Rivoire