Varnish 101: Protecting Paths with HTTP Basic Authentication on Upsun
Welcome to the first article in our Varnish series, where we’ll explore practical techniques for leveraging Varnish Cache (soon to be rebranded as Vinyl) beyond simple caching. While many developers view Varnish as just an HTTP accelerator, its VCL (Varnish Configuration Language) provides powerful capabilities for traffic control, security, and request processing—all before requests reach your backend application.
Throughout this series, we’ll cover practical implementations including path protection, rate limiting, URL normalization, and advanced traffic filtering. This first article focuses on implementing HTTP Basic Authentication directly in VCL to protect specific paths like admin areas and staging environments.
Why Protect Paths in Varnish?
Varnish Cache sits between your application and your users as a powerful HTTP accelerator. Beyond caching, it’s the perfect place to implement simple access controls for specific paths—blocking unauthorized requests before they consume backend resources.
While Upsun offers environment-level HTTP access control, implementing authentication directly in VCL gives you more flexibility:
- Path-specific protection: Secure only certain URLs (e.g.,
/admin
,/staging
) - Multiple credential sets: Use different passwords for different areas
- Custom logic: Combine authentication with other VCL conditions
- Edge enforcement: Block unauthorized traffic at the Varnish layer
Understanding HTTP Basic Authentication
HTTP Basic Authentication is a simple challenge-response mechanism where:
- Client requests a protected resource
- Server responds with
401 Unauthorized
and aWWW-Authenticate
header - Browser displays a login prompt
- Client resends the request with an
Authorization
header containing Base64-encoded credentials - Server validates credentials and either allows or denies access
In VCL, we implement this by checking the Authorization
header against known credentials and generating synthetic responses for authentication challenges.
Implementation
HTTP Basic Auth in VCL requires two parts: checking credentials in vcl_recv
and generating the authentication challenge in vcl_synth
.
Part 1: Credential Verification in vcl_recv
sub vcl_recv {
...
# --- Basic Auth ---
# Check if the path is restricted.
if (req.url ~ "^/admin") {
# Check for the Authorization header
if (!req.http.Authorization) {
# If no Authorization header, request credentials
return (synth(401, "Authentication Required"));
}
# Decode the Base64 encoded credentials (e.g., "Basic dXNlcm5hbWU6cGFzc3dvcmQ=")
# In this example, we'll hardcode the expected credentials
# "username:password" and "admin:admin" Base64 encoded
# Use https://www.debugbear.com/basic-auth-header-generator to generate credentials
if (req.http.Authorization !~ "^Basic (YWRtaW46YWRtaW4=|dXNlcm5hbWU6cGFzc3dvcmQ=)$") {
return (synth(401, "Authentication Required"));
}
}
...
}
Part 2: Authentication Challenge in vcl_synth
sub vcl_synth {
...
if (resp.status == 401) {
set resp.http.WWW-Authenticate = {"Basic realm="Restricted area""};
set resp.http.Content-Type = "text/plain";
set resp.body = "Access Denied";
return (deliver);
}
...
}
How It Works
Path Matching: The condition
if (req.url ~ "^/admin")
identifies protected paths. The^
anchor ensures only paths starting with/admin
are restricted (e.g.,/admin
,/admin/dashboard
, but not/public/admin
).Authorization Header Check:
if (!req.http.Authorization)
verifies whether the client sent credentials. If not, we immediately return a401
status, triggering the authentication challenge.Credential Validation: The second check validates the Base64-encoded credentials:
- HTTP Basic Auth sends credentials as
Authorization: Basic <base64-string>
- The base64 string encodes
username:password
- Example:
admin:admin
becomesYWRtaW46YWRtaW4=
- Example:
username:password
becomesdXNlcm5hbWU6cGFzc3dvcmQ=
- HTTP Basic Auth sends credentials as
Multiple Allowed Credentials: The regex pattern
(YWRtaW46YWRtaW4=|dXNlcm5hbWU6cGFzc3dvcmQ=)
allows multiple username/password combinations using alternation (OR logic).Synthetic Response: When authentication fails,
return (synth(401, "Authentication Required"))
triggersvcl_synth
with a 401 status.WWW-Authenticate Header: In
vcl_synth
, theWWW-Authenticate
header tells the browser to display the login prompt. Therealm
parameter appears in the browser’s authentication dialog.
Generating Base64 Credentials
To create your own credential strings, use the DebugBear Basic Auth Header Generator or encode manually:
# Using command line
echo -n "myuser:mypassword" | base64
# Output: bXl1c2VyOm15cGFzc3dvcmQ=
# Using Python
python3 -c "import base64; print(base64.b64encode(b'myuser:mypassword').decode())"
# Output: bXl1c2VyOm15cGFzc3dvcmQ=
Then add the encoded string to your VCL pattern:
sub vcl_recv {
...
if (req.http.Authorization !~ "^Basic (YWRtaW46YWRtaW4=|dXNlcm5hbWU6cGFzc3dvcmQ=|bXl1c2VyOm15cGFzc3dvcmQ=)$") {
return (synth(401, "Authentication Required"));
}
...
}
Protecting Multiple Paths
You can protect different areas with different credentials:
sub vcl_recv {
...
# Admin area with one set of credentials
if (req.url ~ "^/admin") {
if (!req.http.Authorization ||
req.http.Authorization !~ "^Basic YWRtaW46YWRtaW4=$") {
return (synth(401, "Admin Authentication Required"));
}
}
# Staging area with different credentials
if (req.url ~ "^/staging") {
if (!req.http.Authorization ||
req.http.Authorization !~ "^Basic c3RhZ2luZzpzdGFnZTEyMw==$") {
return (synth(401, "Staging Authentication Required"));
}
}
# API with third set of credentials
if (req.url ~ "^/api/internal") {
if (!req.http.Authorization ||
req.http.Authorization !~ "^Basic YXBpOnNlY3JldGtleQ==$") {
return (synth(401, "API Authentication Required"));
}
}
...
}
Security Considerations
Important: HTTP Basic Auth has several security limitations:
Credentials in VCL: The credentials are stored in plain text (Base64 is just encoding, not encryption) in your VCL configuration. Anyone with access to your VCL can decode them instantly.
HTTPS Required: Always use HTTPS when deploying Basic Auth. Over HTTP, credentials are transmitted in the clear and can be intercepted.
Not for Primary Production User Auth: This approach is suitable for:
- Restricting admin areas with a shared password
- Adding a simple barrier to work-in-progress content
- Protecting staging environments from public access
- Securing internal APIs with simple tokens
Do NOT use for:
- Production user authentication (use proper session-based auth in your application)
- Primary protection for sensitive customer data
- Any scenario requiring user-specific permissions or audit trails
Use Cases
HTTP Basic Auth in Varnish is ideal for:
Admin Areas
Protect administrative interfaces that should only be accessible to team members:
if (req.url ~ "^/admin") {
# Check credentials
}
Staging Content
Hide work-in-progress content from public view while allowing authorized reviewers access:
if (req.url ~ "^/preview/") {
# Check credentials
}
Internal APIs
Add a simple authentication layer for internal API endpoints:
if (req.url ~ "^/api/internal/") {
# Check API credentials
}
Conclusion
HTTP Basic Authentication in Varnish VCL provides a lightweight way to protect specific paths without adding complexity to your application code. While it’s not suitable for production user authentication, it’s perfect for admin areas, staging content, and internal tools.
Ready to take your Varnish security further? In our next article, Varnish 102: Rate Limiting, we’ll explore protecting your backend from abuse using the vsthrottle VMOD to implement intelligent request throttling.