Skip to content
← All writing

Password-Protecting a Static Site with NGINX

I’m building a website for my wedding next year! One of the constraints is that I need to password-protect details like the location and date.

The site is mostly static, so adding a full authentication system felt like a lot. I also needed to protect the JavaScript and other assets, since they could contain the same private information.

Constraints

Here’s what I needed:

  1. Only users with a specific password can access the website's contents.
  2. Users should be able to access the site through a link without manually entering the password.
  3. Users only need to authenticate once; after that, they can freely browse the site.
  4. After the first request, remove the password from the visible URL.

I wanted something lightweight that wouldn’t complicate the static site. My first thought was NGINX, which I already use to serve the files. I’m fairly new to NGINX, but I had a hunch it could handle this.

The approach

Here’s the approach I ended up with:

  1. Links to the site include a query parameter that contains the password.
  2. The server detects the query parameter, sets a cookie with the password, and redirects to the same URL without the query parameter.
  3. If the server detects the correct password cookie, it serves the content. Otherwise, it returns a 403 Forbidden error.

The configuration is pretty small:

server {
  set $password '<password>';

  location / {
    # Handle query parameter authentication
    if ($arg_pw = $password) {
      add_header Set-Cookie "pw=$arg_pw; Path=/; HttpOnly; Secure; SameSite=Lax";
      add_header Cache-Control "no-cache, no-store";
      return 302 $scheme://$host$uri;
    }

    # Check for the password cookie
    if ($cookie_pw != $password) {
      add_header Cache-Control "no-cache, no-store";
      return 403;
    }

    # Serve static files
    try_files $uri $uri/ =404;
  }
}

Debugging challenges

I ran into two issues while putting this together.

Redirect caching

Browsers sometimes cache the resolved target of a redirect while ignoring its side effects, like setting a cookie. I added Cache-Control headers to disable caching on both the redirect and the 403 response.

The cookie’s SameSite policy

I initially used SameSite=Strict, but that caused issues when someone opened the wedding site from an external source, like Discord. Switching to SameSite=Lax fixed it.

A security caveat

This is lightweight access control for a small private site, not a replacement for a proper authentication system. The password still appears in the first request, so it can end up in browser history or server logs before the redirect removes it from the URL.

For what I need, though, it works without adding an application server or a second authentication layer.

© 2026 Eugene CheRSS