---
title: VCL request lifecycle
summary: null
url: https://www.fastly.com/documentation/reference/vcl/request-lifecycle
---


VCL does not run like a traditional program with a single entry point for your code. Instead, Fastly exposes built-in subroutines as *hooks* that execute at significant moments during each HTTP request's lifecycle. These hooks are where you can add your code.

This approach means your uploaded code functions as a *configuration* rather than a standalone application. Changes to your VCL can be generated automatically through the [Fastly control panel](/guides/getting-started/navigating-fastly/about-the-web-interface-controls), compiled, and distributed to all Fastly caches worldwide, all without requiring maintenance windows or service downtime.

Here are the main subroutines, in the order they run:

<!-- VclFlow component removed -->


|Name|Trigger point|Default return state|Alternative return states|
|----|-------------|--------------------|-------------------------|
|`vcl_recv`|Client request received|`lookup`[^recv-note] |`pass`, `error`, `restart`, `upgrade`|
|`vcl_hash`|A cache key will be calculated|`hash` [^hash-note] ||
|`vcl_hit`|An object has been found in cache|`deliver`|`pass`, `error`, `restart`|
|`vcl_miss`|Nothing was found in the cache, preparing backend fetch|`fetch`|`deliver_stale`, `pass`, `error`|
|`vcl_pass`|Cache bypassed, preparing backend fetch|`pass` [^pass-note]|`error`|
|`vcl_fetch`|Origin response headers received|`deliver` [^fetch-note]|`deliver_stale`, `pass`, `error`, `restart`|
|`vcl_error`|Error triggered (explicitly or by Fastly)|`deliver`|`restart`|
|`vcl_deliver`|Preparing to deliver response to client|`deliver`|`restart`|
|`vcl_log`|Finished sending response to client|`deliver`[^log-note]||

[^recv-note]: All return states from `vcl_recv` (except `restart`) pass through `vcl_hash` first. `return(lookup)` and `return(pass)` both move control to `vcl_hash` but flag the request differently, which will determine the exit state from `vcl_hash`.
[^hash-note]: The only possible return state from `vcl_hash` is `hash` but it will trigger different behavior depending on the earlier return state of `vcl_recv`. The default `return(lookup)` in `vcl_recv` will prompt Fastly to perform a cache lookup and run `vcl_hit` or `vcl_miss` after hash. If `vcl_recv` returns `error`, then `vcl_error` is executed after hash. If `vcl_recv` returns `return(pass)`, then `vcl_pass` is executed after hash. The hash process is required in all these cases to create a cache object to enable [hit-for-pass](/guides/concepts/edge-state/cache/request-collapsing/#hit-for-pass).
[^pass-note]: The `return(pass)` exit from `vcl_pass` triggers a backend fetch, similarly to `return(fetch)` in `vcl_miss` but the altered return state is a reminder that the object is flagged for pass, so that it cannot be cached when processed in `vcl_fetch`.
[^fetch-note]: Returning with `return(deliver)` from `vcl_fetch` cannot override an earlier pass, but `return(pass)` here will prevent the response being cached.
[^log-note]: The return state from `vcl_log` simply terminates request processing.

Some subroutines can return `error`, `restart`, or `upgrade`. Any `error` return state will result in the execution flow passing to `vcl_error`, while `restart` will result in the execution flow passing to `vcl_recv`. The special `upgrade` return state will terminate the VCL flow and create a managed WebSocket connection ([learn more](/guides/concepts/real-time-messaging/)).