English
Nginx Basic Authentication: Adding Access Control to the Nuxt Staging Environment
Use cases
Before the official release, you need a Internal test environment or Canary preview environment, only team members or specific users are allowed to access, preventing search engine crawling or accidental access by external users.
💡 What is Nginx Basic Authentication
Nginx Basic Authentication (HTTP Basic Authentication) is a simple access control method that verifies user identity via username and password. Combined with Cloudflare or firewall rules, it can achieve "whitelist + password" double protection.
Precondition
Deployment of the Nuxt application has been completed (refer to Deploying a Nuxt application from scratch to an Ubuntu 22.04 server》)
The server has Nginx installed.
Has sudo privileges
1. Install the apache2-utils tool
htpasswd The command is contained in apache2-utils in the package, used to generate encrypted passwords:
bash
sudo apt-get update
sudo apt-get install -y apache2-utilsVerify installation:
bash
htpasswd -h2. Create a password file
Create a password file and add the first user.
bash
# Create password file (-c means create a new file)
sudo htpasswd -c /etc/nginx/.htpasswd your-username
# The system will prompt you to enter the password twice.Add more users (no -c parameter required)
bash
# Append user to existing password file
sudo htpasswd /etc/nginx/.htpasswd another-usernameView password file contents
bash
sudo cat /etc/nginx/.htpasswd
# Output example:
# your-username:$apr1$randomhash$xxxxxxxxxxxxxDelete user
bash
# Use sed to delete the specified user
sudo sed -i '/username-to-delete/d' /etc/nginx/.htpasswd3. Configure Nginx basic authentication
Option 1: Protect the entire site
Edit your Nginx configuration file (e.g./etc/nginx/sites-available/myapp):
nginx
server {
listen 80;
server_name pre.your-domain.com;
# Basic authentication configuration
auth_basic "Restricted Access - Internal Only";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Option 2: Protect specific paths
If you only want to protect the admin backend or specific paths:
nginx
server {
listen 80;
server_name your-domain.com;
location / {
# public access
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
}
location /admin {
# Authentication required
auth_basic "Admin Area - Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
}
location /preview {
# Authentication required
auth_basic "Preview Area - Internal Only";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
}
}Option 3: Staging environment + IP whitelist dual protection
Combine with an IP whitelist to achieve more granular access control:
nginx
server {
listen 80;
server_name pre.your-domain.com;
location / {
# Allow specific IPs to access directly (no password required)
allow 192.168.1.0/24; # Company intranet
allow 10.0.0.0/8; # enterprise intranet
allow 203.0.113.100; # Specific public IP
# Other IPs require password authentication
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# Reject all unmatched IPs (this is very important)
deny all;
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}4. Complete configuration example (staging environment)
The following is a complete configuration example for the pre-release environment:
nginx
# /etc/nginx/sites-available/preview
server {
listen 80;
server_name preview.your-domain.com;
# Logging
access_log /var/log/nginx/preview-access.log;
error_log /var/log/nginx/preview-error.log;
# Basic Authentication
auth_basic "Preview Environment - Internal Access Only";
auth_basic_user_file /etc/nginx/.htpasswd;
# Custom authentication prompt (optional)
auth_basic_user_file /etc/nginx/.htpasswd;
# Timeout configuration (convenient for debugging in the pre-release environment)
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Static resources (still require authentication, but can be cached)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf)$ {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:3000;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Error page (avoid leaking information)
error_page 401 /401.html;
location = /401.html {
auth_basic off;
return 401 '<html><body><h1>Authentication Required</h1><p>This is a private preview environment.</p></body></html>';
}
}5. Enable the configuration and restart
bash
# Test configuration syntax
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginx6. Test access
Browser access
Visit in the browser http://preview.your-domain.com, a login box will pop up:
Username: The username you created
Password: the password you set
Command-line test
bash
# Access without authentication (should return 401 Unauthorized)
curl -i http://preview.your-domain.com
# Authenticated access
curl -u username:password http://preview.your-domain.com
# Should return 200 OK7. Using with Cloudflare (HTTPS + Authentication)
If you use Cloudflare, you can enable both HTTPS and Basic Authentication:
nginx
# HTTP redirect to HTTPS
server {
listen 80;
server_name preview.your-domain.com;
return 301 https://$server_name$request_uri;
}
# HTTPS configuration
server {
listen 443 ssl http2;
server_name preview.your-domain.com;
# Cloudflare Origin CA certificate
ssl_certificate /etc/nginx/ssl/your-domain.pem;
ssl_certificate_key /etc/nginx/ssl/your-domain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
# Basic Authentication
auth_basic "Preview Environment - Internal Only";
auth_basic_user_file /etc/nginx/.htpasswd;
# Cloudflare real IP
set_real_ip_from 173.245.48.0/20;
# ... other Cloudflare IP ranges ...
real_ip_header CF-Connecting-IP;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}8. Advanced Configuration Tips
Configure authentication timeout
By default, the browser caches authentication information until it is closed. This can be controlled in the following ways:
nginx
# Add authentication expiration time (browser support)
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# Add custom response headers
add_header Cache-Control "no-store, must-revalidate";Custom login failure prompt
nginx
# Custom 401 error page
error_page 401 = @error401;
location @error401 {
auth_basic off;
add_header WWW-Authenticate 'Basic realm="Restricted Access"';
return 401 '<html><body>
<h1>Access Denied</h1>
<p>This preview environment is restricted to internal users only.</p>
<p>Please contact the development team for access.</p>
</body></html>';
}Multi-level authentication
nginx
# Outer basic authentication
auth_basic "Outer Protection";
auth_basic_user_file /etc/nginx/.htpasswd-outer;
location / {
# Inner-layer additional authentication
auth_basic "Inner Protection";
auth_basic_user_file /etc/nginx/.htpasswd-inner;
proxy_pass http://127.0.0.1:3000;
}9. Password Management Best Practices
Generate a strong password
bash
# Use openssl to generate a random password (24 characters)
openssl rand -base64 24
# Use htpasswd to specify bcrypt encryption (more secure)
htpasswd -B -c /etc/nginx/.htpasswd usernameRegular password rotation
bash
#!/bin/bash
# update-htpasswd.sh
# Script for regularly updating passwords
PASSWORD=$(openssl rand -base64 16)
echo "newuser:$(openssl passwd -6 $PASSWORD)" | sudo tee -a /etc/nginx/.htpasswd
echo "New password for newuser: $PASSWORD"
# Notify team members (via Slack, email, etc.)
# curl -X POST -H 'Content-type: application/json' --data '{"text":"Preview password updated"}' your-webhook-url
sudo nginx -t && sudo systemctl reload nginxSecure password file permissions
bash
# Ensure the password file permissions are correct.
sudo chmod 640 /etc/nginx/.htpasswd
sudo chown root:www-data /etc/nginx/.htpasswd
# Verify permissions
ls -la /etc/nginx/.htpasswd
# Should output: -rw-r----- 1 root www-data ...10. Integration with automated deployment scripts
In the previous article's deploy.sh On this basis, add pre-release environment deployment:
bash
#!/bin/bash
# deploy-preview.sh - Deploy to the pre-release environment
SERVER="root@your-server-ip"
APP_PATH="/var/www/preview-app"
PM2_NAME="preview-app"
# Build
echo "📦 Building preview..."
pnpm build
# Packaging
echo "📁 Packaging..."
tar -czf deploy.tar.gz -C .output .
# Upload
echo "📤 Uploading..."
scp deploy.tar.gz $SERVER:$APP_PATH/
# Deploy and ensure the basic authentication configuration takes effect
ssh $SERVER "cd $APP_PATH && \
tar -xzf deploy.tar.gz && \
pm2 reload $PM2_NAME && \
rm deploy.tar.gz && \
echo '✅ Preview deployment complete!'"
rm deploy.tar.gz11. Common Troubleshooting
Q1: The browser does not pop up a login dialog box.
Reason: It may be a syntax error in the authentication configuration or the password file path is incorrect.
bash
# Check Nginx configuration
sudo nginx -t
# Check the password file
sudo cat /etc/nginx/.htpasswd
# Check the Nginx error log
sudo tail -f /var/log/nginx/error.logQ2: Still 401 after login
Reason: Incorrect password or incompatible encryption method.
bash
# Regenerate password (using MD5 encryption, best compatibility)
sudo htpasswd -c /etc/nginx/.htpasswd username
# Restart Nginx
sudo systemctl restart nginxQ3: Static resources are also intercepted by authentication.
Solution Configure static resource paths to not require authentication:
nginx
location /_nuxt/ {
auth_basic off;
proxy_pass http://127.0.0.1:3000;
}Q4: How to temporarily disable authentication
nginx
# Just comment out the authentication-related configuration.
# auth_basic "Restricted Access";
# auth_basic_user_file /etc/nginx/.htpasswd;
# Reload configuration
sudo nginx -s reload12. Security Recommendations
| Suggestion | Explanation |
|---|---|
| Use HTTPS | The password for basic authentication is Base64-encoded and must be used with HTTPS. |
| Restrict IP | Combine with an IP whitelist to reduce the risk of password exposure. |
| Periodic rotation | Change passwords regularly, especially when there are personnel changes. |
| Use a strong password | Password must be at least 16 characters and include uppercase and lowercase letters, numbers, and special characters. |
| Log Monitoring | Monitoring access.log, and promptly handle any abnormal access upon discovery |
| Not for highly sensitive scenarios | Basic authentication is suitable for pre-release environments; for production environments, OAuth/JWT is recommended. |
Conclusion
Using Nginx basic authentication, you can quickly set up a protected staging environment, allowing team members to conduct testing and acceptance in a secure environment.
The advantage of this approach lies in:
Zero code changes: No need to modify Nuxt application code
Simple configuration It can be done with just a few lines of Nginx configuration.
Effective immediately: After modifying the configuration
nginx -s reloadis enoughLow resource usage: Native Nginx support, minimal performance impact
Combined with the complete deployment process in the previous article, you can easily achieve:
Production environment: public access + Cloudflare CDN
Staging environment: password protection + IP whitelist
Further reading
- Deploying a Nuxt application to an Ubuntu 22.04 server from scratch- Includes the complete deployment process and Cloudflare Origin CA certificate configuration (Option 2)