# Browser as a CDN

Source: https://zshot-cli.com/examples/browser-as-a-cdn/
Markdown: https://zshot-cli.com/examples/browser-as-a-cdn/index.md
Section: [Examples](https://zshot-cli.com/examples/)
License: [Enterprise](https://zshot-cli.com/support/licenses/enterprise)

 You can use the ZShot server to act as a caching CDN for signed browser render requests. An initial fetch renders the content in a warm browser, and identical renders are served from disk. The server also supports expiring entries upon demand.

Start the server with caching and URL signing enabled. The [Docker image](https://hub.docker.com/r/scaleassembly/zshot-server) reads signing and the token from the environment; the cache flags append as arguments:

**Local** — `zshot --server`

```sh
zshot --server \
  --server-bind-port 3000 \
  --server-cache \
  --server-cache-ttl 300 \
  --server-signed-url hmac-sha256:s3cr3t \
  --server-signed-url-max-age 300 \
  --server-token-auth purg3-t0k3n
```

**Docker** — `scaleassembly/zshot-server`

```sh
docker run --rm -p 3000:3000 \
  -e ZSHOT_LICENSE="MY-ZSHOT-LICENSE-KEY" \
  -e ZSHOT_SERVER_SIGNED_URL="hmac-sha256:s3cr3t" \
  -e ZSHOT_SERVER_SIGNED_URL_MAX_AGE="300" \
  -e ZSHOT_SERVER_TOKEN_AUTH="purg3-t0k3n" \
  scaleassembly/zshot-server:latest \
  --server-cache --server-cache-ttl 300
```

## Distribute a signed URL

This example server distributes signed URLs for [a menu board](https://zshot-cli.com/example_assets/daves-dog-depot/#anniversary):

```sh
SIGNED_URL=$(python3 - <<'EOF'
import hashlib, hmac, time, urllib.parse
params = {
    "url": "https://zshot-cli.com/example_assets/daves-dog-depot/#anniversary",
    "output_type": "webm",
    "video_duration": "16",
    "video_framerate": "24",
    "browser_width": "1920",
    "browser_height": "1128",
    "video_width": "1280",
    "video_height": "752",
    "wait_for": "js:menu:ready",
    "expires": str(int(time.time()) + 300),
}
canonical = "&".join(
    "%s=%s" % (k, urllib.parse.quote(v, safe=""))
    for k, v in sorted(params.items())
)
signature = hmac.new(b"s3cr3t", canonical.encode(), hashlib.sha256).hexdigest()
print("http://127.0.0.1:3000/?" + canonical + "&signature=" + signature)
EOF
)
```

The signs need no credentials: the signature authorizes the fetch, and an unsigned request gets `401`. The request returns a WEBM capture; every repeat returns in milliseconds from the cache, marked by the `age` header:

```sh
curl -sD- -o board.webm "$SIGNED_URL"   # renders the board
curl -sD- -o board.webm "$SIGNED_URL"   # served from cache
```

```
HTTP/1.1 200 OK
cache-control: public, max-age=300
etag: "sha-256=:gTUKIp2NfEIBx87zKqnqs5DOOWZC+AqTzADwwZi2y/0=:"
age: 0
```

Entries stay fresh for `--server-cache-ttl` seconds.

## Expire the cache

Cache entries can be purged with a `DELETE` of the same render arguments, sent as query parameters or a JSON body. Purging follows the GET auth rules, so an anonymous `DELETE` gets `401`; the Bearer token from `--server-token-auth` authorizes it:

```sh
curl -X DELETE http://127.0.0.1:3000/ \
  -H 'Authorization: Bearer purg3-t0k3n' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://zshot-cli.com/example_assets/daves-dog-depot/#anniversary","output_type":"webm","video_duration":16,"video_framerate":24,"browser_width":1920,"browser_height":1128,"video_width":1280,"video_height":752,"wait_for":"js:menu:ready"}'
```

`204` confirms a fresh entry was expired; a repeat returns `410` because nothing is cached. A bare `DELETE /` will purge the entire cache.

Expected output: [browser-as-a-cdn.webm](/example-renders/browser-as-a-cdn.webm) — Dave's Dog Depot anniversary menu board, served from the render cache

## Related

Flags:

- [`--server`](https://zshot-cli.com/reference/flags/server/server/)
- [`--server-bind-port`](https://zshot-cli.com/reference/flags/server/server-bind-port/)
- [`--server-cache`](https://zshot-cli.com/reference/flags/server/server-cache/)
- [`--server-cache-ttl`](https://zshot-cli.com/reference/flags/server/server-cache-ttl/)
- [`--server-signed-url`](https://zshot-cli.com/reference/flags/server/server-signed-url/)
- [`--server-signed-url-max-age`](https://zshot-cli.com/reference/flags/server/server-signed-url-max-age/)
- [`--server-token-auth`](https://zshot-cli.com/reference/flags/server/server-token-auth/)

Topics:

- [API](https://zshot-cli.com/reference/server/api/)
- [WebM](https://zshot-cli.com/reference/formats/webm/)

## Navigation

- [Examples](https://zshot-cli.com/examples/)
  - [Webpage as PNG](https://zshot-cli.com/examples/hello-from-zshot/)
  - [Full-page phone screenshot](https://zshot-cli.com/examples/full-page-phone/)
  - [Wait for a JavaScript event](https://zshot-cli.com/examples/wait-for-event/)
  - [Rendered HTML](https://zshot-cli.com/examples/html-output/)
  - [Tablet screenshot](https://zshot-cli.com/examples/tablet-screenshot/)
  - [Accessible PDF](https://zshot-cli.com/examples/accessible-pdf/)
  - [WebM video](https://zshot-cli.com/examples/webm-video/)
  - [Animated GIF](https://zshot-cli.com/examples/animated-gif/)
  - [Dark mode](https://zshot-cli.com/examples/dark-mode/)
  - [Emulate geolocation](https://zshot-cli.com/examples/geolocation/)
  - [Log in, then capture](https://zshot-cli.com/examples/navigate-login/)
  - [Log in, then extract JSON](https://zshot-cli.com/examples/sales-report-json/)
  - [Upload a file, download the result](https://zshot-cli.com/examples/enhance-leads/)
  - [Crawl a site into a WACZ](https://zshot-cli.com/examples/crawl-links/)
  - [Archive a sitemap into a WACZ](https://zshot-cli.com/examples/wacz-sitemap/)
  - [Run an API server](https://zshot-cli.com/examples/api-server/)
  - [Signed URL support](https://zshot-cli.com/examples/menu-board/)
  - **Browser as a CDN**
- [Download](https://zshot-cli.com/download/)
- [Purchase](https://zshot-cli.com/purchase/)
- [Reference](https://zshot-cli.com/reference/)
- [Support](https://zshot-cli.com/support/)
- [Changelog](https://zshot-cli.com/changelog/)

Site index: https://zshot-cli.com/llms.txt

