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:
- Only users with a specific password can access the website's contents.
- Users should be able to access the site through a link without manually entering the password.
- Users only need to authenticate once; after that, they can freely browse the site.
- 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:
- Links to the site include a query parameter that contains the password.
- The server detects the query parameter, sets a cookie with the password, and redirects to the same URL without the query parameter.
- If the server detects the correct password cookie, it serves the content. Otherwise, it returns a
403 Forbiddenerror.
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.