vcl_deliver
The built-in vcl_deliver
subroutine is executed before the first byte of the response is emitted to the client. Deliver happens on every response individually, including responses delivered from cache and those received from a backend, making it an ideal place to add debugging information or user-specific session data that cannot be shared with other users.
Common uses for the deliver subroutine are:
- making changes for each specific user, such as adding a
Set-Cookie
header - adding CORS or other performance or security related HTTP headers
- performing changes to the TCP connection, such as enabling BBR and/or manipulating
client.socket.cwnd
- if required, restarting to jump back to
vcl_recv
Clustering handoff
To support caching at Fastly's scale, objects are stored across numerous physical servers and each individual VCL flow is processed by up to two machines. The (random) server that initially handles the client request (in vcl_recv
) and ultimately delivers the response (here in vcl_deliver
) is the delivery node. However, if clustering is enabled, the server that is responsible for fetching the object from the backend and storing it is a different machine known as the fetch node. When a request is eligible for clustering, it is handed off from the delivery node to the fetch node immediately after hash
(provided there is no local cache hit on the delivery node). The request is passed back to the delivery node immediately before deliver
. Because the fetch node is not responsible for the client request, any changes made to the req
object while a request is on the fetch node will not be retained when control moves into the deliver
stage.
Clustering is disabled automatically if there is a hit on the delivery node, after a restart
, or if you return(pass)
or error
from vcl_recv
. It can also be disabled manually by setting the Fastly-No-Shield
HTTP header to "1"
in vcl_recv
. Where clustering is disabled, all VCL flow stages happen on the delivery node and share state.
State transitions
vcl_deliver |
To see this subroutine in the context of the full VCL flow, see using VCL.
Example
The code example Enable modern web security headers to all responses is a good example of the vcl_deliver
subroutine in use:
Tokens available in this subroutine
The following limited-scope VCL functions and variables are available for use in this subroutine (those in bold are available only in this subroutine, those available in *all* subroutines are not listed):
- bereq.body_bytes_written
- bereq.bytes_written
- bereq.header_bytes_written
- client.socket.congestion_algorithm
- client.socket.cwnd
- client.socket.nexthop
- client.socket.pace
- client.socket.ploss
- client.socket.tcp_info
- client.socket.tcpi_advmss
- client.socket.tcpi_bytes_acked
- client.socket.tcpi_bytes_received
- client.socket.tcpi_data_segs_in
- client.socket.tcpi_data_segs_out
- client.socket.tcpi_delivery_rate
- client.socket.tcpi_delta_retrans
- client.socket.tcpi_last_data_sent
- client.socket.tcpi_max_pacing_rate
- client.socket.tcpi_min_rtt
- client.socket.tcpi_notsent_bytes
- client.socket.tcpi_pacing_rate
- client.socket.tcpi_pmtu
- client.socket.tcpi_rcv_mss
- client.socket.tcpi_rcv_rtt
- client.socket.tcpi_rcv_space
- client.socket.tcpi_rcv_ssthresh
- client.socket.tcpi_reordering
- client.socket.tcpi_rtt
- client.socket.tcpi_rttvar
- client.socket.tcpi_segs_in
- client.socket.tcpi_segs_out
- client.socket.tcpi_snd_cwnd
- client.socket.tcpi_snd_mss
- client.socket.tcpi_snd_ssthresh
- client.socket.tcpi_total_retrans
- esi.allow_inside_cdata
- fastly_info.h2.is_push
- fastly_info.h2.stream_id
- obj.age
- obj.entered
- obj.hits
- obj.is_pci
- obj.lastuse
- quic.cc.cwnd
- quic.cc.ssthresh
- quic.num_bytes.received
- quic.num_bytes.sent
- quic.num_packets.ack_received
- quic.num_packets.decryption_failed
- quic.num_packets.late_acked
- quic.num_packets.lost
- quic.num_packets.received
- quic.num_packets.sent
- quic.rtt.latest
- quic.rtt.minimum
- quic.rtt.smoothed
- quic.rtt.variance
- req.backend.ip
- req.backend.name
- req.backend.port
- req.body_bytes_read
- req.bytes_read
- req.digest.ratio
- req.esi
- req.esi_level
- req.is_ipv6
- req.is_purge
- resp.http.{NAME}
- resp.is_locally_generated
- resp.proto
- resp.response
- resp.status
- resp.tarpit
- time.end
- time.end.msec
- time.end.msec_frac
- time.end.sec
- time.end.usec
- time.end.usec_frac
- time.to_first_byte
- tls.client.certificate.dn
- tls.client.certificate.is_cert_bad
- tls.client.certificate.is_cert_expired
- tls.client.certificate.is_cert_missing
- tls.client.certificate.is_cert_unknown
- tls.client.certificate.is_unknown_ca
- tls.client.certificate.is_verified
- tls.client.certificate.issuer_dn
- tls.client.certificate.not_after
- tls.client.certificate.not_before
- tls.client.certificate.raw_certificate_b64
- tls.client.certificate.serial_number
- tls.client.cipher
- tls.client.ciphers_list
- tls.client.ciphers_list_sha
- tls.client.ciphers_list_txt
- tls.client.ciphers_sha
- tls.client.handshake_sent_bytes
- tls.client.iana_chosen_cipher_id
- tls.client.ja3_md5
- tls.client.ja4
- tls.client.protocol
- tls.client.servername
- tls.client.tlsexts_list
- tls.client.tlsexts_list_sha
- tls.client.tlsexts_list_txt
- tls.client.tlsexts_sha
- transport.bw_estimate
- transport.type
- waf.anomaly_score
- waf.blocked
- waf.counter
- waf.executed
- waf.failures
- waf.http_violation_score
- waf.lfi_score
- waf.logdata
- waf.logged
- waf.message
- waf.passed
- waf.php_injection_score
- waf.rce_score
- waf.rfi_score
- waf.rule_id
- waf.session_fixation_score
- waf.severity
- waf.sql_injection_score
- waf.xss_score