Preflighting for paywall

Use a custom Paywall header to trigger preflight requests to authenticate every article view with a backend paywall service.


Use this solution in your VCL service (click RUN below to test this solution or clone it to make changes):


Use this solution in your Compute@Edge service:

  1. Rust
// Ingest cookies
if let Some(req_cookie_jar) = req
.and_then(|h| parse_cookies_to_jar(&h).ok())
// Getting a session ID from a cookie is rather simplistic in this
// demo. For a more advanced solution, combine the paywall pattern
// with a JSON web token verification at the edge
.map(|c| c.value())
// Only GETs and HEADs are considered for paywall restriction.
if !matches!(req.get_method(), &Method::GET | &Method::HEAD) {
return Ok(req.send(CONTENT_BACKEND)?);
let _ = req.take_body(); // request bodies will not be forwarded.
// run the content request
let mut content_resp = req.send(CONTENT_BACKEND)?;
// if 403 or 5xx - don't continue
if content_resp.get_status() == 403 || content_resp.get_status().is_server_error() {
return Ok(content_resp);
// If the response contains `paywall` header we run a check against the URL
let paywall_hdr = match content_resp.remove_header("paywall") {
None => {
// No paywall header received, deliver the content
return Ok(content_resp);
Some(paywall_hdr) => paywall_hdr,
let paywall_url = Url::parse(paywall_hdr.to_str()?)?;
if paywall_url.host_str() != Some("") {
// Not an expected paywall, deliver the content as is
return Ok(content_resp);
// First, re-create a request with original headers we received from client
let mut paywall_req = content_resp.take_backend_request().unwrap();
// run the request against the paywall and get a response
let paywall_resp = paywall_req.send(PAYWALL_BACKEND)?;
// Check the paywall response
if let Some(result_value) = paywall_resp.get_header("paywall-result") {
if result_value == "BLOCK" {
// if answer is `BLOCK` - deliver an error
return Ok(Response::from_body(
"<p>This content is only available to premium subscribers.</p>\n",
let meta_header = &HeaderName::from_static("paywall-meta");
if let Some(paywall_meta) = paywall_resp.get_header(meta_header) {
content_resp.set_header(meta_header, paywall_meta.clone());