# Website thumbnails from a URL: capture, size, format and caching, measured

> Make website thumbnails for directories and link cards: capture at desktop width, shrink after, pick WebP, and cache with a signed URL. Sizes measured.

Published 2026-11-08 · https://shareglint.com/en/guides/website-thumbnails

A website thumbnail is a small picture of a page's first screen: the preview in a link directory, a "sites we built" grid, a bookmark manager, a CMS field that shows where a link goes. Making one sounds like one screenshot, but three choices decide whether it looks right and loads fast: the window you capture in, the format, and where the shrinking and caching happen. This page goes through each with numbers we measured.

**How we measured.** On 2026-11-08 we captured the home pages of python.org, w3.org, nodejs.org, debian.org, rust-lang.org and mozilla.org through our screenshot API from our development stack (Free-plan captures, which carry a small badge), in PNG, JPEG and WebP, then shrank them with Pillow 11.1. Each capture took 4.8–8.9 s, most of it the sites' own loading.

## 1. Capture at desktop width, shrink afterwards

The tempting shortcut is to ask for a 400 px wide screenshot. Websites see a 400 px window as a phone and switch to their mobile layout: python.org captured at 400×250 showed a full-width menu (Close, Python, PSF, Docs, PyPI…) and nothing of its home page. Its responsive layout is doing what the [viewport meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/viewport) asks for.

So capture at the width people use the site at — our default viewport is 1280×800 — and scale the picture down. The viewport's height sets the crop: 1280×800 is 16:10; for 4:3 cards send `"viewport_height": 960`; for a square, 1280×1280.

## 2. WebP, and how much quality

Sizes at 1280×800, first screen only, cookie banners removed:

| Site | PNG | JPEG (q 85) | WebP (q 85) | WebP (q 60) |
|---|---|---|---|---|
| python.org | 133 KB | 105 KB | 56 KB | 38 KB |
| w3.org | 157 KB | 111 KB | 69 KB | 50 KB |
| nodejs.org | 256 KB | 112 KB | 62 KB | 43 KB |
| debian.org | 593 KB | 158 KB | 109 KB | 69 KB |
| rust-lang.org | 107 KB | 116 KB | 68 KB | 49 KB |
| mozilla.org | 58 KB | 77 KB | 35 KB | 27 KB |

WebP was the smallest for every page, at about half of JPEG. PNG is lossless and swings wildly: flat colours compress well (mozilla.org), photos and gradients do not (debian.org's banner, 593 KB). `quality` (1–100, default 85) applies to JPEG and WebP; 60 is fine for a picture that will be shown at a third of its size.

## 3. Shrink once, not in every browser

A 1280×800 picture in `<img width="400">` looks right, but every visitor downloads the full 56 KB. Shrunk to 400×250 first, the same WebP was 8.7 KB for python.org, 8.5 KB for nodejs.org and 13.6 KB for debian.org — six times smaller. With Pillow:

```python
from PIL import Image

img = Image.open("python.org.webp")       # the 1280×800 capture
img.thumbnail((400, 400))                 # keeps the aspect ratio: 400×250
img.save("python.org-400.webp", "WEBP", quality=80)
```

Do it where your images are processed anyway: when the link is added to your directory, in a build step, or through an image CDN that resizes by URL. For retina screens, keep an 800 px version too and offer both with `srcset` ([MDN: img](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img)). Our API does not shrink captures for you: `scale` goes up (1, 2, 3 for retina), never down. Pillow's `thumbnail()` is documented in the [Pillow reference](https://pillow.readthedocs.io/en/stable/reference/Image.html).

## 4. Caching: a signed URL in the page

For thumbnails shown straight from our side, put a signed `/s/` URL in the `<img>`. It is a normal GET that anyone can load, with your options in the query and a signature so nobody can change them:

```js
import { createHmac } from 'node:crypto';

const enc = (v) => encodeURIComponent(String(v))
  .replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());

export function thumbnailUrl(pageUrl) {
  const params = { url: pageUrl, block_cookie_banners: '1', viewport_height: '960' };
  const path = `/s/${process.env.SHOT_ACCOUNT_ID}.webp`;
  const query = Object.keys(params).sort()
    .map((name) => `${enc(name)}=${enc(params[name])}`).join('&');
  const s = createHmac('sha256', process.env.SHOT_SIGNING_SECRET)
    .update(`${path}?${query}`).digest('hex').slice(0, 32);
  return `https://shareglint.com${path}?${query}&s=${s}`;
}
// <img src={thumbnailUrl('https://www.w3.org/')} width="400" height="300" loading="lazy" alt="w3.org">
```

What that URL did for w3.org at 1280×960:

| Request | Answer | Time |
|---|---|---|
| First | 302 to the new capture (81 KB WebP) | 7.1 s |
| Second | 302 to the same file | 0.08 s |
| Third | 302 to the same file | 0.11 s |
| `viewport_height` changed to 961 by hand | 403 | — |

The first visitor waits for the capture. To spare them, request the URL once on your server when the link is added; every later view is a redirect to a stored file. Within 24 hours the same options never capture again, so a page viewed a thousand times a day costs one capture a day. The signature is computed with your signing secret on your server — never in browser code; [signed image URLs](https://shareglint.com/en/guides/signed-image-urls) explains the scheme, and [the JavaScript guide](https://shareglint.com/en/screenshot-api/javascript) shows the same function checked against our verifier.

`loading="lazy"` keeps a long grid from loading thumbnails nobody scrolls to, and the `width`/`height` attributes keep the layout from jumping while they load.

## Many URLs at once

A directory with 2,000 links needs 2,000 captures once, then only new links. Send them from a script with a few requests in parallel and webhooks for the results; [bulk website screenshots](https://shareglint.com/en/guides/bulk-website-screenshots) has the script and what concurrency did to the total time. A viewport capture costs 1 render of the quota; repeats within 24 hours are free ([pricing](https://shareglint.com/en/pricing)).

## A thumbnail as a card

For link-preview cards with the site's name and description next to the picture, render the capture into a template instead of showing it raw: `site-card` takes a title, a description and an image, and `browser-frame` puts the screenshot in a browser window. The card then has a fixed layout whatever the site looks like.

## Limits

- **Public pages only.** No logins, no intranet, no pages behind a bot wall.
- **First screen only** in a thumbnail; for the whole page see [full-page screenshots](https://shareglint.com/en/guides/full-page-screenshot-chrome).
- **A day old at most, not live.** A site that changes every hour shows the version captured first that day.
- **Some sites show pop-ups other than cookie banners** (subscription offers, sign-in boxes); `hide_selectors` removes them once you know their selector ([cookie banners and other pop-ups](https://shareglint.com/en/guides/hide-cookie-banners-in-screenshots)).

## FAQ

### How do I get a thumbnail image of a website from its URL?

Take a screenshot of the page's first screen at desktop width (1280 px), then shrink it to the size you display, for example 400 px wide. With our API, a signed /s/ URL in an img tag captures the page on the first request and serves the stored file afterwards; resize it in your own pipeline or image CDN.

### Why not just capture the page at thumbnail size?

Because a narrow window is a phone to the website. Captured with a 400 px viewport, python.org showed its mobile menu instead of its home page. Capture at the width people browse at and scale the picture down.

### Which format is best for website thumbnails?

WebP. On six home pages captured at 1280×800, WebP at quality 85 was 35–109 KB against 77–158 KB for JPEG and 58–593 KB for PNG; at quality 60 it was 27–69 KB. Shrunk to 400×250, the WebP thumbnails were 8–14 KB.

### How often are thumbnails refreshed?

The same URL with the same options returns the stored capture for 24 hours; the first request after that captures the page again. So a thumbnail is at most a day old, and you pay for at most one capture per page per day however many people view it.
