assets: icons, manifest, social cards, 404
assets checks the files around your pages that browsers, phones and social networks look
for: the favicon, the Apple touch icon, the web app manifest and its icons, the Open Graph image
shown when someone shares a link, and the page served for missing URLs. These break quietly: a
renamed icon or a relative og:image looks fine in the browser and only shows up as a blank
tab icon or a card without a picture. It is not in the default set.
Nothing to install. It reads the build (dist/) and starts no server or browser. Set the
top-level siteUrl so absolute og:image URLs on your site are checked against the build and
og:url is checked against your origin.
With render.mode on, it checks the DOM a browser
renders instead of the shipped HTML file.
Run it
Section titled “Run it”npx vidimus assetsWhat it checks
Section titled “What it checks”Pages matched by the top-level exclude or assets.exclude are skipped; by default that is
the 404 page. Each check can be turned off on its own.
Favicon (assets.favicon)
Section titled “Favicon (assets.favicon)”- Every
<link rel="icon">and<link rel="apple-touch-icon">on your site that points at a local file must resolve to a file in the build:icon /favicon.svg not found(error).rel="shortcut icon"counts asicon. - If no page has a
<link rel="icon">and there is no/favicon.icoin the build root:no favicon: add <link rel="icon"> or /favicon.ico(error). - If no page has a
<link rel="apple-touch-icon">and there is no/apple-touch-icon.png:no <link rel="apple-touch-icon"> on any page(warning).
Web app manifest (assets.manifest)
Section titled “Web app manifest (assets.manifest)”If no page links a manifest: no <link rel="manifest"> on any page (warning). Browsers need one
to install the site and to pick its name, icons and theme color. The checks below run for each
manifest a page links with <link rel="manifest">, and each finding lists the pages that link it.
- The file exists (
manifest /site.webmanifest not found) and parses as a JSON object (manifest /site.webmanifest is not valid JSON). - It has a
nameorshort_name(manifest … has no name or short_name). - It has a non-empty
iconsarray (manifest … has no icons), and each iconsrc, resolved relative to the manifest, exists in the build (manifest icon /icons/512.png not found). - At least one icon is 512 pixels or larger on its shorter side, from its declared
sizes("any"counts) or its measured size: otherwisemanifest … has no icon of at least 512x512(warning).
Open Graph and Twitter cards (assets.openGraph)
Section titled “Open Graph and Twitter cards (assets.openGraph)”Pages with <meta name="robots" content="noindex"> are skipped, since they are not meant to
be shared.
og:title,og:imageandtwitter:cardare present and not empty; each missing one is a warning such asmissing <meta property="og:image">. vidimus accepts the key in either thepropertyor thenameattribute.og:imageis an absolute URL (og:image "/og.png" is not an absolute URL, error). Social networks do not resolve relative URLs.- An
og:imageon your site exists in the build (og:image /og.png not found, error) and, when its size can be read (PNG, JPEG, GIF, WebP, AVIF, SVG), is at leastassets.ogImage:og:image /og.png is 800x418, smaller than 1200x630(warning). - With
siteUrlset,og:urlis on the same origin:og:url origin https://staging.example.com differs from siteUrl https://example.com(error).
404 page (assets.notFound)
Section titled “404 page (assets.notFound)”Without 404.html or 404/index.html in the build: no 404 page (404.html or 404/index.html)
(warning). Most static hosts serve that file for missing URLs.
Files the site turns out to need
Section titled “Files the site turns out to need”Some root files only matter for some sites: ads.txt for sites that show ads, a
change-password URL for sites with accounts, app association files for sites with an app.
vidimus looks for signs in the pages and checks the file only when a sign is there, so you
don’t need to know the file exists to be told you need it. Each option takes 'auto'
(default: check when a sign is found), 'on' (always check) or 'off'. A missing file is a
warning listing the pages that gave the sign away; an invalid one is an error.
| Option | Sign in the pages | File |
|---|---|---|
assets.adsTxt |
a publisher ad tag: AdSense, Google Publisher Tag, Amazon, Prebid, Xandr, Media.net, Ezoic, Mediavine, Raptive, Taboola, Outbrain, Criteo, PubMatic, Magnite, OpenX, Carbon | /ads.txt |
assets.changePassword |
an <input type="password"> or autocomplete="current-password"/"new-password" |
/.well-known/change-password |
assets.appleAppSiteAssociation |
<meta name="apple-itunes-app">, an ios-app:// alternate link, or "platform": "itunes" in the manifest’s related_applications |
/.well-known/apple-app-site-association |
assets.assetLinks |
<meta name="google-play-app">, an android-app:// alternate link, or "platform": "play" in related_applications |
/.well-known/assetlinks.json |
- ads.txt: every record must read
<domain>, <account id>, DIRECT|RESELLER[, <cert id>](ads.txt has malformed lines, with the line numbers), and there must be at least one. An AdSenseca-pub-…ID found in a page must have itsgoogle.com, pub-…line:ads.txt does not list AdSense publisher pub-…. Advertiser pixels such as Google Ads conversion tracking don’t count as ads. Anapp-ads.txtin the build is checked the same way; vidimus never asks for one, since no page can tell whether your app shows ads. - change-password (W3C): lets
password managers send users straight to the page where they change their password. It
counts when the build has the path as a file or folder, or
_redirectshas a rule from it. The fix suggests the first page with anautocomplete="new-password"field as target. A redirect set up on the server instead is invisible in the build: set the option to'off'. - apple-app-site-association:
.well-known/or the root, a JSON object with anapplinks,webcredentials,appclipsoractivitycontinuationsection. - assetlinks.json: a non-empty JSON array of statements with
relationandtarget.
These files live at the origin root, so with a siteUrl path (https://example.github.io/project/)
the checks are skipped with a log line.
Example output
Section titled “Example output”─── assets ──────────────────────────────────────────────────────
✖ og:image "/og/home.png" is not an absolute URL on: / → Use an absolute URL, e.g. <meta property="og:image" content="https://example.com/og/home.png">.
✖ manifest icon /icons/icon-512.png not found in: dist/site.webmanifest on: / /about/ /blog/ +9 more → Add /icons/icon-512.png to the build or fix its "src" in /site.webmanifest.
⚠ missing <meta name="twitter:card"> on: /about/ /blog/ /contact/ +8 more → Add <meta name="twitter:card" content="summary_large_image"> to the <head>.
⚠ no <link rel="apple-touch-icon"> on any page → Add a 180x180 PNG and <link rel="apple-touch-icon" href="/apple-touch-icon.png">.
✖ assets: 4 problem(s) across 12 page(s) (0.1s)A clean run ends with ✔ assets: 12 page(s), all assets in place.
Options
Section titled “Options”| Key | Default | Description |
|---|---|---|
assets.favicon |
true |
check favicon and Apple touch icon links |
assets.manifest |
true |
warn without a manifest, check linked ones and their icons |
assets.openGraph |
true |
check og:title, og:image, twitter:card and og:url |
assets.ogImage |
{ width: 1200, height: 630 } |
warn when og:image is smaller than this |
assets.notFound |
true |
warn without a 404 page |
assets.adsTxt |
'auto' |
check /ads.txt (and an existing /app-ads.txt) when a page shows ads |
assets.changePassword |
'auto' |
check /.well-known/change-password when a page has a password field |
assets.appleAppSiteAssociation |
'auto' |
check the iOS app association file when the site points at an iOS app |
assets.assetLinks |
'auto' |
check assetlinks.json when the site points at an Android app |
assets.exclude |
['^/404(\\.html|/)?$'] |
URL path patterns to skip |
Top-level keys used: siteUrl, exclude.
import { defineConfig } from 'vidimus';
export default defineConfig({ siteUrl: 'https://example.com', audits: ['i18n', 'csp', 'a11y', 'links', 'r12s', 'assets'], assets: { manifest: false, ogImage: { width: 1200, height: 600 }, exclude: ['^/404(\\.html|/)?$', '^/embed/'], },});Arrays replace, so repeat the 404 pattern when you add to assets.exclude.
Common fixes
Section titled “Common fixes”The head tags that pass every check:
<link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="icon" href="/favicon.ico" sizes="32x32"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest">
<meta property="og:title" content="Pricing and plans"><meta property="og:url" content="https://example.com/pricing/"><meta property="og:image" content="https://example.com/og/pricing.png"><meta name="twitter:card" content="summary_large_image">Build the og:image and og:url from the same site URL your framework uses for canonical
links (Astro.site in Astro, .Permalink or absURL in Hugo) so they stay absolute.
A minimal site.webmanifest, with a 512px icon:
{ "name": "Example", "short_name": "Example", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#0f172a", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } ]}Put the icons, the manifest, favicon.ico and apple-touch-icon.png (180x180) in the folder
your framework copies to the build as is, usually public/ (static/ in Hugo). Add a
404.html page there or as a route; see Frameworks for where each one expects
it.
These docs are deliberately built 8 times, with VitePress, Starlight, Hugo, Eleventy, Zola, mdBook, React and Angular, from the same markdown, and audited by vidimus as one site. Why.