Table of Contents
- Password Protection
- Overview
- Quick Setup
- Step 1: Generate Password Hash
- Step 2: Add to .pages File
- Step 3: Configure Traefik (Optional)
- Step 4: Commit and Push
- Advanced Configuration
- Password Generation Methods
- Login Page
- How It Works
- Security Features
- Multiple Protected Sites
- Removing Protection
- Troubleshooting
- Login page doesn't appear
- Wrong password error even with correct password
- Cookie not persisting
- Getting redirected after login
- Best Practices
- Example Configurations
Password Protection
Protect your Forgejo Pages sites with secure password authentication.
Overview
The password protection feature allows you to require authentication before users can access your site. It uses:
- SHA256 password hashing - Passwords never stored in plaintext
- HMAC-signed cookies - Prevents cookie tampering
- Secure cookies - HttpOnly, Secure (HTTPS), SameSite=Strict
- Beautiful login UI - Gradient design with centered form
Quick Setup
Step 1: Generate Password Hash
echo -n "your-password-here" | shasum -a 256
Example output:
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
Step 2: Add to .pages File
enabled: true
password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
Step 3: Configure Traefik (Optional)
http:
middlewares:
pages-server:
plugin:
pages-server:
pagesDomain: pages.example.com
forgejoHost: https://git.example.com
authCookieDuration: 3600 # 1 hour
authSecretKey: "randomly-generated-secret-key"
Step 4: Commit and Push
git add .pages
git commit -m "Add password protection"
git push
Advanced Configuration
Cookie Duration
Control how long users stay logged in:
authCookieDuration: 14400 # 4 hours in seconds
Recommendations:
- Public sites: 1-4 hours (3600-14400)
- Private sites: 8-24 hours (28800-86400)
- Internal sites: 7 days (604800)
Cookie Security
For maximum security, always configure authSecretKey:
# Generate random secret key
openssl rand -base64 32
authSecretKey: "your-random-secret-here"
Without authSecretKey, cookies are still functional but not cryptographically signed.
Password Generation Methods
Using shasum (Mac/Linux)
echo -n "mypassword" | shasum -a 256
Using OpenSSL
# Generate random password
openssl rand -base64 16
# Hash a password
echo -n "your-password" | openssl dgst -sha256
Using Python
import hashlib
password = "mypassword"
hash_object = hashlib.sha256(password.encode())
print(hash_object.hexdigest())
Using Node.js
const crypto = require('crypto');
const password = 'mypassword';
const hash = crypto.createHash('sha256').update(password).digest('hex');
console.log(hash);
Login Page
When users visit a password-protected site, they see a beautiful login page:
- 🎨 Purple gradient background
- 📱 Responsive design
- 🔍 Repository name displayed
- ⚠️ Error messages for wrong passwords
- ⌨️ Auto-focus on password field
- 🔒 Secure HTTPS-only form submission
How It Works
- User visits protected site
- Plugin checks for authentication cookie
- No cookie found → Show login page
- User enters password
- Password hashed with SHA256
- Hash compared with stored hash
- Match → Set signed cookie, redirect to site
- No match → Show error message
Security Features
Password Hashing
- Passwords stored as SHA256 hashes in
.pagesfile - Original password never stored
- One-way hashing prevents password recovery
Cookie Security
- HttpOnly: JavaScript cannot access cookies (XSS protection)
- Secure: Only sent over HTTPS connections
- SameSite=Strict: Prevents CSRF attacks
- HMAC Signature: Prevents cookie tampering (when authSecretKey configured)
Cache TTL
- Password hashes cached for 60 seconds
- Reduces .pages file reads
- Automatic cache refresh every minute
- No password stored in cache (only hash)
Multiple Protected Sites
Each repository gets its own authentication:
Cookie: pages_auth_username_repo1 # For repo1
Cookie: pages_auth_username_repo2 # For repo2
Users must authenticate separately for each protected repository.
Removing Protection
Simply remove the password: line from .pages:
enabled: true
# password: removed
Wait up to 60 seconds for cache to expire, or clear cache manually.
Troubleshooting
Login page doesn't appear
Problem: Site loads normally without asking for password
Solutions:
- Verify
password:field exists in.pagesfile - Check hash is valid SHA256 (64 hex characters)
- Wait 60 seconds for cache to update
- Clear password cache:
redis-cli DEL "password:username:repository"
Wrong password error even with correct password
Problem: "Incorrect password" message with right password
Solutions:
- Verify you're using the hash in
.pages, not plaintext - Check for extra spaces/newlines in hash
- Re-generate hash:
echo -n "password" | shasum -a 256 - Ensure exact password match (case-sensitive)
Cookie not persisting
Problem: Have to login on every page
Solutions:
- Ensure site served over HTTPS (cookies are Secure-only)
- Check browser allows cookies
- Verify
authCookieDurationis positive number - Check browser cookie settings
Getting redirected after login
Problem: Stuck in login/redirect loop
Solutions:
- Clear browser cookies for the site
- Check
authSecretKeyhasn't changed - Verify system clock is accurate
- Check browser JavaScript is enabled
Best Practices
- Use strong passwords - 16+ characters, mixed case, numbers, symbols
- Configure authSecretKey - Enables HMAC cookie signing
- Set appropriate cookie duration - Balance security vs. convenience
- Use HTTPS - Required for secure cookies
- Rotate passwords - Change periodically for sensitive sites
- Monitor access - Check logs for failed login attempts
Example Configurations
Basic Protection
enabled: true
password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
With Custom Domain
enabled: true
custom_domain: private.example.com
password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
High Security (Short Duration)
Traefik config:
authCookieDuration: 1800 # 30 minutes
authSecretKey: "long-random-secret-key"
.pages:
enabled: true
password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
Low Security (Long Duration)
Traefik config:
authCookieDuration: 604800 # 7 days
authSecretKey: "random-secret"
.pages:
enabled: true
password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8