
A 401 Unauthorized error means the server requires authentication for the requested resource and either no credentials were provided or the ones sent were rejected. Despite the name, this error is really about authentication (proving who you are) rather than authorisation (whether you have permission).
If you're seeing 401 errors on your own server, here's how to diagnose the cause and fix it.
A 401 status code tells the client: "You need to authenticate before I'll serve this resource." The server will typically include a WWW-Authenticate header in the response, telling the client what authentication scheme is required (Basic, Bearer, Digest, etc.).
This is fundamentally different from a 403 Forbidden. A 403 means the server knows who you are but you're not allowed access. A 401 means the server doesn't know who you are at all — or the credentials you provided are wrong.
Key distinction:
The most straightforward cause. The request doesn't include an authentication header, or the username/password combination is wrong. This happens often with API calls where a token is expired, missing, or malformed.
# This will return 401 if the endpoint requires auth
curl -i https://api.example.com/protected-resource
# This should work with valid credentials
curl -i -H "Authorization: Bearer YOUR_TOKEN_HERE" https://api.example.com/protected-resource
JWTs and session tokens expire. If your application issues tokens with a short TTL and the client doesn't refresh them, subsequent requests will fail with a 401.
Check the token's expiry:
# Decode a JWT to check its expiry (requires jq)
echo "YOUR_JWT_TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq '.exp' | xargs -I {} date -d @{}
If you've set up HTTP Basic Authentication on your web server and the credentials file is missing, misconfigured, or has wrong permissions, all requests will receive a 401.
Check the password file exists and is readable:
# Verify the htpasswd file exists
ls -la /etc/nginx/.htpasswd
# Verify Nginx can read it
sudo -u www-data cat /etc/nginx/.htpasswd
The client might be sending Basic auth when the server expects Bearer tokens, or vice versa. Check the WWW-Authenticate header in the 401 response to see what the server expects:
curl -s -D - https://api.example.com/protected -o /dev/null | grep -i www-authenticate
In cross-origin requests, the browser sends a preflight OPTIONS request. If your server doesn't handle this correctly, the browser may strip the Authorization header from the actual request, causing a 401.
If you're running Nginx or Apache as a reverse proxy, it may not be forwarding the Authorization header to your backend application.
If you're using Nginx's built-in Basic Authentication, verify the setup:
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Create or update the password file:
# Install apache2-utils if needed
sudo apt-get install apache2-utils
# Create a new password file with a user
sudo htpasswd -c /etc/nginx/.htpasswd admin
# Add another user (without -c, to avoid overwriting)
sudo htpasswd /etc/nginx/.htpasswd anotheruser
# Set correct permissions
sudo chown www-data:www-data /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd
# Reload Nginx
sudo systemctl reload nginx
<Directory /var/www/html/admin>
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Directory>
# Create the password file
sudo htpasswd -c /etc/apache2/.htpasswd admin
# Reload Apache
sudo systemctl reload apache2
If your Nginx proxy isn't passing the Authorization header to your backend:
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Your server needs to handle OPTIONS requests and explicitly allow the Authorization header:
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
proxy_pass http://localhost:3000;
}
If your API uses JWTs, implement a token refresh flow. The client should detect 401 responses and automatically request a new token using a refresh token before retrying the request. Most HTTP client libraries support interceptors for this:
# Test your refresh endpoint
curl -X POST https://api.example.com/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "your_refresh_token"}'
A 401 error on a public-facing page means your visitors are being asked to log in when they shouldn't be — or your API consumers are being locked out. If a misconfigured deployment accidentally adds authentication to your homepage or a key landing page, you want to know about it immediately, not hours later.
Domain Monitor checks your site every minute from multiple locations. If a page that should return a 200 starts returning 401, you'll receive an instant alert via email, SMS, or Slack. You can set up downtime alerts for your most important endpoints, so authentication misconfigurations are caught before they block real users. With continuous website monitoring, you'll always know whether your site is responding as expected.
| Cause | Fix |
|---|---|
| Missing credentials | Include the correct auth header |
| Wrong username/password | Verify credentials, reset if needed |
| Expired token | Implement token refresh logic |
| Bad htpasswd file | Recreate with htpasswd, fix permissions |
| Proxy stripping auth header | Add proxy_set_header Authorization |
| CORS blocking auth header | Handle OPTIONS preflight properly |
A 401 is the server's way of saying "prove who you are." If your credentials are correct and you're still getting one, the problem is almost always in how the authentication headers are being sent, forwarded, or validated.
Wildcard, SAN (multi-domain), and single-domain SSL certificates cover different use cases. Here's a clear comparison to help you pick the right type — and avoid paying for coverage you don't need.
Read moreDNS resolves correctly from your office but fails for users in other countries or on different ISPs. Here's why geographic DNS inconsistency happens and how to diagnose which layer is causing it.
Read moreRegistrar lock and transfer lock are often confused — and disabling the wrong one leaves your domain vulnerable. Here's a clear breakdown of what each does and when to use them.
Read moreLooking to monitor your website and domains? Join our platform and start today.