VCL counters and limiters

This information is part of a limited availability release. For additional details, read our product and feature lifecycle descriptions.

Fastly provides two data types in VCL that act as containers to keep track of elements and their count: counters and limiters. They can be used to do things like implement rate limiting to protect origin servers from abuse.

Counters and limiters normally operate at the edge and synchronize counts within a Fastly POP. You can also use counters and limiters in a shield POP. Counting done at the shield may result in reduced accuracy depending on traffic volumes. We currently limit you to 5 counters per service. Contact support to discuss increasing this limit.

Counter

The counter data type allows you to define VCL counters with various parameters. Once defined, counters can be used with the counter.inc() and counter.get() functions to increment and fetch the current value respectively. For example:

counter counter_login {
.type = "estimated";
.time_window = 60s;
}

The counter data type has the following fields:

FieldDescription
.typeThe type of the counter. The only valid type is estimated.
.time_windowThe duration of time during which the counter will accumulate increments. Applicable only for estimated counters. Minimum is 60s (1m) and maximum 3600s (1h). When a window elapses, some of the counts accumulated are carried over to the next window.

counter.get()

Returns the state of the counter as an INTEGER. The integer returned will reflect the current value of the counter for the key specified.

Example:

counter my_counter { .time_window = 60s; .type = "estimated"; }
sub vcl_deliver {
set resp.http.my_counter_val = counter.get(my_counter, "deliver");
}

counter.inc()

Returns the incremented state of the counter as an INTEGER. The integer returned will reflect the current value of the counter for the key specified.

Example:

counter my_counter { .time_window = 60s; .type = "estimated"; }
sub vcl_deliver {
set resp.http.my_incr_counter_val = counter.inc(my_counter, "deliver");
}

Limiter

The limiter data type operates similarly to counters, allowing you to define a container that keeps track of a count. Once defined, limiters can be used with the limiter.inc() and limiter.get() functions to increment and fetch the current value respectively. For example:

limiter limiter_login {
.max = 50;
.time_window = 60s;
}

The limiter data type has the following fields:

FieldDescription
.maxMaximum value allowed for this rate limiter.
.time_windowThe duration of time during which the counter will accumulate increments. Minimum is 60s (1m), maximum 3600s (1h).

limiter.inc()

Returns the state of the rate limiter as a BOOL value that is false if the count exceeds the specified threshold and true otherwise.

Example:

If my_limiter is above the defined threshold, true will be returned. If my_limiter is under the defined threshold, false will be returned.

limiter my_limiter { .time_window = 60s; .max = 50; }
sub vcl_deliver {
set resp.http.limiter_state = if(limiter.get(my_limiter, client.ip), "under", "over");
}

limiter.get()

Returns the incremented state of the rate limiter. It will return a BOOL value that is true if the count exceeds the specified threshold and false otherwise

Example:

If my_limiter is above the defined threshold, true will be returned. If my_limiter is under the defined threshold, false will be returned.

limiter my_limiter { .time_window = 60s; .max = 50; }
sub vcl_deliver {
set resp.http.limiter_state = if(limiter.inc(my_limiter, client.ip), "under", "over");
}

Use case: rate limiting

Counters and limiters allow you to implement rate limiters to protect origin servers from abuse. In particular, a counter window's "carry over" behavior allows you to approximate continuous time windows. This makes the .time_window() function particularly useful for things like determining rate limits because its value does not experience a sudden drop at the expiration of each window.

Taking advantage of this behavior, you could obtain a message rate (in requests per second) by dividing the total count seen within a .time_window() by the length of the window. You could then check the total number of requests seen within the window to help protect against different types of excessive traffic attacks.