[{"data":1,"prerenderedAt":1748},["ShallowReactive",2],{"navigation":3,"-guide-middleware":61,"-guide-middleware-surround":1743},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Getting Started","/guide","1.guide/01.index",[9,11,16,21,26,31,36,41,46,51,56],{"title":5,"path":6,"stem":7,"icon":10},"ph:book-open-duotone",{"title":12,"path":13,"stem":14,"icon":15},"Fetch Handler","/guide/handler","1.guide/02.handler","i-fluent:target-24-regular",{"title":17,"path":18,"stem":19,"icon":20},"Server Instance","/guide/server","1.guide/03.server","radix-icons:component-instance",{"title":22,"path":23,"stem":24,"icon":25},"Middleware","/guide/middleware","1.guide/04.middleware","clarity:plugin-line",{"title":27,"path":28,"stem":29,"icon":30},"Server Options","/guide/options","1.guide/05.options","ri:settings-3-line",{"title":32,"path":33,"stem":34,"icon":35},"TLS","/guide/tls","1.guide/06.tls","ri:lock-2-line",{"title":37,"path":38,"stem":39,"icon":40},"Bundler Usage","/guide/bundler","1.guide/07.bundler","clarity:bundle-line",{"title":42,"path":43,"stem":44,"icon":45},"Node.js Support","/guide/node","1.guide/08.node","akar-icons:node-fill",{"title":47,"path":48,"stem":49,"icon":50},"Using CLI","/guide/cli","1.guide/10.cli","garden:terminal-cli-stroke-12",{"title":52,"path":53,"stem":54,"icon":55},"Body Size Limiting","/guide/body-limit","1.guide/11.body-limit","ri:scales-3-line",{"title":57,"path":58,"stem":59,"icon":60},"AWS Lambda","/guide/aws-lambda","1.guide/9.aws-lambda","clarity:cloud-traffic-line",{"id":62,"title":22,"body":63,"description":1737,"extension":1738,"meta":1739,"navigation":1740,"path":23,"seo":1741,"stem":24,"__hash__":1742},"content/1.guide/04.middleware.md",{"type":64,"value":65,"toc":1727,"icon":25},"minimark",[66,71,456,460,481,491,495,502,596,601,609,633,712,730,743,750,768,815,844,848,871,957,963,969,1150,1186,1221,1232,1249,1298,1324,1349,1356,1380,1395,1403,1407,1427,1431,1462,1679,1700,1713,1723],[67,68,70],"h2",{"id":69},"example","Example",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-ts shiki shiki-themes github-light github-dark github-dark","import { serve, type ServerMiddleware, type ServerPlugin } from \"srvx\";\n\nconst xPoweredBy: ServerMiddleware = async (req, next) => {\n  const res = await next();\n  res.headers.set(\"X-Powered-By\", \"srvx\");\n  return res;\n};\n\nconst devLogs: ServerPlugin = (server) => {\n  if (process.env.NODE_ENV === \"production\") {\n    return;\n  }\n  console.log(`Logger plugin enabled!`);\n  server.options.middleware.push((req, next) => {\n    console.log(`[request] [${req.method}] ${req.url}`);\n    return next();\n  });\n};\n\nserve({\n  middleware: [xPoweredBy],\n  plugins: [devLogs],\n  fetch(request) {\n    return new Response(`👋 Hello there.`);\n  },\n});\n","ts","",[79,80,81,115,122,166,187,210,219,225,230,256,277,285,291,307,331,367,376,382,387,392,401,407,413,426,444,450],"code",{"__ignoreMap":77},[82,83,86,90,94,97,100,102,105,108,112],"span",{"class":84,"line":85},"line",1,[82,87,89],{"class":88},"so5gQ","import",[82,91,93],{"class":92},"slsVL"," { serve, ",[82,95,96],{"class":88},"type",[82,98,99],{"class":92}," ServerMiddleware, ",[82,101,96],{"class":88},[82,103,104],{"class":92}," ServerPlugin } ",[82,106,107],{"class":88},"from",[82,109,111],{"class":110},"sfrk1"," \"srvx\"",[82,113,114],{"class":92},";\n",[82,116,118],{"class":84,"line":117},2,[82,119,121],{"emptyLinePlaceholder":120},true,"\n",[82,123,125,128,132,135,138,141,144,147,151,154,157,160,163],{"class":84,"line":124},3,[82,126,127],{"class":88},"const",[82,129,131],{"class":130},"shcOC"," xPoweredBy",[82,133,134],{"class":88},":",[82,136,137],{"class":130}," ServerMiddleware",[82,139,140],{"class":88}," =",[82,142,143],{"class":88}," async",[82,145,146],{"class":92}," (",[82,148,150],{"class":149},"sQHwn","req",[82,152,153],{"class":92},", ",[82,155,156],{"class":149},"next",[82,158,159],{"class":92},") ",[82,161,162],{"class":88},"=>",[82,164,165],{"class":92}," {\n",[82,167,169,172,176,178,181,184],{"class":84,"line":168},4,[82,170,171],{"class":88},"  const",[82,173,175],{"class":174},"suiK_"," res",[82,177,140],{"class":88},[82,179,180],{"class":88}," await",[82,182,183],{"class":130}," next",[82,185,186],{"class":92},"();\n",[82,188,190,193,196,199,202,204,207],{"class":84,"line":189},5,[82,191,192],{"class":92},"  res.headers.",[82,194,195],{"class":130},"set",[82,197,198],{"class":92},"(",[82,200,201],{"class":110},"\"X-Powered-By\"",[82,203,153],{"class":92},[82,205,206],{"class":110},"\"srvx\"",[82,208,209],{"class":92},");\n",[82,211,213,216],{"class":84,"line":212},6,[82,214,215],{"class":88},"  return",[82,217,218],{"class":92}," res;\n",[82,220,222],{"class":84,"line":221},7,[82,223,224],{"class":92},"};\n",[82,226,228],{"class":84,"line":227},8,[82,229,121],{"emptyLinePlaceholder":120},[82,231,233,235,238,240,243,245,247,250,252,254],{"class":84,"line":232},9,[82,234,127],{"class":88},[82,236,237],{"class":130}," devLogs",[82,239,134],{"class":88},[82,241,242],{"class":130}," ServerPlugin",[82,244,140],{"class":88},[82,246,146],{"class":92},[82,248,249],{"class":149},"server",[82,251,159],{"class":92},[82,253,162],{"class":88},[82,255,165],{"class":92},[82,257,259,262,265,268,271,274],{"class":84,"line":258},10,[82,260,261],{"class":88},"  if",[82,263,264],{"class":92}," (process.env.",[82,266,267],{"class":174},"NODE_ENV",[82,269,270],{"class":88}," ===",[82,272,273],{"class":110}," \"production\"",[82,275,276],{"class":92},") {\n",[82,278,280,283],{"class":84,"line":279},11,[82,281,282],{"class":88},"    return",[82,284,114],{"class":92},[82,286,288],{"class":84,"line":287},12,[82,289,290],{"class":92},"  }\n",[82,292,294,297,300,302,305],{"class":84,"line":293},13,[82,295,296],{"class":92},"  console.",[82,298,299],{"class":130},"log",[82,301,198],{"class":92},[82,303,304],{"class":110},"`Logger plugin enabled!`",[82,306,209],{"class":92},[82,308,310,313,316,319,321,323,325,327,329],{"class":84,"line":309},14,[82,311,312],{"class":92},"  server.options.middleware.",[82,314,315],{"class":130},"push",[82,317,318],{"class":92},"((",[82,320,150],{"class":149},[82,322,153],{"class":92},[82,324,156],{"class":149},[82,326,159],{"class":92},[82,328,162],{"class":88},[82,330,165],{"class":92},[82,332,334,337,339,341,344,346,349,352,355,357,359,362,365],{"class":84,"line":333},15,[82,335,336],{"class":92},"    console.",[82,338,299],{"class":130},[82,340,198],{"class":92},[82,342,343],{"class":110},"`[request] [${",[82,345,150],{"class":92},[82,347,348],{"class":110},".",[82,350,351],{"class":92},"method",[82,353,354],{"class":110},"}] ${",[82,356,150],{"class":92},[82,358,348],{"class":110},[82,360,361],{"class":92},"url",[82,363,364],{"class":110},"}`",[82,366,209],{"class":92},[82,368,370,372,374],{"class":84,"line":369},16,[82,371,282],{"class":88},[82,373,183],{"class":130},[82,375,186],{"class":92},[82,377,379],{"class":84,"line":378},17,[82,380,381],{"class":92},"  });\n",[82,383,385],{"class":84,"line":384},18,[82,386,224],{"class":92},[82,388,390],{"class":84,"line":389},19,[82,391,121],{"emptyLinePlaceholder":120},[82,393,395,398],{"class":84,"line":394},20,[82,396,397],{"class":130},"serve",[82,399,400],{"class":92},"({\n",[82,402,404],{"class":84,"line":403},21,[82,405,406],{"class":92},"  middleware: [xPoweredBy],\n",[82,408,410],{"class":84,"line":409},22,[82,411,412],{"class":92},"  plugins: [devLogs],\n",[82,414,416,419,421,424],{"class":84,"line":415},23,[82,417,418],{"class":130},"  fetch",[82,420,198],{"class":92},[82,422,423],{"class":149},"request",[82,425,276],{"class":92},[82,427,429,431,434,437,439,442],{"class":84,"line":428},24,[82,430,282],{"class":88},[82,432,433],{"class":88}," new",[82,435,436],{"class":130}," Response",[82,438,198],{"class":92},[82,440,441],{"class":110},"`👋 Hello there.`",[82,443,209],{"class":92},[82,445,447],{"class":84,"line":446},25,[82,448,449],{"class":92},"  },\n",[82,451,453],{"class":84,"line":452},26,[82,454,455],{"class":92},"});\n",[67,457,459],{"id":458},"order-of-execution","Order of execution",[461,462,463,464,467,468,471,472,476,477,480],"p",{},"Middleware run in the order they appear in the ",[79,465,466],{},"middleware"," array, each wrapping the next, with your ",[79,469,470],{},"fetch"," handler at the center. A middleware that returns a response ",[473,474,475],"strong",{},"without"," calling ",[79,478,479],{},"next()"," short-circuits the rest of the chain — nothing after it runs.",[461,482,483,484,487,488,490],{},"Plugins are applied in ",[79,485,486],{},"plugins"," array order, before the server starts listening. A plugin that pushes middleware appends to the end of the array, so ",[79,489,466],{}," entries always run first.",[67,492,494],{"id":493},"built-in-middleware-and-plugins","Built-in middleware and plugins",[461,496,497,498,501],{},"srvx ships several optional extensions as separate subpath imports. All of them are opt-in — importing ",[79,499,500],{},"srvx"," alone pulls in none of them.",[503,504,505,524],"table",{},[506,507,508],"thead",{},[509,510,511,515,518,521],"tr",{},[512,513,514],"th",{},"Import",[512,516,517],{},"Export",[512,519,520],{},"Kind",[512,522,523],{},"Runtimes",[525,526,527,545,562,580],"tbody",{},[509,528,529,535,540,542],{},[530,531,532],"td",{},[79,533,534],{},"srvx/log",[530,536,537],{},[79,538,539],{},"loggerMiddleware()",[530,541,22],{},[530,543,544],{},"All",[509,546,547,552,557,559],{},[530,548,549],{},[79,550,551],{},"srvx/static",[530,553,554],{},[79,555,556],{},"staticMiddleware()",[530,558,22],{},[530,560,561],{},"Node, Deno, Bun",[509,563,564,569,574,577],{},[530,565,566],{},[79,567,568],{},"srvx/mtls",[530,570,571],{},[79,572,573],{},"mtlsPlugin()",[530,575,576],{},"Plugin",[530,578,579],{},"Node",[509,581,582,587,592,594],{},[530,583,584],{},[79,585,586],{},"srvx/tracing",[530,588,589],{},[79,590,591],{},"tracingPlugin()",[530,593,576],{},[530,595,561],{},[597,598,600],"h3",{"id":599},"request-logging","Request logging",[461,602,603,605,606,608],{},[79,604,539],{}," from ",[79,607,534],{}," prints one line per request with the method, URL, status, and duration.",[461,610,611,612,614,615,618,619,628,629,632],{},"Colors are disabled when ",[79,613,267],{}," is ",[79,616,617],{},"production",", as well as when ",[620,621,625],"a",{"href":622,"rel":623},"https://no-color.org/",[624],"nofollow",[79,626,627],{},"NO_COLOR"," is set or output is not a TTY. Set ",[79,630,631],{},"FORCE_COLOR"," to keep them in any of those cases.",[72,634,637],{"className":74,"code":635,"filename":636,"language":76,"meta":77,"style":77},"import { serve } from \"srvx\";\nimport { loggerMiddleware } from \"srvx/log\";\n\nserve({\n  middleware: [loggerMiddleware()],\n  fetch: () => new Response(\"👋 Hello there.\"),\n});\n","server.ts",[79,638,639,652,666,670,676,687,708],{"__ignoreMap":77},[82,640,641,643,646,648,650],{"class":84,"line":85},[82,642,89],{"class":88},[82,644,645],{"class":92}," { serve } ",[82,647,107],{"class":88},[82,649,111],{"class":110},[82,651,114],{"class":92},[82,653,654,656,659,661,664],{"class":84,"line":117},[82,655,89],{"class":88},[82,657,658],{"class":92}," { loggerMiddleware } ",[82,660,107],{"class":88},[82,662,663],{"class":110}," \"srvx/log\"",[82,665,114],{"class":92},[82,667,668],{"class":84,"line":124},[82,669,121],{"emptyLinePlaceholder":120},[82,671,672,674],{"class":84,"line":168},[82,673,397],{"class":130},[82,675,400],{"class":92},[82,677,678,681,684],{"class":84,"line":189},[82,679,680],{"class":92},"  middleware: [",[82,682,683],{"class":130},"loggerMiddleware",[82,685,686],{"class":92},"()],\n",[82,688,689,691,694,696,698,700,702,705],{"class":84,"line":212},[82,690,418],{"class":130},[82,692,693],{"class":92},": () ",[82,695,162],{"class":88},[82,697,433],{"class":88},[82,699,436],{"class":130},[82,701,198],{"class":92},[82,703,704],{"class":110},"\"👋 Hello there.\"",[82,706,707],{"class":92},"),\n",[82,709,710],{"class":84,"line":221},[82,711,455],{"class":92},[72,713,717],{"className":714,"code":715,"language":716,"meta":77,"style":77},"language-sh shiki shiki-themes github-light github-dark github-dark","[10:32:03 AM] GET http://localhost:3000/ [200] (1.42ms)\n","sh",[79,718,719],{"__ignoreMap":77},[82,720,721,724,727],{"class":84,"line":85},[82,722,723],{"class":92},"[10:32:03 AM] GET http://localhost:3000/ [200] (",[82,725,726],{"class":130},"1.42ms",[82,728,729],{"class":92},")\n",[461,731,732,733,735,736,738,739,742],{},"The duration is measured around ",[79,734,479],{},", so place ",[79,737,539],{}," first in the array for it to cover the whole chain. The ",[620,740,741],{"href":48},"CLI"," enables this middleware automatically.",[461,744,745],{},[473,746,747,749],{},[79,748,539],{}," options:",[751,752,753],"ul",{},[754,755,756,759,760,763,764,767],"li",{},[79,757,758],{},"batch",": Coalesce lines and write once per event-loop turn (default: ",[79,761,762],{},"true","). Set to ",[79,765,766],{},"false"," to hand each line to stdout as its request completes — slower under concurrency, but a line never waits for a flush turn.",[72,769,771],{"className":74,"code":770,"language":76,"meta":77,"style":77},"serve({\n  middleware: [loggerMiddleware({ batch: false })],\n  fetch: () => new Response(\"👋 Hello there.\"),\n});\n",[79,772,773,779,793,811],{"__ignoreMap":77},[82,774,775,777],{"class":84,"line":85},[82,776,397],{"class":130},[82,778,400],{"class":92},[82,780,781,783,785,788,790],{"class":84,"line":117},[82,782,680],{"class":92},[82,784,683],{"class":130},[82,786,787],{"class":92},"({ batch: ",[82,789,766],{"class":174},[82,791,792],{"class":92}," })],\n",[82,794,795,797,799,801,803,805,807,809],{"class":84,"line":124},[82,796,418],{"class":130},[82,798,693],{"class":92},[82,800,162],{"class":88},[82,802,433],{"class":88},[82,804,436],{"class":130},[82,806,198],{"class":92},[82,808,704],{"class":110},[82,810,707],{"class":92},[82,812,813],{"class":84,"line":168},[82,814,455],{"class":92},[461,816,817,820,821,824,825,828,829,828,832,835,836,839,840,843],{},[473,818,819],{},"Crash safety:"," buffered lines are flushed when the process ends — on normal exit, ",[79,822,823],{},"process.exit()",", uncaught exceptions, and ",[79,826,827],{},"SIGHUP","/",[79,830,831],{},"SIGTERM",[79,833,834],{},"SIGINT",", whether a shutdown handler (such as srvx's graceful shutdown) is installed or the signal is default-handled. Only a hard kill (",[79,837,838],{},"SIGKILL",", the OOM killer) can drop the lines of the final event-loop turn; ",[79,841,842],{},"batch: false"," narrows that window to the single write in flight.",[597,845,847],{"id":846},"static-files","Static files",[461,849,850,605,852,854,855,858,859,862,863,866,867,870],{},[79,851,556],{},[79,853,551],{}," serves files from a directory, with ",[79,856,857],{},"index.html"," resolution, ",[79,860,861],{},".html"," extension fallback (",[79,864,865],{},"/about"," → ",[79,868,869],{},"about.html","), common MIME types, gzip/Brotli compression, and path-traversal protection.",[72,872,874],{"className":74,"code":873,"filename":636,"language":76,"meta":77,"style":77},"import { serve } from \"srvx\";\nimport { staticMiddleware } from \"srvx/static\";\n\nserve({\n  middleware: [staticMiddleware({ dir: \"public\" })],\n  fetch: () => new Response(\"Not found\", { status: 404 }),\n});\n",[79,875,876,888,902,906,912,927,953],{"__ignoreMap":77},[82,877,878,880,882,884,886],{"class":84,"line":85},[82,879,89],{"class":88},[82,881,645],{"class":92},[82,883,107],{"class":88},[82,885,111],{"class":110},[82,887,114],{"class":92},[82,889,890,892,895,897,900],{"class":84,"line":117},[82,891,89],{"class":88},[82,893,894],{"class":92}," { staticMiddleware } ",[82,896,107],{"class":88},[82,898,899],{"class":110}," \"srvx/static\"",[82,901,114],{"class":92},[82,903,904],{"class":84,"line":124},[82,905,121],{"emptyLinePlaceholder":120},[82,907,908,910],{"class":84,"line":168},[82,909,397],{"class":130},[82,911,400],{"class":92},[82,913,914,916,919,922,925],{"class":84,"line":189},[82,915,680],{"class":92},[82,917,918],{"class":130},"staticMiddleware",[82,920,921],{"class":92},"({ dir: ",[82,923,924],{"class":110},"\"public\"",[82,926,792],{"class":92},[82,928,929,931,933,935,937,939,941,944,947,950],{"class":84,"line":212},[82,930,418],{"class":130},[82,932,693],{"class":92},[82,934,162],{"class":88},[82,936,433],{"class":88},[82,938,436],{"class":130},[82,940,198],{"class":92},[82,942,943],{"class":110},"\"Not found\"",[82,945,946],{"class":92},", { status: ",[82,948,949],{"class":174},"404",[82,951,952],{"class":92}," }),\n",[82,954,955],{"class":84,"line":221},[82,956,455],{"class":92},[461,958,959,960,962],{},"When no file matches the request, it calls ",[79,961,479],{}," — so your handler acts as the fallback for unmatched paths.",[461,964,965],{},[473,966,967,749],{},[79,968,556],{},[751,970,971,977,989,1025,1045,1056,1077,1094,1111,1130],{},[754,972,973,976],{},[79,974,975],{},"dir",": The directory to serve files from (required).",[754,978,979,982,983,986,987,348],{},[79,980,981],{},"methods",": HTTP methods to serve (default ",[79,984,985],{},"[\"GET\", \"HEAD\"]","). Other methods fall through to ",[79,988,479],{},[754,990,991,994,995,997,998,1001,1002,153,1005,1008,1009,1011,1012,1014,1015,1017,1018,1021,1022,348],{},[79,992,993],{},"dotfiles",": Dot segments (path segments starting with ",[79,996,348],{},") that may be served (default ",[79,999,1000],{},"[\".well-known\"]","). A path containing any other dot segment — ",[79,1003,1004],{},"/.env",[79,1006,1007],{},"/.git/config"," — falls through to ",[79,1010,479],{},". Pass ",[79,1013,762],{}," to serve every dot segment, or ",[79,1016,766],{}," (or ",[79,1019,1020],{},"[]",") to serve none, including ",[79,1023,1024],{},"/.well-known/",[754,1026,1027,1030,1031,1033,1034,1036,1037,1040,1041,1044],{},[79,1028,1029],{},"encodings",": Serve precompressed variants from disk (default ",[79,1032,766],{},"). Pass ",[79,1035,762],{}," for ",[79,1038,1039],{},"{ br: \".br\", gzip: \".gz\" }",", or a map setting the extension per encoding (keys tried in order, preferred first). Off by default because most deployments ship no precompressed files, so the lookup is a ",[79,1042,1043],{},"stat"," that always misses.",[754,1046,1047,1050,1051,1033,1053,1055],{},[79,1048,1049],{},"compress",": Compress a response on the fly when no precompressed variant is served (default ",[79,1052,762],{},[79,1054,766],{}," to serve only what is already on disk.",[754,1057,1058,1061,1062,1065,1066,1069,1070,1073,1074,1076],{},[79,1059,1060],{},"lastModified",": Emit a ",[79,1063,1064],{},"Last-Modified"," header from the file's modification time, and answer a matching ",[79,1067,1068],{},"If-Modified-Since"," request with ",[79,1071,1072],{},"304 Not Modified"," (default ",[79,1075,762],{},").",[754,1078,1079,1082,1083,1086,1087,1069,1090,1073,1092,1076],{},[79,1080,1081],{},"etag",": Emit a weak ",[79,1084,1085],{},"ETag"," validator, and answer a matching ",[79,1088,1089],{},"If-None-Match",[79,1091,1072],{},[79,1093,762],{},[754,1095,1096,1099,1100,1103,1104,1073,1107,1110],{},[79,1097,1098],{},"maxAge",": Freshness lifetime in ",[473,1101,1102],{},"seconds",", emitted as ",[79,1105,1106],{},"Cache-Control: max-age=\u003Cn>",[79,1108,1109],{},"undefined",", no header). Lets a client reuse a response without a request until it goes stale.",[754,1112,1113,1116,1117,1119,1120,1123,1124,1126,1127,1129],{},[79,1114,1115],{},"immutable",": Add the ",[79,1118,1115],{}," directive to ",[79,1121,1122],{},"Cache-Control",", so a client does not revalidate a still-fresh response even on reload (default ",[79,1125,766],{},"). Only takes effect alongside ",[79,1128,1098],{},", and only makes sense for fingerprinted (content-hashed) assets.",[754,1131,1132,1135,1136,1139,1140,153,1142,1145,1146,1149],{},[79,1133,1134],{},"renderHTML",": A function receiving ",[79,1137,1138],{},"{ request, html, filename }"," for every HTML file (",[79,1141,861],{},[79,1143,1144],{},".htm","), returning the ",[79,1147,1148],{},"Response"," to send. Use it to inject or template markup before serving.",[461,1151,1152,1153,1156,1157,1160,1161,1163,1164,1166,1167,1170,1171,1174,1175,1178,1179,1182,1183,348],{},"A request resolves in order: the path itself, then ",[79,1154,1155],{},"\u003Cpath>.html",", then ",[79,1158,1159],{},"\u003Cpath>/index.html",". So ",[79,1162,865],{}," serves ",[79,1165,869],{},", while an extension-less file (",[79,1168,1169],{},"LICENSE",", an ACME challenge token) is served at its exact name. A trailing-slash request names a directory, so ",[79,1172,1173],{},"/sub/"," resolves only ",[79,1176,1177],{},"sub/index.html"," — never ",[79,1180,1181],{},"sub.html"," or a file named ",[79,1184,1185],{},"sub",[461,1187,1188,1189,1191,1192,1195,1196,153,1199,1202,1203,1206,1207,1210,1211,1214,1215,1217,1218,1220],{},"By default a compressible response is compressed on the fly as it is sent. Enabling ",[79,1190,1029],{}," adds a disk lookup that takes precedence: for ",[79,1193,1194],{},"/app.js"," with ",[79,1197,1198],{},"Accept-Encoding: br",[79,1200,1201],{},"app.js.br"," is served if it exists (with ",[79,1204,1205],{},"Content-Encoding: br","), and only a missing variant falls back to on-the-fly. A variant always wins because it costs no CPU, and a build can afford a better ratio than a per-request encode can justify — so ",[79,1208,1209],{},"encodings: true"," plus a build step is the cheapest way to serve maximum-quality compressed assets. The two switches are independent: ",[79,1212,1213],{},"compress: false"," serves only what is on disk, and ",[79,1216,1029],{}," off with ",[79,1219,1049],{}," on always compresses on the fly.",[461,1222,1223,1224,1227,1228,1231],{},"Brotli compresses at quality 4 rather than the ",[79,1225,1226],{},"node:zlib"," default of 11, which costs roughly 12x the CPU for a few percent of size. Only files between 1 KiB and 10 MiB are compressed on the fly: below that the encoded body can come out larger than the input, and above it the CPU spent per request outweighs the bandwidth saved — precompress large assets instead. On-the-fly responses are chunked, since the encoded length is not known until the bytes exist, while a variant declares the ",[79,1229,1230],{},"Content-Length"," it has on disk.",[461,1233,1234,1235,1238,1239,1242,1243,1245,1246,1248],{},"Compression applies to compressible types only, so a ",[79,1236,1237],{},".br"," next to an image or font is ignored, and those responses omit ",[79,1240,1241],{},"Vary: Accept-Encoding"," — which compressible ones always set, including uncompressed ones, as a shared cache must key on the header either way. ",[79,1244,1134],{}," routes are never compressed and always read the source file: a variant on disk would not match the rendered output, and the ",[79,1247,1148],{}," the hook returns is the caller's to encode.",[461,1250,1251,1252,1254,1255,1257,1258,1260,1261,1263,1264,1266,1267,1269,1270,1275,1276,1278,1279,1282,1283,1286,1287,1290,1291,1294,1295,1297],{},"Every file served without ",[79,1253,1134],{}," carries an ",[79,1256,1085],{}," and a ",[79,1259,1064],{}," header, and a conditional request that still matches is answered with an empty ",[79,1262,1072],{}," before the body is ever read. ",[79,1265,1089],{}," takes precedence over ",[79,1268,1068],{},", matching ",[620,1271,1274],{"href":1272,"rel":1273},"https://www.rfc-editor.org/rfc/rfc9110#section-13.2.2",[624],"RFC 9110",". The ",[79,1277,1085],{}," is weak (",[79,1280,1281],{},"W/\"…\"","): it is derived from the file's size and modification time rather than its bytes, and folds in the ",[79,1284,1285],{},"Content-Encoding"," so a brotli and a gzip response under one URL get distinct validators. Pass ",[79,1288,1289],{},"etag: false"," or ",[79,1292,1293],{},"lastModified: false"," to drop either header and stop honoring its conditional. ",[79,1296,1134],{}," routes carry neither, since the rendered body is the caller's to validate.",[461,1299,1300,1302,1303,1305,1306,1308,1309,1312,1313,1316,1317,1320,1321,1323],{},[79,1301,1122],{}," is opt-in and off by default, so a client revalidates with those validators on every use. Set ",[79,1304,1098],{}," (in seconds) to send ",[79,1307,1106],{}," and let a client reuse a response without a request until it goes stale; add ",[79,1310,1311],{},"immutable: true"," to send ",[79,1314,1315],{},"max-age=\u003Cn>, immutable",", which also skips revalidation on an explicit reload — appropriate for a fingerprinted asset whose URL changes when its bytes do. The header rides along on the ",[79,1318,1319],{},"304"," too, so a revalidation refreshes the stored freshness. Like the validators, it is omitted on ",[79,1322,1134],{}," routes.",[461,1325,1326,1328,1329,1334,1335,1338,1339,1341,1342,1345,1346,1076],{},[79,1327,1024],{}," is served by default because ",[620,1330,1333],{"href":1331,"rel":1332},"https://www.rfc-editor.org/rfc/rfc8615",[624],"RFC 8615"," reserves it for public metadata: ACME HTTP-01 challenges and ",[79,1336,1337],{},"security.txt"," live there. Allow-listing is by exact segment name, so ",[79,1340,1000],{}," serves neither a sibling sharing its prefix (",[79,1343,1344],{},".well-known-backup",") nor a dot segment nested under it (",[79,1347,1348],{},".well-known/.env",[461,1350,1351,1352,1355],{},"Text responses declare ",[79,1353,1354],{},"charset=utf-8","; without it a browser decodes them with a fallback of its own choosing, mangling any non-ASCII byte the file does not declare inline.",[1357,1358,1359],"note",{},[461,1360,1361,1362,1364,1365,1367,1368,1370,1371,866,1374,1008,1377,1379],{},"\nFiles are only served from within ",[79,1363,975],{},", and both rules above are re-checked against the path a symlink actually resolves to. Symlinks are followed, but one resolving outside ",[79,1366,975],{}," — or onto a dot segment ",[79,1369,993],{}," hides, such as ",[79,1372,1373],{},"public.txt",[79,1375,1376],{},".env",[79,1378,479],{}," instead of being served.",[1357,1381,1382],{},[461,1383,1384,1385,614,1387,1390,1391,1394],{},"\nDespite the runtime-neutral name, ",[79,1386,551],{},[473,1388,1389],{},"Node-API-only"," — it uses ",[79,1392,1393],{},"node:fs"," internally, so it works only on runtimes with Node.js compatibility (Node, Deno, Bun).",[461,1396,1397,1398,1402],{},"See ",[620,1399,1401],{"href":1400},"/guide/cli#serving-static-files","Serving static files"," for the equivalent CLI flag.",[597,1404,1406],{"id":1405},"mutual-tls","Mutual TLS",[461,1408,1409,605,1414,1416,1417,1423,1424,348],{},[620,1410,1412],{"href":1411},"/guide/tls#mutual-tls-mtls",[79,1413,573],{},[79,1415,568],{}," requests a client certificate during the TLS handshake and exposes it on ",[620,1418,1420],{"href":1419},"/guide/handler#requesttls",[79,1421,1422],{},"request.tls",". It requires the ",[620,1425,1426],{"href":43},"Node.js adapter",[597,1428,1430],{"id":1429},"tracing","Tracing",[461,1432,1433,605,1435,1437,1438,1440,1441,1448,1449,1452,1453,1456,1457,348],{},[79,1434,591],{},[79,1436,586],{}," wraps your ",[79,1439,470],{}," handler and each middleware with ",[620,1442,1445],{"href":1443,"rel":1444},"https://nodejs.org/api/diagnostics_channel.html",[624],[79,1446,1447],{},"diagnostics_channel"," instrumentation, publishing to the ",[79,1450,1451],{},"srvx.request"," and ",[79,1454,1455],{},"srvx.middleware"," ",[620,1458,1461],{"href":1459,"rel":1460},"https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel",[624],"tracing channels",[72,1463,1465],{"className":74,"code":1464,"filename":636,"language":76,"meta":77,"style":77},"import { serve } from \"srvx\";\nimport { tracingPlugin } from \"srvx/tracing\";\nimport { tracingChannel } from \"node:diagnostics_channel\";\n\ntracingChannel(\"srvx.request\").subscribe({\n  start: ({ request }) => console.log(`[start] ${request.url}`),\n  asyncEnd: ({ request }) => console.log(`[end] ${request.url}`),\n  error: ({ request, error }) => console.error(`[error] ${request.url}`, error),\n});\n\nserve({\n  plugins: [tracingPlugin()],\n  fetch: () => new Response(\"👋 Hello there.\"),\n});\n",[79,1466,1467,1479,1493,1507,1511,1528,1563,1595,1633,1637,1641,1647,1657,1675],{"__ignoreMap":77},[82,1468,1469,1471,1473,1475,1477],{"class":84,"line":85},[82,1470,89],{"class":88},[82,1472,645],{"class":92},[82,1474,107],{"class":88},[82,1476,111],{"class":110},[82,1478,114],{"class":92},[82,1480,1481,1483,1486,1488,1491],{"class":84,"line":117},[82,1482,89],{"class":88},[82,1484,1485],{"class":92}," { tracingPlugin } ",[82,1487,107],{"class":88},[82,1489,1490],{"class":110}," \"srvx/tracing\"",[82,1492,114],{"class":92},[82,1494,1495,1497,1500,1502,1505],{"class":84,"line":124},[82,1496,89],{"class":88},[82,1498,1499],{"class":92}," { tracingChannel } ",[82,1501,107],{"class":88},[82,1503,1504],{"class":110}," \"node:diagnostics_channel\"",[82,1506,114],{"class":92},[82,1508,1509],{"class":84,"line":168},[82,1510,121],{"emptyLinePlaceholder":120},[82,1512,1513,1516,1518,1521,1523,1526],{"class":84,"line":189},[82,1514,1515],{"class":130},"tracingChannel",[82,1517,198],{"class":92},[82,1519,1520],{"class":110},"\"srvx.request\"",[82,1522,1076],{"class":92},[82,1524,1525],{"class":130},"subscribe",[82,1527,400],{"class":92},[82,1529,1530,1533,1536,1538,1541,1543,1546,1548,1550,1553,1555,1557,1559,1561],{"class":84,"line":212},[82,1531,1532],{"class":130},"  start",[82,1534,1535],{"class":92},": ({ ",[82,1537,423],{"class":149},[82,1539,1540],{"class":92}," }) ",[82,1542,162],{"class":88},[82,1544,1545],{"class":92}," console.",[82,1547,299],{"class":130},[82,1549,198],{"class":92},[82,1551,1552],{"class":110},"`[start] ${",[82,1554,423],{"class":92},[82,1556,348],{"class":110},[82,1558,361],{"class":92},[82,1560,364],{"class":110},[82,1562,707],{"class":92},[82,1564,1565,1568,1570,1572,1574,1576,1578,1580,1582,1585,1587,1589,1591,1593],{"class":84,"line":221},[82,1566,1567],{"class":130},"  asyncEnd",[82,1569,1535],{"class":92},[82,1571,423],{"class":149},[82,1573,1540],{"class":92},[82,1575,162],{"class":88},[82,1577,1545],{"class":92},[82,1579,299],{"class":130},[82,1581,198],{"class":92},[82,1583,1584],{"class":110},"`[end] ${",[82,1586,423],{"class":92},[82,1588,348],{"class":110},[82,1590,361],{"class":92},[82,1592,364],{"class":110},[82,1594,707],{"class":92},[82,1596,1597,1600,1602,1604,1606,1609,1611,1613,1615,1617,1619,1622,1624,1626,1628,1630],{"class":84,"line":227},[82,1598,1599],{"class":130},"  error",[82,1601,1535],{"class":92},[82,1603,423],{"class":149},[82,1605,153],{"class":92},[82,1607,1608],{"class":149},"error",[82,1610,1540],{"class":92},[82,1612,162],{"class":88},[82,1614,1545],{"class":92},[82,1616,1608],{"class":130},[82,1618,198],{"class":92},[82,1620,1621],{"class":110},"`[error] ${",[82,1623,423],{"class":92},[82,1625,348],{"class":110},[82,1627,361],{"class":92},[82,1629,364],{"class":110},[82,1631,1632],{"class":92},", error),\n",[82,1634,1635],{"class":84,"line":232},[82,1636,455],{"class":92},[82,1638,1639],{"class":84,"line":258},[82,1640,121],{"emptyLinePlaceholder":120},[82,1642,1643,1645],{"class":84,"line":279},[82,1644,397],{"class":130},[82,1646,400],{"class":92},[82,1648,1649,1652,1655],{"class":84,"line":287},[82,1650,1651],{"class":92},"  plugins: [",[82,1653,1654],{"class":130},"tracingPlugin",[82,1656,686],{"class":92},[82,1658,1659,1661,1663,1665,1667,1669,1671,1673],{"class":84,"line":293},[82,1660,418],{"class":130},[82,1662,693],{"class":92},[82,1664,162],{"class":88},[82,1666,433],{"class":88},[82,1668,436],{"class":130},[82,1670,198],{"class":92},[82,1672,704],{"class":110},[82,1674,707],{"class":92},[82,1676,1677],{"class":84,"line":309},[82,1678,455],{"class":92},[461,1680,1681,1682,1685,1686,1689,1690,1692,1693,1290,1696,1699],{},"Each event carries ",[79,1683,1684],{},"{ server, request }",", plus ",[79,1687,1688],{},"{ middleware: { index, handler } }"," on the ",[79,1691,1455],{}," channel. Pass ",[79,1694,1695],{},"{ fetch: false }",[79,1697,1698],{},"{ middleware: false }"," to instrument only one of the two.",[461,1701,1702,1703,1705,1706,1709,1710,1712],{},"Because plugins run in order, ",[79,1704,591],{}," only wraps middleware registered before it — keep it ",[473,1707,1708],{},"last"," in the ",[79,1711,486],{}," array so it covers middleware added by earlier plugins.",[1714,1715,1716],"important",{},[461,1717,1718,614,1720,348],{},[79,1719,586],{},[473,1721,1722],{},"experimental",[1724,1725,1726],"style",{},"html pre.shiki code .so5gQ, html code.shiki .so5gQ{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .slsVL, html code.shiki .slsVL{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sfrk1, html code.shiki .sfrk1{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .shcOC, html code.shiki .shcOC{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sQHwn, html code.shiki .sQHwn{--shiki-light:#E36209;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .suiK_, html code.shiki .suiK_{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}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 .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);}",{"title":77,"searchDepth":117,"depth":117,"links":1728},[1729,1730,1731],{"id":69,"depth":117,"text":70},{"id":458,"depth":117,"text":459},{"id":493,"depth":117,"text":494,"children":1732},[1733,1734,1735,1736],{"id":599,"depth":124,"text":600},{"id":846,"depth":124,"text":847},{"id":1405,"depth":124,"text":1406},{"id":1429,"depth":124,"text":1430},"Plugins and middleware allow adding reusable server extensions.","md",{"icon":25},{"icon":25},{"title":22,"description":1737},"-iUbYYWoGHJSr115GzjYn2a5CEHt7k464pZVcy8VYfc",[1744,1746],{"title":17,"path":18,"stem":19,"description":1745,"icon":20,"children":-1},"Control srvx server lifecycle.",{"title":27,"path":28,"stem":29,"description":1747,"icon":30,"children":-1},"Provide additional options to customize listening server.",1785329752784]