---
name: "vcl-step"
section: "7"
platform: "linux"
package: "vinyl-cache"
version: "9.1.0-1"
release: "Arch Linux 2026-11-01"
platforms: ["linux"]
url: "https://new.carta.tech/vcl-step"
---

# vcl-step(7)

Built-in subroutines

## NAME

VCL-steps - Built-in subroutines

## DESCRIPTION

Various built-in subroutines are called during processing of client and backend requests as well as upon **vcl.load** and **vcl.discard**.

See *reference-states* for a detailed graphical overview of the states and how they relate to core code functions and VCL subroutines.

Built-in subroutines always terminate with a **return (\<action\>)**, where **\<action\>** determines how processing continues in the request processing state machine.

The behaviour of actions is identical or at least similar across subroutines, so differences are only documented where relevant.

Common actions are documented in *vcl\_actions* in the next section. Actions specific to only one or some subroutines are documented in *vcl\_steps*.

A default behavior is provided for all *reference-states* in the *vcl-built-in-code* code.

## VCL ACTIONS

Actions are used with the **return(\<action\>)** keyword, which returns control from subroutines back to **vinyld**. The action determines how processing in **vinyld** continues as shown in *reference-states*.

Common actions are documented here, while additional actions specific to only one or some subroutines are documented in the next section *vcl\_steps* as well as which action can be used from which built in subroutine.

### Common actions for the client and backend side

### fail(err)

Transition to *vcl\_synth* on the client side as for **return(synth(503, "VCL Failed"))**, but with any request state changes undone as if **std.rollback()** was called and forcing a connection close. The optional string argument *err* is logged to VSL under a *VCL\_Error* tag.

Returning *fail* from *vcl\_synth* sends a minimal response with a 500 status.

Intended for fatal errors, for which only minimal error handling is possible.

### Common actions for the client side

### synth(status code, reason)

Transition to *vcl\_synth* with **resp.status** and **resp.reason** being preset to the arguments of **synth()**.

### pass

Switch to pass mode, making the current request not use the cache and not putting its response into it. Control will eventually pass to *vcl\_pass*.

### pipe

Switch to pipe mode. Control will eventually pass to *vcl\_pipe*.

### restart

Restart the transaction. Increases the **req.restarts** counter.

If the number of restarts is higher than the *max\_restarts* parameter, control is passed to *vcl\_synth* as for **return(synth(503, "Too many restarts"))**

For a restart, all modifications to **req** attributes are preserved except for **req.restarts** and **req.xid**, which need to change by design.

### Common actions for the backend side

### abandon

Abandon the backend request. Unless the backend request was a background fetch, control is passed to *vcl\_synth* on the client side with **resp.status** preset to 503.

## VCL STEPS

### Client side

### vcl\_recv

Called at the beginning of a request, after the complete request has been received and parsed, after a *restart* or as the result of an ESI include.

Its purpose is to decide whether or not to serve the request, possibly modify it and decide on how to process it further. A backend hint may be set as a default for the backend processing side.

The *vcl\_recv* subroutine may terminate with calling **return()** on one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
pass
see pass section above
pipe
see pipe section above
hash
Continue processing the object as a potential candidate for
caching. Passes the control over to vcl_hash.
purge
Purge the object and it's variants. Control passes through
vcl_hash to vcl_purge.
vcl(label)
Switch to vcl labelled label.
This will roll back the request as if std.rollback(req) was
called and continue vcl processing in vcl_recv of the vcl
labelled label as if it was the active vcl.
The vcl(label) return is only valid while the req.restarts
count is zero and if used from the active vcl.
See the ref_cli_vcl_label command in vinyl-cli(7).
```

### vcl\_pipe

Called upon entering pipe mode. In this mode, the request is passed on to the backend, and any further data from both the client and backend is passed on unaltered until either end closes the connection. Basically, Vinyl Cache will degrade into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, no other VCL subroutine will ever get called after *vcl\_pipe*.

The *vcl\_pipe* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
pipe
Proceed with pipe mode.
```

### vcl\_pass

Called upon entering pass mode. In this mode, the request is passed on to the backend, and the backend's response is passed on to the client, but is not entered into the cache. Subsequent requests submitted over the same client connection are handled normally.

The *vcl\_pass* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
fetch
Proceed with pass mode - initiate a backend request.
```

### vcl\_hash

Called after *vcl\_recv* to create a hash value for the request. This is used as a key to look up the object in Vinyl Cache.

The *vcl\_hash* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see  fail section above
lookup
Look up the object in cache.
Control passes to vcl_purge when coming from a purge
return in vcl_recv.
Otherwise control passes to the next subroutine depending on the
result of the cache lookup:
* a hit: pass to vcl_hit
* a miss or a hit on a hit-for-miss object (an object with
obj.uncacheable == true): pass to vcl_miss
* a hit on a hit-for-pass object (for which pass(DURATION) had been
previously returned from vcl_backend_response): pass to
vcl_pass
```

### vcl\_purge

Called after the purge has been executed and all its variants have been evicted.

The *vcl\_purge* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
```

### vcl\_miss

Called after a cache lookup if the requested document was not found in the cache or if *vcl\_hit* returned **fetch**.

Its purpose is to decide whether or not to attempt to retrieve the document from the backend. A backend hint may be set as a default for the backend processing side.

The *vcl\_miss* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
pass
see pass section above
fetch
Retrieve the requested object from the backend. Control will
eventually pass to vcl_backend_fetch.
```

### vcl\_hit

Called when a cache lookup is successful. The object being hit may be stale: It can have a zero or negative *ttl* with only *grace* or *keep* time left.

The *vcl\_hit* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
pass
see pass section above
deliver
Deliver the object. If it is stale, a background fetch to refresh
it is triggered.
```

### vcl\_deliver

Called before any object except a *vcl\_synth* result is delivered to the client.

The *vcl\_deliver* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
synth(status code, reason)
see synth section above
restart
see restart section above
deliver
Deliver the object to the client.
```

### vcl\_synth

Called to deliver a synthetic object. A synthetic object is generated in VCL, not fetched from the backend. Its body can be constructed by assigning a string or blob to **resp.body** using **=**, or by appending a string or blob to **resp.body** using **+=**, as in these examples:

```
- ``set resp.body = "string";``
- ``set resp.body = "string " + "and another";``
- ``set resp.body += " appending " + "more strings";``
- ``set resp.body = :blob:;``
- ``set resp.body += :blobappend==:;``
```

A *vcl\_synth* defined object never enters the cache, contrary to a *vcl\_backend\_error* defined object, which may end up in cache.

The subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
restart
see restart section above
deliver
Directly deliver the object defined by vcl_synth to the client
without calling vcl_deliver.
```

### Backend Side

### vcl\_backend\_fetch

Called before sending the backend request. In this subroutine you typically alter the request before it gets to the backend.

The *vcl\_backend\_fetch* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
abandon
see abandon section above
fetch
Fetch the object from the backend.
error(status code, reason)
Transition to vcl_backend_error with beresp.status and
beresp.reason being preset to the arguments of error() if
arguments are provided.
```

Before calling *vcl\_backend\_fetch*, Vinyl Cache core prepares the *bereq* backend request as follows:

- •

  Unless the request is a *pass*,

  - •

    set **bereq.method** to **GET** and **bereq.proto** to **HTTP/1.1** and

  - •

    set **bereq.http.Accept\_Encoding** to **gzip** if *ref\_param\_http\_gzip\_support* is enabled.

- •

  If there is an existing cache object to be revalidated, set **bereq.http.If-Modified-Since** from its **Last-Modified** header and/or set **bereq.http.If-None-Match** from its **Etag** header

- •

  Set **bereq.http.X-Vinyl** to the current transaction id (*vxid*)

These changes can be undone or modified in *vcl\_backend\_fetch* before the backend request is issued.

In particular, to cache non-GET requests, **req.method** needs to be saved to a header or variable in *vcl\_recv* and restored to **bereq.method**. Notice that caching non-GET requests typically also requires changing the cache key in *vcl\_hash* e.g. by also hashing the request method and/or request body.

HEAD request can be satisfied from cached GET responses.

### vcl\_backend\_refresh

This subroutine is called after a legitimate 304 response is received from the backend so that you can chose how to handle the object revalidation.

The default is to apply the RFC logic which is to merge the new headers with the stale ones.

The returned value only affects response headers, the body that is delivered is always the one from the stale object.

See *vcl-built-in-refresh* for additional explanations.

```
merge
Merge the headers we got from the backend response with
the ones we had in the cached object.
obj_stale
Return the stale object's headers without performing the RFC merge logic.
beresp
Return the beresp headers as they were at the end of this subroutine
without executing the RFC merge logic. Any content-length and
content-encoding headers present in the 304 beresp will be removed and
replaced by the stale object ones if available. Etag and Last-Modified
headers are also copied from the stale object to allow future revalidations.
fail
see fail section above
abandon
see abandon section above
retry
Retry the backend transaction. Increases the retries counter.
If the number of retries is higher than max_retries,
control will be passed to vcl_backend_error.
error(status code, reason)
Transition to vcl_backend_error with beresp.status and
beresp.reason being preset to the arguments of error() if
arguments are provided.
```

### vcl\_backend\_response

Called after the response headers have been successfully retrieved from the backend.

The *vcl\_backend\_response* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
abandon
see abandon section above
deliver
For a 304 response, create an updated cache object.
Otherwise, fetch the object body from the backend and initiate
delivery to any waiting client requests, possibly in parallel
(streaming).
retry
Retry the backend transaction. Increases the retries counter.
If the number of retries is higher than max_retries,
control will be passed to vcl_backend_error.
pass(duration)
Mark the object as a hit-for-pass for the given duration. Subsequent
lookups hitting this object will be turned into passed transactions,
as if vcl_recv had returned pass.
error(status code, reason)
Transition to vcl_backend_error with beresp.status and
beresp.reason being preset to the arguments of error() if
arguments are provided.
```

### vcl\_backend\_error

This subroutine is called if we fail the backend fetch, if *max\_retries* has been exceeded, or if explicitly transitioned to with **return(error(status code,** **reason))**.

Returning with *abandon* does not leave a cache object.

If returning with **deliver**, a synthetic body can be constructed by assigning a string or blob to **beresp.body** using **=**, or by appending a string or blob to **beresp.body** using **+=**, as in these examples:

```
- ``set beresp.body = "string";``
- ``set beresp.body = "string " + "and another";``
- ``set beresp.body += " appending " + "more strings";``
- ``set beresp.body = :blob:;``
- ``set beresp.body += :blobappend==:;``
```

For **return(deliver)** with **beresp.uncacheable == false**, the constructed object is cached, so be cautious with putting private information in headers or the body of such a cached object. When in doubt, explicitly set **beresp.uncacheable** to **true** in **vcl\_backend\_error** to avoid caching.

The *vcl\_backend\_error* subroutine may terminate with calling **return()** with one of the following keywords:

```
fail(err)
see fail section above
abandon
see abandon section above
deliver
Deliver and possibly cache the object defined in
vcl_backend_error as if it was fetched from the backend, also
referred to as a "backend synth".
retry
Retry the backend transaction. Increases the retries counter.
If the number of retries is higher than max_retries,
vcl_synth on the client side is called with resp.status
preset to 503.
```

### During vcl.load / vcl.discard

### vcl\_init

Called when VCL is loaded, before any requests pass through it. Typically used to initialize VMODs.

The *vcl\_init* subroutine may terminate with calling **return()** with one of the following keywords:

```
ok
Normal return, VCL continues loading.
fail(err)
Abort loading of this VCL.
```

### vcl\_fini

Called when VCL is discarded only after all requests have exited the VCL. Typically used to clean up VMODs.

The *vcl\_fini* subroutine may terminate with calling **return()** with one of the following keywords:

```
ok
Normal return, VCL will be discarded.
```

## SEE ALSO

- •

  *[vinyld(1)](/vinyld/1.md)*

- •

  *[vcl(7)](/vcl/7.md)*

## COPYRIGHT

This document is licensed under the same license as Vinyl Cache itself. See LICENSE for details.

- •

  Copyright (c) 2006 Verdens Gang AS

- •

  Copyright (c) 2006-2021 Varnish Software AS
