Skip to main content
Varnish Cache is a powerful HTTP accelerator that sits between your application and your users, dramatically improving performance through intelligent caching. On Upsun, Varnish becomes even more powerful and can protect your application when combined with our data classification headers.

How Varnish on Upsun Protects Your Applications

In this guide, we’ll explore practical VCL techniques for building a more secure application by blocking malicious traffic based on abuse scores, geographic location and network operators.

Blocking Requests by Abuse Score

One of the most effective ways to protect your application is to block traffic from known malicious IP addresses before it reaches your application layer. Using classification headers like client-abuse-score, you can make intelligent decisions about which requests to block directly in Varnish.

Understanding the Abuse Score Header

The client-abuse-score header contains a numeric value indicating the likelihood that a request originates from a malicious source. Higher scores indicate higher risk. Services like AbuseIPDB aggregate reports of malicious activity from across the internet, and this data can be surfaced through classification headers.

Implementation

Here’s a VCL snippet that blocks requests with high abuse scores:
sub vcl_recv {
...
    # --- Block by Abuse Score ---
    # Check if the "client-abuse-score" header exists in the request.
    if (req.http.client-abuse-score) {
        # If the header exists, convert its value to an integer.
        # Then, check if the score is greater than 25.
        if (std.integer(req.http.client-abuse-score, 0) > 25) {
            # If the score is high, send a synthetic 403 Forbidden response.
            return (synth(403, "High client abuse score detected. Please check your IP on https://www.abuseipdb.com"));
        }
    }
...
}

How It Works

  1. Header Check: First, we verify that the client-abuse-score header exists in the incoming request using if (req.http.client-abuse-score). This prevents errors if the header isn’t present.
  2. Score Conversion: The std.integer() function converts the header value to an integer. The second parameter (0) serves as a fallback value if the conversion fails, ensuring safe handling of unexpected data.
  3. Threshold Evaluation: We compare the abuse score against a threshold (25 in this example). This value is configurable based on your security requirements:
    • Lower thresholds (10-20): More aggressive blocking, may include false positives
    • Medium thresholds (25-50): Balanced approach, blocks clearly malicious traffic
    • Higher thresholds (50+): Conservative, only blocks highly suspicious IPs
  4. Synthetic Response: When the threshold is exceeded, return (synth(403, ...)) generates an HTTP 403 Forbidden response without touching your backend servers. The custom message directs users to check their IP reputation, which is helpful for legitimate users who may be on shared networks.

Benefits

  • Early Termination: Malicious requests are blocked at the edge, never reaching your application servers
  • Resource Savings: Reduces load on backend infrastructure by filtering bad traffic early
  • Customizable: Easy to adjust thresholds based on your traffic patterns and security needs

Tuning Your Threshold

Start with a conservative threshold (like 50) and monitor your traffic. Review blocked requests and adjust downward if you’re not catching enough malicious traffic, or upward if you’re seeing false positives. To understand your typical traffic patterns, review your Upsun environment logs and look for the client-abuse-score header values in legitimate requests. This baseline will help you set appropriate thresholds that protect against attacks without blocking real users.

Geographic Access Control

Another powerful classification header available on Upsun is client-country, which identifies the geographic origin of a request using ISO 3166-1 alpha-2 country codes. This enables you to implement geographic access controls directly in Varnish, blocking or allowing traffic based on country of origin.

When to Use Country-Based Blocking

Geographic filtering is useful in several scenarios:
  • Compliance Requirements: Certain regulations (GDPR, export controls, sanctions) may require restricting access from specific regions
  • Threat Mitigation: If you’re experiencing attacks predominantly from certain geographic regions
  • Service Availability: When your application or content is only licensed or intended for specific markets
  • Resource Protection: Reducing load from regions where you don’t operate or have customers

Implementation

Here’s how to block requests from specific countries:
sub vcl_recv {
...
    # --- Block by Country ---
    # Check if the "client-country" header exists.
    if (req.http.client-country) {
        # Match against a list of blocked country codes.
        # The (?i) flag makes the regex case-insensitive.
        # Example: Russia (RU), and North Korea (KP).
        if (req.http.client-country ~ "(?i)(RU|KP)") {
            # If the country is in the blocklist, return a 403 Forbidden error.
            return (synth(403, "Access from your country is restricted."));
        }
    }
...
}

How It Works

  1. Header Validation: The outer if (req.http.client-country) statement checks whether the country header exists. This defensive check prevents errors if geolocation data is unavailable.
  2. Regex Pattern Matching: The ~ operator performs regular expression matching against the header value. Let’s break down the regex pattern:
    • (?i): Case-insensitive flag, ensuring matches work regardless of uppercase/lowercase
    • (RU|KP): Matches any of the specified country codes using alternation (OR logic)
  3. Country Code Format: The pattern uses ISO 3166-1 alpha-2 codes:
    • RU: Russian Federation
    • KP: Democratic People’s Republic of Korea (North Korea)
  4. Rejection Response: When a match occurs, return (synth(403, ...)) immediately terminates the request with a 403 Forbidden status, providing a clear message to the user.

Customizing Your Country List

Modify the regex pattern to match your requirements:
sub vcl_recv {
...
    # Block a larger set of countries
    if (req.http.client-country ~ "(?i)^(RU|CN|KP|IR|SY)$") {
        return (synth(403, "Access from your country is restricted."));
    }

    # Allow only specific countries (inverse logic)
    if (req.http.client-country && req.http.client-country !~ "(?i)^(US|CA|GB|AU)$") {
        return (synth(403, "Service is only available in select regions."));
    }
...
}

Best Practices

  • Legal Review: Consult with legal counsel before implementing geographic restrictions to ensure compliance with applicable laws
  • VPN Awareness: Users can bypass country-based blocking using VPNs; this is a deterrent, not absolute security
  • Monitoring: Log blocked countries to understand impact and adjust your list accordingly
  • Graceful Messaging: Provide clear, professional error messages that explain the restriction
  • Exception Handling: Consider implementing allowlists for specific IPs (partners, business relationships) within blocked countries

Combining with Other Filters

Country-based filtering works well alongside other security measures:
sub vcl_recv {
...
    # Block high-risk combinations
    if (req.http.client-country ~ "(?i)^(RU|KP)$" &&
        std.integer(req.http.client-abuse-score, 0) > 10) {
        # Lower abuse threshold for certain countries
        return (synth(403, "Access denied due to security policies."));
    }
...
}

Blocking by Autonomous System Number (ASN)

Beyond country codes and abuse scores, you can also filter traffic based on the Autonomous System Number (ASN) using the client-asn classification header. An ASN identifies the network operator responsible for routing internet traffic for a particular IP address block.

Understanding ASN-Based Filtering

ASN filtering provides more granular control than country-based filtering. You might want to block specific ASNs because:
  • Cloud Provider Abuse: Certain cloud hosting providers are frequently used for bot traffic or attacks
  • Known Bad Actors: Specific ISPs or hosting companies with poor abuse management
  • Corporate Policy: Blocking competitors’ networks or specific corporate entities
  • Targeted Threats: When attacks originate from specific network operators

Implementation

Here’s how to block traffic from specific ASNs:
sub vcl_recv {
...
    # --- Block by ASN (Autonomous System Number) ---
    # Check if the "client-asn" header exists.
    if (req.http.client-asn) {
        # Match against a list of blocked ASNs.
        # Example: Alibaba AS45102 and Huawei AS136907 ASNs "45102" and "AS136907".
        if (req.http.client-asn ~ "^(45102|136907)$") {
             # If the ASN is in the blocklist, return a 403 Forbidden error.
            return (synth(403, "Access from your network is restricted."));
        }
    }
...
}

How It Works

  1. Header Check: Verifies the client-asn header exists before attempting to match against it.
  2. Regex Matching: Uses the ~ operator with a regex pattern to match specific ASNs:
    • ^: Start anchor ensures matching from the beginning
    • (45102|136907): Matches either ASN using alternation
    • $: End anchor ensures complete string match
  3. ASN Format: ASNs are numeric identifiers. In this example:
    • 45102: Alibaba
    • 136907: Huawei Cloud

Finding ASN Information

You can identify ASNs using various tools:
# Using whois
whois -h whois.cymru.com " -v 1.2.3.4"

# Using online tools
# - https://bgp.he.net/
# - https://ipinfo.io/

Important Considerations

  • Collateral Damage: ASNs can represent large networks with many legitimate users. Block carefully.
  • Dynamic Threat Landscape: Networks change ownership and abuse patterns shift. Review your blocklist regularly.
  • Granularity vs Coverage: ASN blocking is more precise than country blocking but requires more maintenance.

Combining Classification Headers with Other Security Patterns

Classification headers become even more powerful when combined with the security techniques from earlier in this series:

Enhanced Rate Limiting

Layer rate limiting (from Varnish 102) with abuse scores for dynamic protection:
sub vcl_recv {
...
    # More aggressive rate limiting for suspicious IPs
    if (std.integer(req.http.client-abuse-score, 0) > 15) {
        if (vsthrottle.is_denied("suspicious:" + req.http.X-Client-IP, 10, 30s, 120s)) {
            return (synth(429, "Rate limit exceeded for suspicious traffic"));
        }
    }
...
}

Path Protection with Geofencing

Combine HTTP Basic Auth (from Varnish 101) with geographic restrictions:
sub vcl_recv {
...
    # Require authentication for admin from outside trusted countries
    if (req.url ~ "^/admin") {
        if (req.http.client-country && req.http.client-country !~ "(?i)^(US|CA|GB)$") {
            # Non-trusted country accessing admin - require auth
            if (!req.http.Authorization ||
                req.http.Authorization !~ "^Basic YWRtaW46YWRtaW4=$") {
                return (synth(401, "Authentication Required"));
            }
        }
    }
...
}

Conclusion

These advanced classification header techniques complete our Varnish security series. Combined with HTTP Basic Authentication (Varnish 101), rate limiting (Varnish 102), and cache optimization (Varnish 103), you now have a comprehensive toolkit for building secure, high-performance applications on Upsun. By leveraging classification headers to block malicious traffic based on abuse scores, geographic location, and network operators, you can protect your applications at the edge before threats ever reach your backend infrastructure. Start with conservative thresholds, monitor your traffic patterns, and adjust based on real-world behavior to find the right balance between security and accessibility for your specific use case.
Last modified on April 14, 2026