{"id":15695,"date":"2026-09-06T00:25:16","date_gmt":"2026-09-05T22:25:16","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/image-delivery-format-negotiation-auditor\/"},"modified":"2026-09-06T00:25:16","modified_gmt":"2026-09-05T22:25:16","slug":"image-delivery-format-negotiation-auditor","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/","title":{"rendered":"Image Delivery &#038; Format Negotiation Auditor: WebP, AVIF, srcset, Vary"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">A page can carry the same picture in four ways: as a plain JPEG, as WebP served to everyone, as a negotiated file that depends on the Accept header, or as a picture element that lets the browser choose. Only the second and third are invisible in the markup \u2014 they show up in the response headers. This check asks for each image twice, once with a modern Accept header and once without, and compares what comes back.<\/p>\n<div style=\"margin-bottom: 14px;\">\n        <label for=\"bild-url\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Page address<\/label><br \/>\n        <input id=\"bild-url\" type=\"text\" value=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; box-sizing: border-box; font-family: monospace;\">\n    <\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.82rem; margin-bottom: 16px;\">From this server: one request for the page and, for each of the first six images, one GET capped at 256 KB plus one HEAD. No cookies are sent, no scripts are executed, nothing is stored beyond the rate counter.<\/p>\n<p>    <button id=\"bild-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: var(--on-accent, #0b1114); border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Check image delivery<\/button><\/p>\n<div id=\"bild-ausgabe\" style=\"margin-top: 22px;\"><\/div>\n<p style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.8rem; margin: 24px 0 0;\">Limits worth knowing: only images in the served HTML are seen \u2014 anything a script inserts later stays invisible, as do CSS background images. The first six distinct addresses are checked, and the real file size comes from Content-Length, because the download stops at 256 KB.<\/p>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"A page can carry the same picture in four ways: as a plain JPEG, as WebP served to everyone, as a negotiated file that depends on the Accept header, or as a picture element that lets the browser choose. Only the second and third are invisible in the markup \u2014 they show up in the response headers. This check asks for each image twice, once with a modern Accept header and once without, and compares what comes back.\",\"label_url\":\"Page address\",\"datenschutz\":\"From this server: one request for the page and, for each of the first six images, one GET capped at 256 KB plus one HEAD. No cookies are sent, no scripts are executed, nothing is stored beyond the rate counter.\",\"knopf\":\"Check image delivery\",\"laeuft\":\"Fetching page and images...\",\"grenze\":\"Limits worth knowing: only images in the served HTML are seen \u2014 anything a script inserts later stays invisible, as do CSS background images. The first six distinct addresses are checked, and the real file size comes from Content-Length, because the download stops at 256 KB.\",\"h_bilder\":\"Images checked\",\"tabelle_hinweis\":\"Delivered is what an Accept header naming AVIF and WebP receives; the next column is what the same address returns without it. Two different types mean the server negotiates.\",\"h_befunde\":\"Findings\",\"sp_bild\":\"Image\",\"sp_geliefert\":\"Delivered\",\"sp_ohne_accept\":\"Without Accept\",\"sp_groesse\":\"Size\",\"sp_masse\":\"Intrinsic\",\"sp_markup\":\"In markup\",\"sp_laden\":\"loading\",\"zeile_seite\":\"Page\",\"zeile_gefunden\":\"Images in markup\",\"zeile_geprueft\":\"Images fetched\",\"zeile_dauer\":\"Took\",\"zeile_limit\":\"Checks left this hour\",\"wert_fehlt\":\"missing\",\"stufe_hoch\":\"High\",\"stufe_mittel\":\"Medium\",\"stufe_niedrig\":\"Low\",\"stufe_info\":\"Information\",\"stufe_gut\":\"In order\",\"keine_befunde\":\"Nothing to report.\",\"urteil_gut\":\"Images are delivered soundly.\",\"urteil_warnung\":\"The images load, but bytes or layout stability are being given away.\",\"urteil_kritisch\":\"An image is unreachable, or a negotiated file is being cached for the wrong browsers.\",\"fehler_kein_token\":\"The check could not be started because reCAPTCHA did not load.\",\"fehler_captcha\":\"reCAPTCHA classified the request as automated. Reloading the page normally helps.\",\"fehler_zu_viele\":\"The limit of {limit} checks per hour for this address has been reached.\",\"fehler_adresse\":\"That address cannot be used.\",\"fehler_abruf\":\"The page could not be fetched.\",\"fehler_zaehler\":\"The rate counter is unavailable, so nothing was checked.\",\"fehler_pruefdienst\":\"The reCAPTCHA service could not be reached.\",\"fehler_aufbau\":\"The service is not configured correctly. The fault is on this side.\",\"fehler_eingabe\":\"The request could not be read.\",\"fehler_methode\":\"Wrong request method.\",\"fehler_netz\":\"The service could not be reached.\",\"fehler_antwort\":\"The answer could not be read.\",\"fehler_unbekannt\":\"Something went wrong that has no message of its own.\",\"grund_leer\":\"Nothing was entered.\",\"grund_zu_lang\":\"The input is too long.\",\"grund_unlesbar\":\"It does not parse as an address.\",\"grund_schema\":\"Only http and https addresses are checked.\",\"grund_benutzerinfo\":\"Inputs with credentials in them are refused.\",\"grund_kein_punkt\":\"The host name needs at least one dot.\",\"grund_hostform\":\"A host name is expected, not an IP address.\",\"grund_port\":\"Only the ports 80, 443, 8080 and 8443 are checked.\",\"grund_gesperrter_bereich\":\"That host resolves into a private or reserved network. Those are never fetched.\",\"grund_kein_dns\":\"The host name does not resolve.\",\"grund_abruf\":\"The address did not answer.\",\"grund_zu_viele_spruenge\":\"The address redirects too often.\",\"befund_status\":\"The page answered with status {status} || Images were still read from whatever body came back, but a page that does not answer with 200 rarely shows what visitors see.\",\"befund_keine_bilder\":\"No image element in the served HTML || Either the page genuinely carries none, or every image is inserted by a script after loading. This check only sees what the server sends.\",\"befund_nur_stichprobe\":\"{gesamt} images in the markup, the first {n} were fetched || A sample keeps the load on the checked server small. Findings apply to those {n}; the rest may differ.\",\"befund_bild_unerreichbar\":\"{url} could not be fetched || Reason: {grund}. A referenced image that does not answer leaves a gap on the page and costs a request every time.\",\"befund_bild_status\":\"{url} answered with status {status} || The address is in the markup but does not deliver a file. Browsers show the alt text instead, crawlers record a broken resource.\",\"befund_aushandlung_ohne_vary\":\"{url} negotiates formats but sends no Vary: Accept || With a modern Accept header the server sends {modern}, without it {klassisch} \u2014 so the answer depends on the request. Vary said {vary}. Any shared cache in between stores the first answer and hands it to everyone, which means a browser without WebP support can receive the WebP file.\",\"befund_aushandlung_mit_vary\":\"{url} negotiates formats and says so || {modern} for a modern browser, {klassisch} for an old one, and Vary: Accept in the answer. That is the combination a cache needs in order to keep both apart.\",\"befund_ohne_laenge\":\"{url} came without Content-Length || The real file size stays unknown, because the download stops at 256 KB. Chunked answers behave this way; the size can only be measured by loading the whole file.\",\"befund_schwer_und_klassisch\":\"{url} weighs {kb} KB as {typ} || A file that size in a classic format is the usual place where a page gives away half its bytes. WebP typically saves a quarter to a third at the same quality, AVIF more.\",\"befund_uebermass\":\"{url} is {faktor} times larger than it is displayed || Intrinsic width {eigen} px, largest declared display width {anzeige} px. The surplus is transferred and then scaled down in the browser.\",\"befund_bild_ohne_cache_control\":\"{url} carries no Cache-Control || Without it every cache decides for itself how long the file stays valid. Images rarely change under a fixed address, so they are the safest candidates for a long lifetime.\",\"befund_bild_kurzer_cache\":\"{url} may be cached for {sekunden} seconds || For an image that is short. When the address changes with the content, a year is the usual value.\",\"befund_ohne_masse\":\"{n} of {gesamt} images carry no width and height || Without both attributes the browser cannot reserve space before the file arrives, and the text below jumps once it does. That jump is what Cumulative Layout Shift measures.\",\"befund_masse_vollstaendig\":\"All {n} images carry width and height || The browser can reserve the space before the file arrives, so nothing jumps.\",\"befund_ohne_alt\":\"{n} images have no alt attribute || Screen readers then read the file name, and search engines lose the only text that describes the picture. An empty alt is a valid answer for decoration; a missing one is not.\",\"befund_nur_klassisch\":\"{n} images are delivered as JPEG or PNG with no modern alternative || Neither negotiation nor a picture element offering WebP or AVIF. Whether that matters depends on the file size.\",\"befund_aushandlung_gefunden\":\"{n} images are negotiated by format || The server looks at the Accept header and answers accordingly.\",\"befund_schwere_bilder\":\"{n} images weigh more than {grenze_kb} KB in a classic format || Those are the candidates worth converting first.\",\"befund_erstes_lazy\":\"The first image is marked loading=lazy || {url}. The image at the top of a page is usually the one that decides Largest Contentful Paint, and lazy loading delays exactly that one until layout is known.\",\"befund_spaet_ohne_lazy\":\"{n} images below the first three load eagerly || Images far down the page can wait until they come into view. Whether they are truly below the fold cannot be decided from the markup alone.\"};<\/p>\n<p>    var ENDPUNKT = '\/lw-bildcheck.php';\n    var SITEKEY = '6LcrPkEtAAAAAPo1QCOf-IIM2fCL0UfdJz4y2iSY';\n    var STUFEN = ['hoch', 'mittel', 'niedrig', 'info', 'gut'];\n    var MODERN = ['image\/webp', 'image\/avif'];<\/p>\n<p>    function liste(w) { return Array.isArray(w) ? w : []; }\n    function zahl(w) { return (typeof w === 'number' && isFinite(w)) ? w : null; }\n    function zeichen(w) { return (typeof w === 'string' && w !== '') ? w : null; }\n    function text(schluessel, daten) {\n        var t = T[schluessel];\n        if (typeof t !== 'string') { return null; }\n        return t.replace(\/\\{([a-z_0-9]+)\\}\/g, function (m, k) {\n            return (daten && daten[k] !== undefined && daten[k] !== null) ? String(daten[k]) : m;\n        });\n    }\n    \/** Adresse auf das kuerzen, was in einer Tabellenzelle lesbar bleibt. *\/\n    function kurz(url) {\n        var s = String(url || '');\n        s = s.replace(\/^https?:\\\/\\\/\/, '');\n        if (s.length <= 52) { return s; }\n        return s.slice(0, 24) + '\u2026' + s.slice(-26);\n    }\n\n    function bewerten(antwort) {\n        var a = antwort || {};\n        if (a.fehler) { return { fehler: String(a.fehler), grund: a.grund || null, limit: zahl(a.limit) }; }\n        var befunde = liste(a.befunde).map(function (f) {\n            return { key: String(f.key || ''), stufe: STUFEN.indexOf(f.stufe) === -1 ? 'info' : f.stufe, daten: (f.daten &#038;&#038; typeof f.daten === 'object') ? f.daten : {} };\n        });\n        var gruppen = {};\n        STUFEN.forEach(function (s) { gruppen[s] = befunde.filter(function (f) { return f.stufe === s; }); });\n        var bilder = liste(a.bilder).map(function (z) {\n            return {\n                rang: zahl(z.rang), url: String(z.url || ''), status: zahl(z.status),\n                typ: zeichen(z.typ), altTyp: zeichen(z.alt_typ), kb: zahl(z.kb),\n                eigen: zeichen(z.eigen), markup: zeichen(z.markup),\n                srcset: z.srcset === true, loading: zeichen(z.loading), vary: zeichen(z.vary)\n            };\n        });\n        return {\n            fehler: null, url: String(a.url || ''), endurl: String(a.endurl || ''),\n            urteil: (a.urteil === 'gut' || a.urteil === 'warnung' || a.urteil === 'kritisch') ? a.urteil : 'kritisch',\n            gesamt: zahl(a.gesamt), geprueft: zahl(a.geprueft), bilder: bilder,\n            befunde: befunde, gruppen: gruppen, dauer: zahl(a.dauer_ms), limitRest: zahl(a.limit_rest)\n        };\n    }\n\n    function skriptLaden() {\n        return new Promise(function (auf) {\n            if (window.grecaptcha) { auf(true); return; }\n            var s = document.createElement('script');\n            s.src = 'https:\/\/www.google.com\/recaptcha\/api.js?render=' + SITEKEY;\n            s.onload = function () { auf(true); }; s.onerror = function () { auf(false); };\n            document.head.appendChild(s);\n            setTimeout(function () { auf(!!window.grecaptcha); }, 8000);\n        });\n    }\n    function tokenHolen() {\n        return skriptLaden().then(function (da) {\n            if (!da || !window.grecaptcha || !window.grecaptcha.ready) { return null; }\n            return new Promise(function (auf) {\n                var fertig = false;\n                function einmal(w) { if (!fertig) { fertig = true; auf(w); } }\n                setTimeout(function () { einmal(null); }, 12000);\n                try {\n                    window.grecaptcha.ready(function () {\n                        if (fertig) { return; }\n                        if (!window.grecaptcha.execute) { einmal(null); return; }\n                        window.grecaptcha.execute(SITEKEY, { action: 'bildcheck' }).then(function (t) { einmal(t || null); }, function () { einmal(null); });\n                    });\n                } catch (e) { einmal(null); }\n            });\n        }, function () { return null; });\n    }\n    function abfragen(url) {\n        return tokenHolen().then(function (token) {\n            return fetch(ENDPUNKT, { method: 'POST', headers: { 'Content-Type': 'application\/json' }, body: JSON.stringify({ url: url, token: token || '' }) })\n                .then(function (r) { return r.json().then(function (j) { return j; }, function () { return { fehler: 'antwort' }; }); }, function () { return { fehler: 'netz' }; });\n        });\n    }\n\n    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.BILD = { bewerten: bewerten, darstellen: null, text: text, kurz: kurz, ENDPUNKT: ENDPUNKT, STUFEN: STUFEN, T: T };\n\n    var btn = document.getElementById('bild-btn');\n    var out = document.getElementById('bild-ausgabe');\n    if (!btn || !out) { return; }\n\n    function el(tag, stil, txt) { var e = document.createElement(tag); if (stil) { e.setAttribute('style', stil); } if (txt !== undefined &#038;&#038; txt !== null) { e.textContent = txt; } return e; }\n    function wert(id) { var e = document.getElementById(id); return e ? e.value : ''; }\n    var UEBERSCHRIFT = 'font-family: \"Nunito Sans\", sans-serif; font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; margin: 22px 0 8px;';\n    var ZELLE = 'padding: 5px 12px 5px 0; color: var(--text-secondary, #a0a0b0); font-size: 0.87rem;';\n    var ZELLE_WERT = 'padding: 5px 12px 5px 0; color: var(--text-primary, #e8e8ee); font-size: 0.87rem; font-family: monospace; word-break: break-all;';\n    var KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 8px; padding: 14px 16px; margin: 0 0 10px;';\n    var GUT = ' color: #7ee787;', WARN = ' color: #ffa94d;', ROT = ' color: #ff7b72;', GRAU = ' color: #8a8a99;';\n    var FARBEN = { hoch: '#ff7b72', mittel: '#ffa94d', niedrig: '#e3b341', info: '#8a8a99', gut: '#7ee787' };\n    var URTEIL = { gut: GUT, warnung: WARN, kritisch: ROT };\n    function gitter(sp) { return el('div', 'display: grid; grid-template-columns: ' + sp + '; gap: 0 18px; align-items: baseline;'); }\n    function paar(g, n, w, stil) { g.appendChild(el('div', ZELLE, n)); g.appendChild(el('div', ZELLE_WERT + (stil || ''), w)); }\n\n    \/** Eine Zeile je geprueftem Bild. Waagerecht rollbar, damit nichts umbricht. *\/\n    function tabelle(b) {\n        var huelle = el('div', 'overflow-x: auto; margin: 0 0 6px;');\n        var t = el('table', 'border-collapse: collapse; width: 100%; min-width: 620px; font-size: 0.84rem;');\n        var kopf = el('tr', '');\n        [T.sp_bild, T.sp_geliefert, T.sp_ohne_accept, T.sp_groesse, T.sp_masse, T.sp_markup, T.sp_laden].forEach(function (n) {\n            kopf.appendChild(el('th', 'text-align: left; padding: 6px 12px 6px 0; color: #8a8a99; font-weight: 700; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.4px; border-bottom: 1px solid var(--border, #2a2a35); white-space: nowrap;', n));\n        });\n        t.appendChild(kopf);\n        b.bilder.forEach(function (z) {\n            var r = el('tr', '');\n            function zelle(txt, stil) { r.appendChild(el('td', 'padding: 7px 12px 7px 0; border-bottom: 1px solid var(--border, #2a2a35); font-family: monospace; color: var(--text-primary, #e8e8ee);' + (stil || ''), txt)); }\n            zelle(kurz(z.url), ' word-break: break-all;');\n            var modern = z.typ &#038;&#038; MODERN.indexOf(z.typ) !== -1;\n            zelle(z.status === 200 ? (z.typ || '-') : (z.status === null ? T.wert_fehlt : String(z.status)), z.status !== 200 ? ROT : (modern ? GUT : GRAU));\n            zelle(z.altTyp || '-', (z.altTyp &#038;&#038; z.typ &#038;&#038; z.altTyp !== z.typ) ? GUT : GRAU);\n            zelle(z.kb !== null ? z.kb + ' KB' : '-', GRAU);\n            zelle(z.eigen || '-', GRAU);\n            zelle(z.markup || T.wert_fehlt, z.markup ? GRAU : WARN);\n            zelle(z.loading || '-', GRAU);\n            t.appendChild(r);\n        });\n        huelle.appendChild(t);\n        return huelle;\n    }\n\n    function darstellen(b, ziel) {\n        ziel.innerHTML = '';\n        if (b.fehler) {\n            var txt = T['fehler_' + b.fehler] || T.fehler_unbekannt;\n            if (b.grund &#038;&#038; T['grund_' + b.grund]) { txt = txt + ' ' + T['grund_' + b.grund]; }\n            if (b.limit !== null) { txt = txt.replace('{limit}', String(b.limit)); }\n            ziel.appendChild(el('p', ZELLE + WARN + ' margin: 0;', txt));\n            return;\n        }\n        ziel.appendChild(el('div', 'font-size: 1.05rem; font-weight: 700; margin: 0 0 12px;' + URTEIL[b.urteil], T['urteil_' + b.urteil]));\n\n        var g = gitter('auto auto');\n        paar(g, T.zeile_seite, b.endurl || b.url, GRAU);\n        paar(g, T.zeile_gefunden, (b.gesamt !== null ? b.gesamt : '-') + '', GRAU);\n        paar(g, T.zeile_geprueft, (b.geprueft !== null ? b.geprueft : '-') + '', GRAU);\n        if (b.dauer !== null) { paar(g, T.zeile_dauer, b.dauer + ' ms', GRAU); }\n        if (b.limitRest !== null) { paar(g, T.zeile_limit, String(b.limitRest), GRAU); }\n        ziel.appendChild(g);\n\n        if (b.bilder.length) {\n            ziel.appendChild(el('div', UEBERSCHRIFT, T.h_bilder));\n            ziel.appendChild(tabelle(b));\n            ziel.appendChild(el('p', ZELLE + ' margin: 0 0 6px; font-size: 0.8rem;', T.tabelle_hinweis));\n        }\n\n        ziel.appendChild(el('div', UEBERSCHRIFT, T.h_befunde));\n        var irgendwas = false;\n        STUFEN.forEach(function (s) {\n            var gr = b.gruppen[s];\n            if (!gr.length) { return; }\n            irgendwas = true;\n            ziel.appendChild(el('div', 'font-weight: 700; font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.5px; margin: 14px 0 6px; color: ' + FARBEN[s] + ';', T['stufe_' + s] + ' (' + gr.length + ')'));\n            gr.forEach(function (f) {\n                var k = el('div', KASTEN + ' border-left: 3px solid ' + FARBEN[s] + ';');\n                var daten = {};\n                Object.keys(f.daten).forEach(function (n) { daten[n] = (n === 'url') ? kurz(f.daten[n]) : f.daten[n]; });\n                var t = text('befund_' + f.key, daten) || f.key;\n                var teile = t.split(' || ');\n                k.appendChild(el('div', 'color: var(--text-primary, #e8e8ee); font-size: 0.9rem; font-weight: 700;', teile[0]));\n                if (teile[1]) { k.appendChild(el('div', ZELLE + ' padding: 6px 0 0; line-height: 1.55;', teile[1])); }\n                ziel.appendChild(k);\n            });\n        });\n        if (!irgendwas) { ziel.appendChild(el('p', ZELLE + ' margin: 0;', T.keine_befunde)); }\n    }\n    window.LW_TEST.BILD.darstellen = darstellen;\n\n    var laeuft = false;\n    btn.addEventListener('click', function () {\n        if (laeuft) { return; }\n        laeuft = true; btn.disabled = true;\n        out.innerHTML = '';\n        out.appendChild(el('p', ZELLE + GRAU + ' margin: 0;', T.laeuft));\n        abfragen(wert('bild-url')).then(function (a) { darstellen(bewerten(a), out); laeuft = false; btn.disabled = false; },\n            function () { darstellen(bewerten({ fehler: 'netz' }), out); laeuft = false; btn.disabled = false; });\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fetches a page, takes the first images from its markup and asks the server for each one twice: once as a browser that accepts WebP and AVIF, once as one that does not. The comparison shows whether formats are negotiated at all and whether the answer carries Vary: Accept, without which a shared cache hands the modern file to a browser that cannot read it. Markup is checked alongside: width and height, srcset, loading and alt.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91094,92757,91315],"class_list":["post-15695","page","type-page","status-publish","hentry","tag-seo","tag-web-development","tag-web-performance"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Image Delivery &amp; Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Image Delivery &amp; Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Fetches a page, takes the first images from its markup and asks the server for each one twice: once as a browser that accepts WebP and AVIF, once as one that does not. The comparison shows whether formats are negotiated at all and whether the answer carries Vary: Accept, without which a shared cache hands the modern file to a browser that cannot read it. Markup is checked alongside: width and height, srcset, loading and alt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/image-delivery-format-negotiation-auditor\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/image-delivery-format-negotiation-auditor\\\/\",\"name\":\"Image Delivery & Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-09-05T22:25:16+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/image-delivery-format-negotiation-auditor\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/image-delivery-format-negotiation-auditor\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/image-delivery-format-negotiation-auditor\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Image Delivery &#038; Format Negotiation Auditor: WebP, AVIF, srcset, Vary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Image Delivery & Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/","og_locale":"en_US","og_type":"article","og_title":"Image Delivery & Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog","og_description":"Fetches a page, takes the first images from its markup and asks the server for each one twice: once as a browser that accepts WebP and AVIF, once as one that does not. The comparison shows whether formats are negotiated at all and whether the answer carries Vary: Accept, without which a shared cache hands the modern file to a browser that cannot read it. Markup is checked alongside: width and height, srcset, loading and alt.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/","name":"Image Delivery & Format Negotiation Auditor: WebP, AVIF, srcset, Vary - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-09-05T22:25:16+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/image-delivery-format-negotiation-auditor\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"Image Delivery &#038; Format Negotiation Auditor: WebP, AVIF, srcset, Vary"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/15695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=15695"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/15695\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=15695"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=15695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}