---
title: Subroutines
summary: null
url: https://www.fastly.com/documentation/reference/vcl/subroutines
---

[VCL services](https://www.fastly.com/documentation/guides/full-site-delivery/custom-vcl) do not have a single entry point or main function, but rather a number of predefined subroutines that are called at various stages of the request lifecycle. See [using VCL](https://www.fastly.com/documentation/guides/full-site-delivery/custom-vcl/developer-guide-using) to learn more about how and when each subroutine is called. It is also possible to define your own custom subroutines and call them from the built-in ones.

## Built-in (lifecycle) subroutines

The following subroutines are part of the [VCL lifecycle](https://www.fastly.com/documentation/guides/full-site-delivery/custom-vcl/developer-guide-using).
Each subroutine represents a different stage of the Varnish state machine.

- [vcl_deliver](https://www.fastly.com/documentation/reference/vcl/subroutines/deliver/)
- [vcl_error](https://www.fastly.com/documentation/reference/vcl/subroutines/error/)
- [vcl_fetch](https://www.fastly.com/documentation/reference/vcl/subroutines/fetch/)
- [vcl_hash](https://www.fastly.com/documentation/reference/vcl/subroutines/hash/)
- [vcl_hit](https://www.fastly.com/documentation/reference/vcl/subroutines/hit/)
- [vcl_log](https://www.fastly.com/documentation/reference/vcl/subroutines/log/)
- [vcl_miss](https://www.fastly.com/documentation/reference/vcl/subroutines/miss/)
- [vcl_pass](https://www.fastly.com/documentation/reference/vcl/subroutines/pass/)
- [vcl_recv](https://www.fastly.com/documentation/reference/vcl/subroutines/recv/)

The prefix `vcl_` is reserved and cannot be used in the names of custom subroutines.

## Custom subroutines

Custom subroutines are defined using the `sub` keyword followed by a name,
an optional parameter list, and an optional return type.
Subroutine parameter and return types many be any of the following:
ACL, BACKEND, BOOL, INTEGER, FLOAT, TIME, REGEX, RTIME, STRING, or IP.
The code for a subroutine is contained within brackets `{}`.

Defining a `VOID`-typed subroutine:

```vcl
sub compression_check {
  if (req.http.Accept-Encoding ~ "gzip|br") {
    set req.http.Compression-Accepted-By-Client = "yes";
  }
}
```

Subroutines may optionally have a [type](https://www.fastly.com/documentation/reference/vcl/types),
and return a value in accordance with their type:

```vcl
sub compression_matches BOOL {
  if (req.http.Accept-Encoding ~ "gzip|br") {
    return true;
  }
  return false;
}
```

If no type is supplied, the type is implicitly `VOID`.

Defining a `VOID`-typed subroutine with a parameter list:

```vcl
sub url_path_check(STRING var.path) {
  if (req.url.path == var.path) {
    set req.http.X-Is-Valid-Path = "yes";
  }
}

sub url_path_check_either(STRING var.path1, STRING var.path2) {
  if (req.url.path == var.path1 || req.url.path == var.path2) {
    set req.http.X-Is-Valid-Path = "yes";
  }
}
```

Defining subroutine with a parameter list that returns a value:

```vcl
sub url_path_is(STRING var.path) BOOL {
  if (req.url.path == var.path) {
    return true;
  }
  return false;
}

sub url_path_is_either(STRING var.path1, STRING var.path2) BOOL {
  if (req.url.path == var.path1 || req.url.path == var.path2) {
    return true;
  }
  return false;
}
```

### Calling a subroutine

> **IMPORTANT:** The validity of a subroutine to the compiler depends on where it is called from. A subroutine called from `vcl_recv` may contain only code that would be valid within the `vcl_recv` subroutine.

`VOID`-typed custom subroutines (those that do not return a value) are called using the `call` statement.
Calling a `VOID`-typed subroutine:

```vcl
sub vcl_recv {
  # set the compression-accepted-by-client header if applicable
  call compression_check;
  call compression_check(); # Equivalent to the call above.

  # Check if the request URL path matches a target path.
  call url_path_check("index.html");
  call url_path_check_either("index.html", "blog.html");
}
```

Typed subroutines other than `VOID` are called by placing them to the right hand side of an assignment followed by parentheses `()`.
Calling subroutines that return a value:

```vcl
sub vcl_recv {
  declare local var.compressed BOOL;
  set var.compressed = compression_matches();

  declare local var.is_home_page BOOL;
  set var.is_home_page = url_path_is("index.html");

  declare local var.is_valid_page BOOL;
  set var.is_valid_page = url_path_is_either("index.html", "blog.html");
}
```

The `call` keyword is only for VOID typed subroutines that contain isolated logic
you wish to run.
Typed subroutines other than VOID may not be called with the `call` keyword.

## Returning a value

`VOID` subroutines may blanket `return;` with no value.
These return directly from the subroutine to its caller.

Non-`VOID` subroutines must return a value in accordance
with their type.
These return directly from the subroutine to its caller,
and the return value is made available to the caller.

It is permitted to "fall off" the end of a subroutine
by not having a return statement, even for subroutines defined
to return a non-`VOID` value.
In this case, the value returned to the caller will be a default value.
These default values correspond to the default values for
[uninitialised local variables](https://www.fastly.com/documentation/reference/vcl/declarations/local-variables/)
of the same type:

```vcl
sub f INTEGER {
}

sub g {
  declare local var.i INTEGER;
  set var.i = f(); # f() returns 0
}
```

## Returning a state

In the [VCL lifecycle](https://www.fastly.com/documentation/guides/full-site-delivery/custom-vcl/developer-guide-using), built-in subroutines return a state indicating what Fastly should do next.

> **HINT:** Returning a state is different from returning a _value_. All subroutines can return a state. It's also possible for a typed custom subroutine to return a state instead of its declared type. Returning a state from a typed subroutine will still terminate the parent subroutine. For example, this custom subroutine is typed as a BOOL but returns a state which ends the processing of `vcl_recv`:
>
> ```vcl
> sub compression_matches BOOL {
>   if (req.http.Accept-Encoding ~ "gzip|br") {
>     return(lookup); # Ends the parent sub and proceeds to 'lookup' (assignment is cancelled)
>   }
>   return false; # Passes control back to parent sub and assigns the value false
> }
> ```

Custom subroutines may also return a state as if they were the built-in subroutine that called them. For instance, `vcl_recv` might call a custom subroutine that includes `return (lookup);` because "lookup" is a [valid return state of recv](https://www.fastly.com/documentation/reference/vcl/subroutines/recv/#state-transitions). This type of return acts as if it had been placed within the calling subroutine, and terminates processing of both the custom subroutine and the parent built-in subroutine.

Returning a state makes custom subroutines sensitive to where they are called from. If a custom subroutine contains a `return (fetch);` statement, it can no longer be called from any context other than `vcl_miss` (the only built-in subroutine to have a valid return state of `fetch`). The same applies to using [variables](https://www.fastly.com/documentation/reference/vcl/variables) scoped to specific subroutines within custom subroutines.

## Concatenation

Built-in subroutines with the same name are automatically concatenated in the order the compiler encounters them. Note that any statement that terminates processing of a subroutine (such as `return`, `restart` or `error`) will make any subsequently defined subroutines with the same name unreachable.

```vcl
sub vcl_recv {
  return (lookup);
}

sub vcl_recv {
  # this code won't run due to the return statement in the prior vcl_recv
  # if the order of these two subs were reversed, the origin would be set before returning
  set req.backend = origin;
}
```

Custom subroutines may not be concatenated. Multiple declarations of the same (non-built-in) name are not permitted.

## Recursion

Subroutines may not call themselves, either directly or via another subroutine. Even if the loop is not infinite, the compiler will still disallow it.

The following code causes an infinite loop and will be disallowed by the compiler:

```vcl
sub foo {
  call bar;
}

sub bar {
  call foo;
}
```

The following code cannot cause an infinite loop, but is still disallowed:

```vcl
sub foo {
  call bar;
}

sub bar {
  if (false) {
    call foo;
  }
}
```
