> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Varnish 101: Protecting Paths with HTTP Basic Authentication on Upsun

> Learn how to implement HTTP Basic Authentication directly in Varnish VCL on Upsun to protect specific paths like admin areas, staging environments, and work-in-progress content without touching your backend.

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["jared-wright"], date: "2025-10-17T11:55:00+00:00", image: "/images/posts/hands-on/varnish-101-protecting-paths-with-http-basic-authentication-on-upsun/varnish-101.webp" }} />

**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](https://varnish-cache.org/#years-old-and-it-is-time-to-get-serious-er)) 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](https://docs.upsun.com/add-services/varnish.html) 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](https://docs.upsun.com/environments/http-access-control.html#use-a-username-and-password), 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:

1. Client requests a protected resource
2. Server responds with `401 Unauthorized` and a `WWW-Authenticate` header
3. Browser displays a login prompt
4. Client resends the request with an `Authorization` header containing Base64-encoded credentials
5. 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

```vcl {filename=".upsun/config.vcl"} theme={null}
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

```vcl {filename=".upsun/config.vcl"} theme={null}
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

1. **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`).

2. **Authorization Header Check**: `if (!req.http.Authorization)` verifies whether the client sent credentials. If not, we immediately return a `401` status, triggering the authentication challenge.

3. **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` becomes `YWRtaW46YWRtaW4=`
   * Example: `username:password` becomes `dXNlcm5hbWU6cGFzc3dvcmQ=`

4. **Multiple Allowed Credentials**: The regex pattern `(YWRtaW46YWRtaW4=|dXNlcm5hbWU6cGFzc3dvcmQ=)` allows multiple username/password combinations using alternation (OR logic).

5. **Synthetic Response**: When authentication fails, `return (synth(401, "Authentication Required"))` triggers `vcl_synth` with a 401 status.

6. **WWW-Authenticate Header**: In `vcl_synth`, the `WWW-Authenticate` header tells the browser to display the login prompt. The `realm` parameter appears in the browser's authentication dialog.

## Generating Base64 Credentials

To create your own credential strings, use the [DebugBear Basic Auth Header Generator](https://www.debugbear.com/basic-auth-header-generator) or encode manually:

```bash {filename="Terminal"} theme={null}
# 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:

```vcl {filename=".upsun/config.vcl"} theme={null}
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:

```vcl {filename=".upsun/config.vcl"} theme={null}
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:

1. **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.

2. **HTTPS Required**: Always use HTTPS when deploying Basic Auth. Over HTTP, credentials are transmitted in the clear and can be intercepted.

3. **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:

```vcl {filename=".upsun/config.vcl"} theme={null}
if (req.url ~ "^/admin") {
    # Check credentials
}
```

### Staging Content

Hide work-in-progress content from public view while allowing authorized reviewers access:

```vcl {filename=".upsun/config.vcl"} theme={null}
if (req.url ~ "^/preview/") {
    # Check credentials
}
```

### Internal APIs

Add a simple authentication layer for internal API endpoints:

```vcl {filename=".upsun/config.vcl"} theme={null}
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.
