[{"data":1,"prerenderedAt":1504},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints":1495},{"id":4,"title":5,"body":6,"description":1476,"extension":1477,"meta":1478,"navigation":911,"path":1489,"seo":1490,"stem":1493,"__hash__":1494},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Findex.md","Resource Hints & Early Hints",{"type":7,"value":8,"toc":1462},"minimark",[9,14,24,37,196,199,204,207,302,321,325,350,354,357,445,460,464,467,576,583,587,590,707,718,725,729,732,800,803,807,818,1035,1050,1054,1057,1093,1113,1124,1128,1147,1159,1165,1174,1182,1186,1189,1328,1331,1334,1338,1360,1372,1381,1399,1403,1444,1449,1452,1455,1458],[10,11,13],"h1",{"id":12},"resource-hints-early-hints-buying-back-the-discovery-delay","Resource Hints & Early Hints: Buying Back the Discovery Delay",[15,16,17,18,23],"p",{},"This guide extends the delivery layer described in ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture"," to the problem a cache cannot solve: a resource the browser has not discovered yet is not being fetched, no matter how close to the user it is stored.",[15,25,26,27,31,32,36],{},"Every subresource on a page has a discovery timestamp — the moment the browser learns it exists — and a request timestamp. The gap between them is pure waste, and on a modern SPA-ish page it is routinely ",[28,29,30],"code",{},"400–900ms",", because the fonts, the hero image and the route chunk are all referenced from a CSS or JavaScript file that must itself be downloaded and parsed first. Resource hints close that gap by telling the browser about a resource before the parser reaches it, and Early Hints goes further by sending those hints ",[33,34,35],"em",{},"before the HTML response has even been generated",".",[38,39,46,47,54,55,54,59,54,63,54,72,54,79,54,84,54,91,54,98,54,104,54,108,54,111,54,115,54,122,54,127,54,132,54,136,54,139,54,142,54,144,54,146,54,148,54,150,54,154,54,157,54,161,54,165,54,168,54,171,54,174,54,177,54,179,54,181,54,185,54,191,54],"svg",{"xmlns":40,"viewBox":41,"width":42,"role":43,"ariaLabel":44,"style":45},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 300","100%","img","Three request timelines: no hints where the hero image starts after CSS parsing, preload where it starts with the HTML, and 103 Early Hints where it starts during server think time","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)","\n  ",[48,49],"rect",{"className":50,"x":52,"y":52,"width":42,"height":42,"fill":53},[51],"svg-canvas","0","#ffffff"," ",[56,57,58],"title",{},"Discovery time with no hints, with preload, and with 103 Early Hints",[60,61,62],"desc",{},"Three stacked timelines show the LCP image request starting progressively earlier: after CSS parse with no hints, alongside the HTML with a preload, and during server think time with Early Hints.",[48,64],{"x":65,"y":65,"width":66,"height":67,"rx":68,"fill":69,"stroke":70,"style":71},"1","758","298","10","none","currentColor","stroke-opacity:0.18",[73,74,78],"text",{"x":75,"y":76,"fill":70,"style":77},"24","34","font-size:17px;font-weight:700","When does the hero image start downloading?",[73,80,83],{"x":75,"y":81,"fill":70,"style":82},"76","font-size:11.5px;font-weight:700","No hints",[48,85],{"x":86,"y":87,"width":88,"height":75,"rx":89,"fill":70,"stroke":70,"style":90},"150","60","90","3","fill-opacity:0.06;stroke-opacity:0.3",[73,92,97],{"x":93,"y":94,"fill":70,"style":95,"textAnchor":96},"195","77","font-size:10.5px;","middle","server",[48,99],{"x":100,"y":87,"width":101,"height":75,"rx":89,"fill":102,"stroke":102,"style":103},"240","80","#0466c8","fill-opacity:0.16;stroke-opacity:0.5",[73,105,107],{"x":106,"y":94,"fill":70,"style":95,"textAnchor":96},"280","HTML",[48,109],{"x":110,"y":87,"width":88,"height":75,"rx":89,"fill":102,"stroke":102,"style":103},"320",[73,112,114],{"x":113,"y":94,"fill":70,"style":95,"textAnchor":96},"365","CSS",[48,116],{"x":117,"y":87,"width":118,"height":75,"rx":89,"fill":119,"stroke":120,"style":121},"410","180","#ffc300","#b8860b","fill-opacity:0.28",[73,123,126],{"x":124,"y":94,"fill":70,"style":125,"textAnchor":96},"500","font-size:10.5px;font-weight:700;","hero image",[73,128,131],{"x":129,"y":94,"fill":70,"style":130},"600","font-size:10.5px;fill-opacity:0.75","LCP here",[73,133,135],{"x":75,"y":134,"fill":70,"style":82},"146","preload",[48,137],{"x":86,"y":138,"width":88,"height":75,"rx":89,"fill":70,"stroke":70,"style":90},"130",[73,140,97],{"x":93,"y":141,"fill":70,"style":95,"textAnchor":96},"147",[48,143],{"x":100,"y":138,"width":101,"height":75,"rx":89,"fill":102,"stroke":102,"style":103},[73,145,107],{"x":106,"y":141,"fill":70,"style":95,"textAnchor":96},[48,147],{"x":110,"y":138,"width":118,"height":75,"rx":89,"fill":119,"stroke":120,"style":121},[73,149,126],{"x":117,"y":141,"fill":70,"style":125,"textAnchor":96},[48,151],{"x":110,"y":152,"width":88,"height":153,"rx":89,"fill":102,"stroke":102,"style":103},"158","20",[73,155,114],{"x":113,"y":156,"fill":70,"style":95,"textAnchor":96},"173",[73,158,160],{"x":159,"y":141,"fill":70,"style":130},"510","≈180ms earlier",[73,162,164],{"x":75,"y":163,"fill":70,"style":82},"222","103 Early Hints",[48,166],{"x":86,"y":167,"width":88,"height":75,"rx":89,"fill":70,"stroke":70,"style":90},"206",[73,169,97],{"x":93,"y":170,"fill":70,"style":95,"textAnchor":96},"223",[48,172],{"x":86,"y":173,"width":118,"height":75,"rx":89,"fill":119,"stroke":120,"style":121},"234",[73,175,126],{"x":100,"y":176,"fill":70,"style":125,"textAnchor":96},"251",[48,178],{"x":100,"y":167,"width":101,"height":75,"rx":89,"fill":102,"stroke":102,"style":103},[73,180,107],{"x":106,"y":170,"fill":70,"style":95,"textAnchor":96},[73,182,184],{"x":183,"y":176,"fill":70,"style":130},"345","starts inside server think time",[186,187],"line",{"x1":86,"y1":188,"x2":86,"y2":189,"stroke":70,"style":190},"276","286","stroke-opacity:0.5",[73,192,195],{"x":86,"y":193,"fill":70,"style":194,"textAnchor":96},"296","font-size:10px;","request sent",[15,197,198],{},"Used well, hints are the cheapest LCP improvement available. Used carelessly, they are a way to make a page slower while feeling productive: every hint competes for the same connection, and a preload that fires ahead of the LCP image has actively harmed the metric it was meant to help.",[200,201,203],"h2",{"id":202},"the-five-hints-and-what-each-one-actually-does","The Five Hints and What Each One Actually Does",[15,205,206],{},"Each hint operates at a different layer, and confusing them is the root of most misuse.",[208,209,210,226],"table",{},[211,212,213],"thead",{},[214,215,216,220,223],"tr",{},[217,218,219],"th",{},"Hint",[217,221,222],{},"What it does",[217,224,225],{},"Costs if wrong",[227,228,229,243,256,268,285],"tbody",{},[214,230,231,237,240],{},[232,233,234],"td",{},[28,235,236],{},"dns-prefetch",[232,238,239],{},"Resolves DNS for an origin",[232,241,242],{},"Negligible; a wasted lookup",[214,244,245,250,253],{},[232,246,247],{},[28,248,249],{},"preconnect",[232,251,252],{},"DNS + TCP + TLS to an origin",[232,254,255],{},"Holds an idle connection for ~10s; wasteful beyond 2–4 origins",[214,257,258,262,265],{},[232,259,260],{},[28,261,135],{},[232,263,264],{},"Fetches a specific file at high priority",[232,266,267],{},"Steals bandwidth and priority from the real critical path",[214,269,270,275,282],{},[232,271,272],{},[28,273,274],{},"modulepreload",[232,276,277,278,281],{},"Fetches and ",[33,279,280],{},"parses"," an ES module and its graph",[232,283,284],{},"Same as preload plus compile cost",[214,286,287,292,299],{},[232,288,289],{},[28,290,291],{},"prefetch",[232,293,294,295,298],{},"Fetches for a ",[33,296,297],{},"future"," navigation at lowest priority",[232,300,301],{},"Wasted bytes if the navigation never happens",[15,303,304,305,307,308,310,311,313,314,316,317,36],{},"The distinction that matters most in practice is ",[28,306,249],{}," versus ",[28,309,135],{},". ",[28,312,249],{}," prepares a pipe without knowing what will travel through it — ideal when you know the origin (a font host, an image CDN, an API) but not yet the exact URL. ",[28,315,135],{}," names an exact file, which means you must be certain the page will use it. Choosing between them is the subject of ",[19,318,320],{"href":319},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Fpreload-vs-preconnect-when-to-use-each\u002F","preload vs preconnect: when to use each",[200,322,324],{"id":323},"prerequisites","Prerequisites",[326,327,328,336,339,347],"ul",{},[329,330,331,332,335],"li",{},"A CDN or origin server that can set response headers per route. Header-based hints (",[28,333,334],{},"Link:",") are processed earlier than markup-based ones and are the only form that works with Early Hints.",[329,337,338],{},"HTTP\u002F2 or HTTP\u002F3 end to end. On HTTP\u002F1.1 the connection limit makes aggressive hinting counterproductive.",[329,340,341,342,346],{},"The Network panel with the ",[343,344,345],"strong",{},"Priority"," column enabled — hints change priority, and priority is what decides which request wins when bandwidth is scarce.",[329,348,349],{},"A known LCP element per route. Every decision below is made relative to it: a hint that delays the LCP resource is a regression regardless of what it accelerates.",[200,351,353],{"id":352},"_1-establish-the-critical-path-before-adding-any-hint","1. Establish the Critical Path Before Adding Any Hint",[15,355,356],{},"Hints reorder a queue. If you do not know the current order, you cannot know whether your change improved it.",[358,359,364],"pre",{"className":360,"code":361,"language":362,"meta":363,"style":363},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Requests in start order with priority, for the routes that matter.\nnpx lighthouse https:\u002F\u002Fexample.com --output=json --quiet \\\n  | jq -r '.audits[\"network-requests\"].details.items[]\n           | \"\\(.startTime|floor)ms  \\(.priority)  \\(.resourceType)  \\(.url|split(\"?\")[0])\"' \\\n  | head -25\n# trade-off: Lighthouse's simulated throttling distorts absolute timings. Use it\n# for ORDER and PRIORITY; take absolute numbers from a real device trace.\n","bash","",[28,365,366,374,399,414,422,433,439],{"__ignoreMap":363},[367,368,370],"span",{"class":186,"line":369},1,[367,371,373],{"class":372},"sjfSM","# Requests in start order with priority, for the routes that matter.\n",[367,375,377,381,385,388,392,395],{"class":186,"line":376},2,[367,378,380],{"class":379},"sQw3B","npx",[367,382,384],{"class":383},"sZ8jY"," lighthouse",[367,386,387],{"class":383}," https:\u002F\u002Fexample.com",[367,389,391],{"class":390},"sPXB4"," --output=json",[367,393,394],{"class":390}," --quiet",[367,396,398],{"class":397},"sPARh"," \\\n",[367,400,402,405,408,411],{"class":186,"line":401},3,[367,403,404],{"class":397},"  |",[367,406,407],{"class":379}," jq",[367,409,410],{"class":390}," -r",[367,412,413],{"class":383}," '.audits[\"network-requests\"].details.items[]\n",[367,415,417,420],{"class":186,"line":416},4,[367,418,419],{"class":383},"           | \"\\(.startTime|floor)ms  \\(.priority)  \\(.resourceType)  \\(.url|split(\"?\")[0])\"'",[367,421,398],{"class":397},[367,423,425,427,430],{"class":186,"line":424},5,[367,426,404],{"class":397},[367,428,429],{"class":379}," head",[367,431,432],{"class":390}," -25\n",[367,434,436],{"class":186,"line":435},6,[367,437,438],{"class":372},"# trade-off: Lighthouse's simulated throttling distorts absolute timings. Use it\n",[367,440,442],{"class":186,"line":441},7,[367,443,444],{"class":372},"# for ORDER and PRIORITY; take absolute numbers from a real device trace.\n",[15,446,447,448,451,452,455,456,36],{},"Look for the signature problem: the LCP image at ",[28,449,450],{},"Low"," priority, starting after two stylesheets and a font. That is a discovery-and-priority problem, and it is exactly what hints exist to fix — though for images specifically, ",[28,453,454],{},"fetchpriority"," is often the better tool, as covered in ",[19,457,459],{"href":458},"\u002Fimage-media-optimization\u002Fimage-cdns-and-fetchpriority\u002Fusing-fetchpriority-to-prioritize-the-lcp-image\u002F","using fetchpriority to prioritize the LCP image",[200,461,463],{"id":462},"_2-preconnect-the-two-origins-that-matter","2. Preconnect the Two Origins That Matter",[15,465,466],{},"Preconnect is cheap per origin and expensive in aggregate. Each hint holds an open connection for roughly ten seconds whether or not it is used, consuming client resources and sometimes a server connection slot.",[358,468,472],{"className":469,"code":470,"language":471,"meta":363,"style":363},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u003C!-- In \u003Chead>, as early as possible: the origins the critical path needs. -->\n\u003Clink rel=\"preconnect\" href=\"https:\u002F\u002Fimages.example-cdn.com\" crossorigin>\n\u003Clink rel=\"preconnect\" href=\"https:\u002F\u002Fapi.example.com\">\n\u003C!-- Cheap fallback for origins that are likely but not certain. -->\n\u003Clink rel=\"dns-prefetch\" href=\"https:\u002F\u002Fanalytics.example.net\">\n\u003C!-- trade-off: two or three preconnects is the practical ceiling. Beyond that\n     you are opening connections that compete with the ones already carrying\n     your HTML and CSS — measure LCP before and after, per origin. -->\n","html",[28,473,474,479,512,533,538,560,565,570],{"__ignoreMap":363},[367,475,476],{"class":186,"line":369},[367,477,478],{"class":372},"\u003C!-- In \u003Chead>, as early as possible: the origins the critical path needs. -->\n",[367,480,481,485,489,492,495,498,501,503,506,509],{"class":186,"line":376},[367,482,484],{"class":483},"saISM","\u003C",[367,486,488],{"class":487},"sZBmE","link",[367,490,491],{"class":390}," rel",[367,493,494],{"class":483},"=",[367,496,497],{"class":383},"\"preconnect\"",[367,499,500],{"class":390}," href",[367,502,494],{"class":483},[367,504,505],{"class":383},"\"https:\u002F\u002Fimages.example-cdn.com\"",[367,507,508],{"class":390}," crossorigin",[367,510,511],{"class":483},">\n",[367,513,514,516,518,520,522,524,526,528,531],{"class":186,"line":401},[367,515,484],{"class":483},[367,517,488],{"class":487},[367,519,491],{"class":390},[367,521,494],{"class":483},[367,523,497],{"class":383},[367,525,500],{"class":390},[367,527,494],{"class":483},[367,529,530],{"class":383},"\"https:\u002F\u002Fapi.example.com\"",[367,532,511],{"class":483},[367,534,535],{"class":186,"line":416},[367,536,537],{"class":372},"\u003C!-- Cheap fallback for origins that are likely but not certain. -->\n",[367,539,540,542,544,546,548,551,553,555,558],{"class":186,"line":424},[367,541,484],{"class":483},[367,543,488],{"class":487},[367,545,491],{"class":390},[367,547,494],{"class":483},[367,549,550],{"class":383},"\"dns-prefetch\"",[367,552,500],{"class":390},[367,554,494],{"class":483},[367,556,557],{"class":383},"\"https:\u002F\u002Fanalytics.example.net\"",[367,559,511],{"class":483},[367,561,562],{"class":186,"line":435},[367,563,564],{"class":372},"\u003C!-- trade-off: two or three preconnects is the practical ceiling. Beyond that\n",[367,566,567],{"class":186,"line":441},[367,568,569],{"class":372},"     you are opening connections that compete with the ones already carrying\n",[367,571,573],{"class":186,"line":572},8,[367,574,575],{"class":372},"     your HTML and CSS — measure LCP before and after, per origin. -->\n",[15,577,578,579,582],{},"The ",[28,580,581],{},"crossorigin"," attribute must match how the resource will be fetched. Fonts and any CORS-mode fetch need it; a plain image from an image CDN does not. A mismatch means the warmed connection is never reused and you have paid the handshake twice.",[200,584,586],{"id":585},"_3-preload-only-what-defines-the-above-the-fold-experience","3. Preload Only What Defines the Above-the-Fold Experience",[15,588,589],{},"The preload rule that survives contact with production: preload a resource only if it is (a) needed for the first viewport, (b) discovered late by the parser, and (c) not already high priority. All three conditions, not any one.",[358,591,593],{"className":469,"code":592,"language":471,"meta":363,"style":363},"\u003C!-- Late-discovered, first-viewport, currently low priority: a genuine case. -->\n\u003Clink rel=\"preload\" href=\"\u002Ffonts\u002Finter-latin.woff2\" as=\"font\"\n      type=\"font\u002Fwoff2\" crossorigin>\n\u003Clink rel=\"preload\" href=\"\u002Fcss\u002Fcritical.css\" as=\"style\">\n\u003Clink rel=\"modulepreload\" href=\"\u002Fjs\u002Froute-home.js\">\n\u003C!-- trade-off: `as` is not optional metadata — it sets the priority and the\n     Accept header. A preload with the wrong `as` fetches a second copy at the\n     wrong priority, which is strictly worse than not preloading at all. -->\n",[28,594,595,600,628,642,670,692,697,702],{"__ignoreMap":363},[367,596,597],{"class":186,"line":369},[367,598,599],{"class":372},"\u003C!-- Late-discovered, first-viewport, currently low priority: a genuine case. -->\n",[367,601,602,604,606,608,610,613,615,617,620,623,625],{"class":186,"line":376},[367,603,484],{"class":483},[367,605,488],{"class":487},[367,607,491],{"class":390},[367,609,494],{"class":483},[367,611,612],{"class":383},"\"preload\"",[367,614,500],{"class":390},[367,616,494],{"class":483},[367,618,619],{"class":383},"\"\u002Ffonts\u002Finter-latin.woff2\"",[367,621,622],{"class":390}," as",[367,624,494],{"class":483},[367,626,627],{"class":383},"\"font\"\n",[367,629,630,633,635,638,640],{"class":186,"line":401},[367,631,632],{"class":390},"      type",[367,634,494],{"class":483},[367,636,637],{"class":383},"\"font\u002Fwoff2\"",[367,639,508],{"class":390},[367,641,511],{"class":483},[367,643,644,646,648,650,652,654,656,658,661,663,665,668],{"class":186,"line":416},[367,645,484],{"class":483},[367,647,488],{"class":487},[367,649,491],{"class":390},[367,651,494],{"class":483},[367,653,612],{"class":383},[367,655,500],{"class":390},[367,657,494],{"class":483},[367,659,660],{"class":383},"\"\u002Fcss\u002Fcritical.css\"",[367,662,622],{"class":390},[367,664,494],{"class":483},[367,666,667],{"class":383},"\"style\"",[367,669,511],{"class":483},[367,671,672,674,676,678,680,683,685,687,690],{"class":186,"line":424},[367,673,484],{"class":483},[367,675,488],{"class":487},[367,677,491],{"class":390},[367,679,494],{"class":483},[367,681,682],{"class":383},"\"modulepreload\"",[367,684,500],{"class":390},[367,686,494],{"class":483},[367,688,689],{"class":383},"\"\u002Fjs\u002Froute-home.js\"",[367,691,511],{"class":483},[367,693,694],{"class":186,"line":435},[367,695,696],{"class":372},"\u003C!-- trade-off: `as` is not optional metadata — it sets the priority and the\n",[367,698,699],{"class":186,"line":441},[367,700,701],{"class":372},"     Accept header. A preload with the wrong `as` fetches a second copy at the\n",[367,703,704],{"class":186,"line":572},[367,705,706],{"class":372},"     wrong priority, which is strictly worse than not preloading at all. -->\n",[15,708,709,710,713,714,717],{},"An image already in the initial HTML does not meet condition (b): the preload scanner finds it during parsing anyway, so ",[28,711,712],{},"fetchpriority=\"high\""," on the ",[28,715,716],{},"\u003Cimg>"," is the correct tool and a preload adds nothing. Fonts, on the other hand, are referenced from CSS and meet all three conditions, which is why they are the canonical preload example.",[15,719,720,721,36],{},"Chrome will warn in the console when a preloaded resource goes unused within a few seconds. Treat every such warning as a bug, because it means you spent critical-path bandwidth on nothing — the diagnostic walkthrough is in ",[19,722,724],{"href":723},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Ffixing-unused-preload-warnings-in-the-console\u002F","fixing unused preload warnings",[200,726,728],{"id":727},"_4-move-the-hints-into-response-headers","4. Move the Hints Into Response Headers",[15,730,731],{},"Markup hints are processed when the parser reaches them, which on a large document can be several kilobytes in. Header hints are processed the moment the response headers arrive — before a single byte of body is parsed.",[358,733,737],{"className":734,"code":735,"language":736,"meta":363,"style":363},"language-nginx shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Same hints, delivered a full document-parse earlier.\nlocation \u002F {\n  add_header Link '\u003C\u002Ffonts\u002Finter-latin.woff2>; rel=preload; as=font; type=\"font\u002Fwoff2\"; crossorigin' always;\n  add_header Link '\u003Chttps:\u002F\u002Fimages.example-cdn.com>; rel=preconnect' always;\n}\n# trade-off: header hints apply to EVERY response from this location, including\n# routes that do not use the font. Scope them per route, or you will preload a\n# heading font on an API response and pay for it on every request.\n","nginx",[28,738,739,744,755,769,780,785,790,795],{"__ignoreMap":363},[367,740,741],{"class":186,"line":369},[367,742,743],{"class":372},"# Same hints, delivered a full document-parse earlier.\n",[367,745,746,749,752],{"class":186,"line":376},[367,747,748],{"class":397},"location",[367,750,751],{"class":379}," \u002F ",[367,753,754],{"class":483},"{\n",[367,756,757,760,763,766],{"class":186,"line":401},[367,758,759],{"class":397},"  add_header ",[367,761,762],{"class":483},"Link ",[367,764,765],{"class":383},"'\u003C\u002Ffonts\u002Finter-latin.woff2>; rel=preload; as=font; type=\"font\u002Fwoff2\"; crossorigin'",[367,767,768],{"class":483}," always;\n",[367,770,771,773,775,778],{"class":186,"line":416},[367,772,759],{"class":397},[367,774,762],{"class":483},[367,776,777],{"class":383},"'\u003Chttps:\u002F\u002Fimages.example-cdn.com>; rel=preconnect'",[367,779,768],{"class":483},[367,781,782],{"class":186,"line":424},[367,783,784],{"class":483},"}\n",[367,786,787],{"class":186,"line":435},[367,788,789],{"class":372},"# trade-off: header hints apply to EVERY response from this location, including\n",[367,791,792],{"class":186,"line":441},[367,793,794],{"class":372},"# routes that do not use the font. Scope them per route, or you will preload a\n",[367,796,797],{"class":186,"line":572},[367,798,799],{"class":372},"# heading font on an API response and pay for it on every request.\n",[15,801,802],{},"Header hints are also the only form that CDNs can inject or rewrite at the edge, which matters when your origin is slow to render but your CDN knows the asset manifest for the route.",[200,804,806],{"id":805},"_5-send-103-early-hints-for-server-think-time","5. Send 103 Early Hints for Server Think Time",[15,808,809,810,813,814,817],{},"Early Hints is the logical endpoint of this progression. The server sends an informational ",[28,811,812],{},"103"," response carrying ",[28,815,816],{},"Link"," headers immediately, then continues generating the real response. The browser starts fetching the hinted resources during what would otherwise be dead time.",[358,819,823],{"className":820,"code":821,"language":822,"meta":363,"style":363},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Cloudflare Workers-style edge handler: hint first, render second.\nexport default {\n  async fetch(request, env, ctx) {\n    const hints = [\n      '\u003C\u002Fcss\u002Fcritical.css>; rel=preload; as=style',\n      '\u003C\u002Ffonts\u002Finter-latin.woff2>; rel=preload; as=font; crossorigin',\n      '\u003Chttps:\u002F\u002Fimages.example-cdn.com>; rel=preconnect'\n    ].join(', ');\n\n    \u002F\u002F The platform sends 103 with these headers while the origin still works.\n    const early = new Response(null, { status: 103, headers: { Link: hints } });\n    const real = await renderPage(request, env);\n    real.headers.set('Link', hints);   \u002F\u002F repeat for clients that ignored the 103\n    return real;\n  }\n};\n\u002F\u002F trade-off: Early Hints only pays off when server think time is real — a TTFB\n\u002F\u002F above roughly 200ms. On a page served from edge cache in 20ms there is no dead\n\u002F\u002F window to fill, and the extra round trip of informational headers can cost\n\u002F\u002F more than it saves. Measure TTFB first.\n","javascript",[28,824,825,830,841,855,869,877,884,889,906,913,919,948,967,987,996,1002,1011,1017,1023,1029],{"__ignoreMap":363},[367,826,827],{"class":186,"line":369},[367,828,829],{"class":372},"\u002F\u002F Cloudflare Workers-style edge handler: hint first, render second.\n",[367,831,832,835,838],{"class":186,"line":376},[367,833,834],{"class":397},"export",[367,836,837],{"class":397}," default",[367,839,840],{"class":379}," {\n",[367,842,843,846,850,853],{"class":186,"line":401},[367,844,845],{"class":397},"  async",[367,847,849],{"class":848},"smZ65"," fetch",[367,851,852],{"class":379},"(request, env, ctx) ",[367,854,754],{"class":483},[367,856,857,860,863,866],{"class":186,"line":416},[367,858,859],{"class":397},"    const",[367,861,862],{"class":390}," hints",[367,864,865],{"class":397}," =",[367,867,868],{"class":483}," [\n",[367,870,871,874],{"class":186,"line":424},[367,872,873],{"class":383},"      '\u003C\u002Fcss\u002Fcritical.css>; rel=preload; as=style'",[367,875,876],{"class":483},",\n",[367,878,879,882],{"class":186,"line":435},[367,880,881],{"class":383},"      '\u003C\u002Ffonts\u002Finter-latin.woff2>; rel=preload; as=font; crossorigin'",[367,883,876],{"class":483},[367,885,886],{"class":186,"line":441},[367,887,888],{"class":383},"      '\u003Chttps:\u002F\u002Fimages.example-cdn.com>; rel=preconnect'\n",[367,890,891,894,897,900,903],{"class":186,"line":572},[367,892,893],{"class":483},"    ].",[367,895,896],{"class":848},"join",[367,898,899],{"class":483},"(",[367,901,902],{"class":383},"', '",[367,904,905],{"class":483},");\n",[367,907,909],{"class":186,"line":908},9,[367,910,912],{"emptyLinePlaceholder":911},true,"\n",[367,914,916],{"class":186,"line":915},10,[367,917,918],{"class":372},"    \u002F\u002F The platform sends 103 with these headers while the origin still works.\n",[367,920,922,924,927,929,932,935,937,940,943,945],{"class":186,"line":921},11,[367,923,859],{"class":397},[367,925,926],{"class":390}," early",[367,928,865],{"class":397},[367,930,931],{"class":397}," new",[367,933,934],{"class":848}," Response",[367,936,899],{"class":483},[367,938,939],{"class":390},"null",[367,941,942],{"class":483},", { status: ",[367,944,812],{"class":390},[367,946,947],{"class":483},", headers: { Link: hints } });\n",[367,949,951,953,956,958,961,964],{"class":186,"line":950},12,[367,952,859],{"class":397},[367,954,955],{"class":390}," real",[367,957,865],{"class":397},[367,959,960],{"class":397}," await",[367,962,963],{"class":848}," renderPage",[367,965,966],{"class":483},"(request, env);\n",[367,968,970,973,976,978,981,984],{"class":186,"line":969},13,[367,971,972],{"class":483},"    real.headers.",[367,974,975],{"class":848},"set",[367,977,899],{"class":483},[367,979,980],{"class":383},"'Link'",[367,982,983],{"class":483},", hints);   ",[367,985,986],{"class":372},"\u002F\u002F repeat for clients that ignored the 103\n",[367,988,990,993],{"class":186,"line":989},14,[367,991,992],{"class":397},"    return",[367,994,995],{"class":483}," real;\n",[367,997,999],{"class":186,"line":998},15,[367,1000,1001],{"class":483},"  }\n",[367,1003,1005,1008],{"class":186,"line":1004},16,[367,1006,1007],{"class":379},"}",[367,1009,1010],{"class":483},";\n",[367,1012,1014],{"class":186,"line":1013},17,[367,1015,1016],{"class":372},"\u002F\u002F trade-off: Early Hints only pays off when server think time is real — a TTFB\n",[367,1018,1020],{"class":186,"line":1019},18,[367,1021,1022],{"class":372},"\u002F\u002F above roughly 200ms. On a page served from edge cache in 20ms there is no dead\n",[367,1024,1026],{"class":186,"line":1025},19,[367,1027,1028],{"class":372},"\u002F\u002F window to fill, and the extra round trip of informational headers can cost\n",[367,1030,1032],{"class":186,"line":1031},20,[367,1033,1034],{"class":372},"\u002F\u002F more than it saves. Measure TTFB first.\n",[15,1036,1037,1038,1041,1042,1045,1046,36],{},"The gain is proportional to how long your origin thinks. A personalised page with a ",[28,1039,1040],{},"600ms"," TTFB can start its CSS and font downloads ",[28,1043,1044],{},"500ms"," earlier — often the single largest LCP improvement available without touching the application. A statically cached page gains almost nothing. CDN-specific configuration and the caching interactions are covered in ",[19,1047,1049],{"href":1048},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Fusing-103-early-hints-behind-a-cdn\u002F","using 103 Early Hints behind a CDN",[200,1051,1053],{"id":1052},"deconstructing-priority-what-hints-actually-change","Deconstructing Priority: What Hints Actually Change",[15,1055,1056],{},"A hint's effect is mediated entirely by the browser's priority system, so it helps to know what you are moving.",[326,1058,1059,1071,1085],{},[329,1060,1061,1064,1065,1068,1069,36],{},[343,1062,1063],{},"Highest \u002F High"," — HTML, CSS in ",[28,1066,1067],{},"\u003Chead>",", synchronous scripts before the first image, fonts that are actively blocking text, and any resource marked ",[28,1070,712],{},[329,1072,1073,1076,1077,1080,1081,1084],{},[343,1074,1075],{},"Medium"," — images in the initial viewport once discovered, ",[28,1078,1079],{},"defer"," and ",[28,1082,1083],{},"async"," scripts after the parser is finished.",[329,1086,1087,1089,1090,1092],{},[343,1088,450],{}," — images outside the viewport, ",[28,1091,291],{}," targets, and most third-party requests.",[15,1094,1095,1097,1098,1101,1102,1104,1105,1108,1109,1112],{},[28,1096,135],{}," promotes a resource into the band implied by its ",[28,1099,1100],{},"as"," value. That is why the wrong ",[28,1103,1100],{}," is worse than no hint: ",[28,1106,1107],{},"as=\"fetch\""," on a stylesheet requests it at a lower band ",[33,1110,1111],{},"and"," under different credentials, producing a duplicate request that the CSS parser later ignores.",[15,1114,1115,1116,1119,1120,1123],{},"The corollary is that hints cannot create bandwidth. On a constrained connection, promoting three resources demotes everything else by the same amount. If your LCP image and your hero font are both ",[28,1117,1118],{},"High",", they now share the pipe, and one of them arrives later than it did before. Always re-check the LCP timestamp after adding a hint; a faster font that costs ",[28,1121,1122],{},"120ms"," of LCP is a net loss.",[200,1125,1127],{"id":1126},"advanced-diagnostics-and-failure-modes","Advanced Diagnostics and Failure Modes",[15,1129,1130,1133,1134,1136,1137,1139,1140,1143,1144,1146],{},[343,1131,1132],{},"The duplicated-request signature."," Two requests for the same URL, one from the preload and one from the real use, means a mismatch in ",[28,1135,1100],{},", ",[28,1138,581],{},", or ",[28,1141,1142],{},"type",". Fonts are the usual victim because they are always fetched anonymously — a font preload without ",[28,1145,581],{}," guarantees a duplicate download.",[15,1148,1149,54,1152,1154,1155,1158],{},[343,1150,1151],{},"Prefetch competing with the current page.",[28,1153,291],{}," is low priority, but on a slow connection low priority still consumes bytes. Prefetching the next route during the initial load can delay LCP on the current one. Gate prefetches behind an idle callback or a hover intent, and skip them entirely when ",[28,1156,1157],{},"navigator.connection.saveData"," is set.",[15,1160,1161,1164],{},[343,1162,1163],{},"Hints that outlive the resource they point at."," Content-hashed filenames change on every build. A hint written into an nginx config or a CDN rule quickly points at a file that no longer exists, producing a 404 at high priority — the worst of both worlds. Generate header hints from the same build manifest that produces the filenames, never by hand.",[15,1166,1167,1170,1171,1173],{},[343,1168,1169],{},"Early Hints and caching interact."," A cached ",[28,1172,812],{}," must be revalidated with the response it accompanied, or you will hint assets from a previous deploy. Ensure your CDN keys the informational response together with the main response, and confirm with a hard-refresh test after a deploy that changes the asset manifest.",[15,1175,1176,1179,1180,36],{},[343,1177,1178],{},"Cache-partitioned connections."," Preconnecting to an origin the page never uses is not just neutral — it consumes a connection slot and, on some mobile radios, keeps the radio in a high-power state. Audit preconnects whenever a vendor is removed; they are the most commonly orphaned tag in a ",[28,1181,1067],{},[200,1183,1185],{"id":1184},"validation-and-budgeting","Validation and Budgeting",[15,1187,1188],{},"Hints are easy to add and easy to leave behind, so the budget should test for waste as well as for absence.",[358,1190,1194],{"className":1191,"code":1192,"language":1193,"meta":363,"style":363},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","{\n  \"ci\": {\n    \"assert\": {\n      \"assertions\": {\n        \"uses-rel-preconnect\": [\"warn\", { \"minScore\": 1 }],\n        \"largest-contentful-paint\": [\"error\", { \"maxNumericValue\": 2500 }],\n        \"server-response-time\": [\"warn\", { \"maxNumericValue\": 400 }],\n        \"render-blocking-resources\": [\"error\", { \"maxNumericValue\": 300 }]\n      }\n    }\n  }\n}\n","json",[28,1195,1196,1200,1208,1215,1222,1247,1269,1289,1310,1315,1320,1324],{"__ignoreMap":363},[367,1197,1198],{"class":186,"line":369},[367,1199,754],{"class":483},[367,1201,1202,1205],{"class":186,"line":376},[367,1203,1204],{"class":487},"  \"ci\"",[367,1206,1207],{"class":483},": {\n",[367,1209,1210,1213],{"class":186,"line":401},[367,1211,1212],{"class":487},"    \"assert\"",[367,1214,1207],{"class":483},[367,1216,1217,1220],{"class":186,"line":416},[367,1218,1219],{"class":487},"      \"assertions\"",[367,1221,1207],{"class":483},[367,1223,1224,1227,1230,1233,1236,1239,1242,1244],{"class":186,"line":424},[367,1225,1226],{"class":487},"        \"uses-rel-preconnect\"",[367,1228,1229],{"class":483},": [",[367,1231,1232],{"class":383},"\"warn\"",[367,1234,1235],{"class":483},", { ",[367,1237,1238],{"class":487},"\"minScore\"",[367,1240,1241],{"class":483},": ",[367,1243,65],{"class":390},[367,1245,1246],{"class":483}," }],\n",[367,1248,1249,1252,1254,1257,1259,1262,1264,1267],{"class":186,"line":435},[367,1250,1251],{"class":487},"        \"largest-contentful-paint\"",[367,1253,1229],{"class":483},[367,1255,1256],{"class":383},"\"error\"",[367,1258,1235],{"class":483},[367,1260,1261],{"class":487},"\"maxNumericValue\"",[367,1263,1241],{"class":483},[367,1265,1266],{"class":390},"2500",[367,1268,1246],{"class":483},[367,1270,1271,1274,1276,1278,1280,1282,1284,1287],{"class":186,"line":441},[367,1272,1273],{"class":487},"        \"server-response-time\"",[367,1275,1229],{"class":483},[367,1277,1232],{"class":383},[367,1279,1235],{"class":483},[367,1281,1261],{"class":487},[367,1283,1241],{"class":483},[367,1285,1286],{"class":390},"400",[367,1288,1246],{"class":483},[367,1290,1291,1294,1296,1298,1300,1302,1304,1307],{"class":186,"line":572},[367,1292,1293],{"class":487},"        \"render-blocking-resources\"",[367,1295,1229],{"class":483},[367,1297,1256],{"class":383},[367,1299,1235],{"class":483},[367,1301,1261],{"class":487},[367,1303,1241],{"class":483},[367,1305,1306],{"class":390},"300",[367,1308,1309],{"class":483}," }]\n",[367,1311,1312],{"class":186,"line":908},[367,1313,1314],{"class":483},"      }\n",[367,1316,1317],{"class":186,"line":915},[367,1318,1319],{"class":483},"    }\n",[367,1321,1322],{"class":186,"line":921},[367,1323,1001],{"class":483},[367,1325,1326],{"class":186,"line":950},[367,1327,784],{"class":483},[15,1329,1330],{},"Then add a check no standard audit performs: fail the build if the console emitted an unused-preload warning during the run. Lighthouse surfaces these in its console messages, and a simple assertion over that list catches the most common regression — a preload left in place after the asset it named was renamed or removed.",[15,1332,1333],{},"In the field, the signal to watch is the LCP resource-load-delay phase. That phase is exactly the discovery gap hints exist to close, so a successful hinting change shows up there first and most clearly, well before it moves the aggregate p75.",[200,1335,1337],{"id":1336},"faq","FAQ",[1339,1340,1343,1347],"details",{"className":1341},[1342],"faq-item",[1344,1345,1346],"summary",{},"Should I preload my LCP image?",[15,1348,1349,1350,1352,1353,1355,1356,1359],{},"Usually no — mark it ",[28,1351,712],{}," instead. An ",[28,1354,716],{}," in the initial HTML is already found by the preload scanner during parsing, so a preload adds a second discovery path without making the fetch any earlier, and it risks a duplicate request if the attributes do not match exactly. Preload genuinely helps when the LCP image is ",[33,1357,1358],{},"not"," discoverable from the HTML: a CSS background image, or an image whose URL is computed by JavaScript.",[1339,1361,1363,1366],{"className":1362},[1342],[1344,1364,1365],{},"How many preconnect hints are too many?",[15,1367,1368,1369,1371],{},"Two or three is the practical ceiling for the critical path. Each preconnect holds an open connection for about ten seconds and competes with the connections already carrying your HTML and CSS, so the fourth and fifth hints usually cost more than they save. Use ",[28,1370,236],{}," for the remaining likely-but-not-certain origins — it resolves the name without opening a socket.",[1339,1373,1375,1378],{"className":1374},[1342],[1344,1376,1377],{},"Do Early Hints help a page served from the CDN cache?",[15,1379,1380],{},"Barely. Early Hints fills server think time, and a page served from edge cache has almost none — the HTML arrives in tens of milliseconds, so there is no window in which to start other downloads. The technique pays off on personalised, uncacheable or slow-origin responses where TTFB is a few hundred milliseconds or more. Measure TTFB per route and enable it where the dead time is real.",[1339,1382,1384,1387],{"className":1383},[1342],[1344,1385,1386],{},"Why does Chrome say my preloaded resource was not used?",[15,1388,1389,1390,1392,1393,1395,1396,1398],{},"Because the request the preload made and the request the page later made are not identical. The usual causes are a missing ",[28,1391,581],{}," on a font, a wrong or absent ",[28,1394,1100],{}," value, a differing ",[28,1397,1142],{},", or a media query that excluded the resource on this viewport. Compare the two entries in the Network panel side by side — if any of the request attributes differ, the browser treats them as different resources and downloads both.",[200,1400,1402],{"id":1401},"related","Related",[326,1404,1405,1412,1419,1425,1431,1437],{},[329,1406,1407,1411],{},[19,1408,1410],{"href":1409},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002F","HTTP Cache-Control Headers Explained"," — make the resources you hint cacheable so repeat visits skip the fetch entirely.",[329,1413,1414,1418],{},[19,1415,1417],{"href":1416},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fcdn-edge-caching-configuration\u002F","CDN Edge Caching Configuration"," — where header hints are injected and how edge caching changes their value.",[329,1420,1421,1424],{},[19,1422,1423],{"href":319},"Preload vs preconnect: when to use each"," — pick the right hint for a known origin versus a known file.",[329,1426,1427,1430],{},[19,1428,1429],{"href":1048},"Using 103 Early Hints behind a CDN"," — configuration, caching interactions and measurement.",[329,1432,1433,1436],{},[19,1434,1435],{"href":723},"Fixing unused-preload warnings"," — find the attribute mismatch behind a duplicated request.",[329,1438,1439,1443],{},[19,1440,1442],{"href":1441},"\u002Fcore-web-vitals-measurement\u002Ffont-loading-and-text-rendering-performance\u002F","Font Loading & Text Rendering Performance"," — the resource class that benefits most from a correctly written preload.",[1445,1446,1448],"script",{"type":1447},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Apply resource hints and Early Hints correctly\",\n  \"description\": \"Establish the critical path, preconnect the origins that matter, preload only late-discovered above-the-fold resources, move hints into response headers, and send 103 Early Hints where server think time is real.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Map the critical path\", \"text\": \"List requests in start order with their priorities to see what is discovered late and what is already high priority.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#1-establish-the-critical-path-before-adding-any-hint\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Preconnect key origins\", \"text\": \"Open connections to the two or three origins the critical path needs, matching crossorigin to how the resource will be fetched.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#2-preconnect-the-two-origins-that-matter\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Preload selectively\", \"text\": \"Preload only resources that are needed above the fold, discovered late, and not already high priority, with a correct as value.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#3-preload-only-what-defines-the-above-the-fold-experience\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Deliver hints in headers\", \"text\": \"Move hints from markup into Link response headers so they are processed before the document body is parsed.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#4-move-the-hints-into-response-headers\" },\n    { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Send 103 Early Hints\", \"text\": \"Emit an informational 103 response with the same Link headers so downloads start during server think time.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#5-send-103-early-hints-for-server-think-time\" },\n    { \"@type\": \"HowToStep\", \"position\": 6, \"name\": \"Budget and detect waste\", \"text\": \"Assert LCP and render-blocking budgets in CI and fail the build on unused-preload console warnings.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F#validation-and-budgeting\" }\n  ]\n}\n",[1445,1450,1451],{"type":1447},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Resource Hints & Early Hints: Buying Back the Discovery Delay\",\n  \"description\": \"How to use preconnect, preload, modulepreload, prefetch and 103 Early Hints to start critical requests earlier without starving the LCP resource.\",\n  \"datePublished\": \"2026-08-01\",\n  \"dateModified\": \"2026-08-01\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F\" }\n}\n",[1445,1453,1454],{"type":1447},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    { \"@type\": \"Question\", \"name\": \"Should I preload my LCP image?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Usually no — use fetchpriority=\\\"high\\\" instead. An img in the initial HTML is already found by the preload scanner, so a preload adds a second discovery path and risks a duplicate request. Preload helps when the LCP image is a CSS background or a JavaScript-computed URL.\" } },\n    { \"@type\": \"Question\", \"name\": \"How many preconnect hints are too many?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Two or three for the critical path. Each holds a connection open for about ten seconds and competes with the connections carrying your HTML and CSS. Use dns-prefetch for the remaining likely-but-uncertain origins.\" } },\n    { \"@type\": \"Question\", \"name\": \"Do Early Hints help a page served from the CDN cache?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Barely. Early Hints fills server think time, and an edge-cached page has almost none. It pays off on personalised, uncacheable or slow-origin responses where TTFB is a few hundred milliseconds or more.\" } },\n    { \"@type\": \"Question\", \"name\": \"Why does Chrome say my preloaded resource was not used?\", \"acceptedAnswer\": { \"@type\": \"Answer\", \"text\": \"Because the preload request and the later real request are not identical. Common causes are a missing crossorigin on a font, a wrong or absent as value, a differing type, or a media query that excluded the resource on this viewport.\" } }\n  ]\n}\n",[1445,1456,1457],{"type":1447},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Advanced Caching Strategies & CDN Architecture\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Resource Hints & Early Hints\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F\" }\n  ]\n}\n",[1459,1460,1461],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}",{"title":363,"searchDepth":376,"depth":376,"links":1463},[1464,1465,1466,1467,1468,1469,1470,1471,1472,1473,1474,1475],{"id":202,"depth":376,"text":203},{"id":323,"depth":376,"text":324},{"id":352,"depth":376,"text":353},{"id":462,"depth":376,"text":463},{"id":585,"depth":376,"text":586},{"id":727,"depth":376,"text":728},{"id":805,"depth":376,"text":806},{"id":1052,"depth":376,"text":1053},{"id":1126,"depth":376,"text":1127},{"id":1184,"depth":376,"text":1185},{"id":1336,"depth":376,"text":1337},{"id":1401,"depth":376,"text":1402},"Start critical requests earlier with preconnect, preload, modulepreload and 103 Early Hints — without stealing bandwidth from the resources that define LCP.","md",{"slug":1479,"type":1480,"breadcrumb":1481,"datePublished":1488,"dateModified":1488},"resource-hints-and-early-hints","topic",[1482,1485,1486],{"name":1483,"url":1484},"Home","\u002F",{"name":22,"url":21},{"name":5,"url":1487},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002F","2026-08-01","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints",{"title":1491,"description":1492},"Resource Hints & 103 Early Hints at the Edge","Use preconnect, preload, modulepreload, prefetch and 103 Early Hints correctly: priority effects, ordering rules, CDN support, and how to avoid wasting bandwidth.","advanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Findex","l4Eep23o-KcwCtiyN6kaUjy0aqfvZQR2CHFY_y46BbQ",[1496,1500],{"title":1497,"path":1498,"stem":1499,"children":-1},"Setting up immutable cache headers for hashed assets","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002Fsetting-up-immutable-cache-headers-for-hashed-assets","advanced-caching-strategies-cdn-architecture\u002Fhttp-cache-control-headers-explained\u002Fsetting-up-immutable-cache-headers-for-hashed-assets\u002Findex",{"title":1501,"path":1502,"stem":1503,"children":-1},"Fixing Unused-Preload Warnings in the Console","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Ffixing-unused-preload-warnings-in-the-console","advanced-caching-strategies-cdn-architecture\u002Fresource-hints-and-early-hints\u002Ffixing-unused-preload-warnings-in-the-console\u002Findex",1785622405979]