[{"data":1,"prerenderedAt":820},["ShallowReactive",2],{"navigation":3,"-guide-tls":61,"-guide-tls-surround":815},[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":32,"body":63,"description":809,"extension":810,"meta":811,"navigation":812,"path":33,"seo":813,"stem":34,"__hash__":814},"content/1.guide/06.tls.md",{"type":64,"value":65,"toc":802,"icon":35},"minimark",[66,88,93,117,217,228,250,264,282,295,298,315,322,358,603,618,624,663,669,678,735,739,798],[67,68,69,70,74,75,78,79,82,83,87],"p",{},"TLS (the successor of SSL) encrypts the connection between client and server using a ",[71,72,73],"strong",{},"certificate"," and its ",[71,76,77],{},"private key",". Serving over HTTPS in srvx is a matter of providing them. ",[71,80,81],{},"Mutual TLS (mTLS)"," goes one step further and also asks the ",[84,85,86],"em",{},"client"," to present a certificate, which the server verifies against a trusted CA — a common building block for service-to-service and zero-trust setups.",[89,90,92],"h2",{"id":91},"enabling-https","Enabling HTTPS",[67,94,95,96,100,101,104,105,112,113,116],{},"Provide ",[97,98,99],"code",{},"tls.cert"," and ",[97,102,103],{},"tls.key",". srvx switches the ",[106,107,109],"a",{"href":108},"/guide/options#protocol",[97,110,111],{},"protocol"," to ",[97,114,115],{},"https"," automatically.",[118,119,124],"pre",{"className":120,"code":121,"language":122,"meta":123,"style":123},"language-js shiki shiki-themes github-light github-dark github-dark","import { serve } from \"srvx\";\n\nserve({\n  tls: { cert: \"./server.crt\", key: \"./server.key\" },\n  fetch: () => new Response(\"👋 Secure hello!\"),\n});\n","js","",[97,125,126,149,156,166,184,211],{"__ignoreMap":123},[127,128,131,135,139,142,146],"span",{"class":129,"line":130},"line",1,[127,132,134],{"class":133},"so5gQ","import",[127,136,138],{"class":137},"slsVL"," { serve } ",[127,140,141],{"class":133},"from",[127,143,145],{"class":144},"sfrk1"," \"srvx\"",[127,147,148],{"class":137},";\n",[127,150,152],{"class":129,"line":151},2,[127,153,155],{"emptyLinePlaceholder":154},true,"\n",[127,157,159,163],{"class":129,"line":158},3,[127,160,162],{"class":161},"shcOC","serve",[127,164,165],{"class":137},"({\n",[127,167,169,172,175,178,181],{"class":129,"line":168},4,[127,170,171],{"class":137},"  tls: { cert: ",[127,173,174],{"class":144},"\"./server.crt\"",[127,176,177],{"class":137},", key: ",[127,179,180],{"class":144},"\"./server.key\"",[127,182,183],{"class":137}," },\n",[127,185,187,190,193,196,199,202,205,208],{"class":129,"line":186},5,[127,188,189],{"class":161},"  fetch",[127,191,192],{"class":137},": () ",[127,194,195],{"class":133},"=>",[127,197,198],{"class":133}," new",[127,200,201],{"class":161}," Response",[127,203,204],{"class":137},"(",[127,206,207],{"class":144},"\"👋 Secure hello!\"",[127,209,210],{"class":137},"),\n",[127,212,214],{"class":129,"line":213},6,[127,215,216],{"class":137},"});\n",[67,218,219],{},[71,220,221,227],{},[106,222,224],{"href":223},"/guide/options#tls",[97,225,226],{},"tls"," options:",[229,230,231,238,244],"ul",{},[232,233,234,237],"li",{},[97,235,236],{},"cert",": Certificate in PEM format — file path or inline content (required).",[232,239,240,243],{},[97,241,242],{},"key",": Private key in PEM format — file path or inline content (required).",[232,245,246,249],{},[97,247,248],{},"passphrase",": Passphrase for the private key (optional).",[67,251,252,253,256,257,100,260,263],{},"Server TLS works on ",[71,254,255],{},"Node.js",", ",[71,258,259],{},"Deno",[71,261,262],{},"Bun",".",[265,266,267],"tip",{},[67,268,269,271,272,271,274,277,278,281],{},[97,270,236],{},"/",[97,273,242],{},[97,275,276],{},"ca"," values are treated as inline PEM when they start with ",[97,279,280],{},"-----BEGIN ",", otherwise as file paths.",[283,284,285,287],"important",{},[67,286],{},[229,288,289,292],{},[232,290,291],{},"Never commit private keys; load them from environment variables or a secret manager.",[232,293,294],{},"Consider automatic certificate management (e.g. Let's Encrypt) for production.",[89,296,81],{"id":297},"mutual-tls-mtls",[67,299,300,301,304,305,308,309,263],{},"Mutual TLS is provided by the opt-in ",[97,302,303],{},"mtlsPlugin()"," from ",[97,306,307],{},"srvx/mtls",". It requests a client certificate during the handshake and exposes it — with the negotiated protocol and cipher — on ",[106,310,312],{"href":311},"#requesttls",[97,313,314],{},"request.tls",[67,316,317,318,321],{},"There are two places an unauthenticated client can be turned away, depending on ",[97,319,320],{},"rejectUnauthorized",":",[229,323,324,341],{},[232,325,326,333,334,336,337,340],{},[71,327,328,329,332],{},"TLS handshake (default, ",[97,330,331],{},"rejectUnauthorized: true",")"," — a client without a certificate signed by a trusted ",[97,335,276],{}," never completes the handshake. The connection is dropped before any request reaches your ",[97,338,339],{},"fetch"," handler, so there is nothing to check for in application code.",[232,342,343,349,350,353,354,357],{},[71,344,345,346,332],{},"Application layer (",[97,347,348],{},"rejectUnauthorized: false"," — every handshake is allowed to complete, and ",[97,351,352],{},"request.tls.authorized"," tells you whether the presented certificate (if any) was trusted. This is useful when you want to respond with your own error (e.g. a JSON ",[97,355,356],{},"401",") instead of a raw connection reset, or when unauthenticated clients should still reach some routes.",[118,359,361],{"className":120,"code":360,"language":122,"meta":123,"style":123},"import { serve } from \"srvx/node\";\nimport { mtlsPlugin } from \"srvx/mtls\";\n\nserve({\n  tls: { cert: \"./server.crt\", key: \"./server.key\" },\n  plugins: [\n    mtlsPlugin({\n      ca: \"./ca.crt\",\n      requestCert: true,\n      // Accept the handshake even for untrusted/missing certs so the\n      // handler can decide how to respond, instead of a TLS-level reset.\n      rejectUnauthorized: false,\n    }),\n  ],\n  fetch: (request) => {\n    if (!request.tls?.authorized) {\n      return new Response(\"client certificate required\", { status: 401 });\n    }\n    return new Response(`Hello, ${request.tls.peerCertificate?.subject?.CN}`);\n  },\n});\n",[97,362,363,376,390,394,400,412,417,425,437,449,456,462,473,479,485,505,520,543,549,592,598],{"__ignoreMap":123},[127,364,365,367,369,371,374],{"class":129,"line":130},[127,366,134],{"class":133},[127,368,138],{"class":137},[127,370,141],{"class":133},[127,372,373],{"class":144}," \"srvx/node\"",[127,375,148],{"class":137},[127,377,378,380,383,385,388],{"class":129,"line":151},[127,379,134],{"class":133},[127,381,382],{"class":137}," { mtlsPlugin } ",[127,384,141],{"class":133},[127,386,387],{"class":144}," \"srvx/mtls\"",[127,389,148],{"class":137},[127,391,392],{"class":129,"line":158},[127,393,155],{"emptyLinePlaceholder":154},[127,395,396,398],{"class":129,"line":168},[127,397,162],{"class":161},[127,399,165],{"class":137},[127,401,402,404,406,408,410],{"class":129,"line":186},[127,403,171],{"class":137},[127,405,174],{"class":144},[127,407,177],{"class":137},[127,409,180],{"class":144},[127,411,183],{"class":137},[127,413,414],{"class":129,"line":213},[127,415,416],{"class":137},"  plugins: [\n",[127,418,420,423],{"class":129,"line":419},7,[127,421,422],{"class":161},"    mtlsPlugin",[127,424,165],{"class":137},[127,426,428,431,434],{"class":129,"line":427},8,[127,429,430],{"class":137},"      ca: ",[127,432,433],{"class":144},"\"./ca.crt\"",[127,435,436],{"class":137},",\n",[127,438,440,443,447],{"class":129,"line":439},9,[127,441,442],{"class":137},"      requestCert: ",[127,444,446],{"class":445},"suiK_","true",[127,448,436],{"class":137},[127,450,452],{"class":129,"line":451},10,[127,453,455],{"class":454},"sCsY4","      // Accept the handshake even for untrusted/missing certs so the\n",[127,457,459],{"class":129,"line":458},11,[127,460,461],{"class":454},"      // handler can decide how to respond, instead of a TLS-level reset.\n",[127,463,465,468,471],{"class":129,"line":464},12,[127,466,467],{"class":137},"      rejectUnauthorized: ",[127,469,470],{"class":445},"false",[127,472,436],{"class":137},[127,474,476],{"class":129,"line":475},13,[127,477,478],{"class":137},"    }),\n",[127,480,482],{"class":129,"line":481},14,[127,483,484],{"class":137},"  ],\n",[127,486,488,490,493,497,500,502],{"class":129,"line":487},15,[127,489,189],{"class":161},[127,491,492],{"class":137},": (",[127,494,496],{"class":495},"sQHwn","request",[127,498,499],{"class":137},") ",[127,501,195],{"class":133},[127,503,504],{"class":137}," {\n",[127,506,508,511,514,517],{"class":129,"line":507},16,[127,509,510],{"class":133},"    if",[127,512,513],{"class":137}," (",[127,515,516],{"class":133},"!",[127,518,519],{"class":137},"request.tls?.authorized) {\n",[127,521,523,526,528,530,532,535,538,540],{"class":129,"line":522},17,[127,524,525],{"class":133},"      return",[127,527,198],{"class":133},[127,529,201],{"class":161},[127,531,204],{"class":137},[127,533,534],{"class":144},"\"client certificate required\"",[127,536,537],{"class":137},", { status: ",[127,539,356],{"class":445},[127,541,542],{"class":137}," });\n",[127,544,546],{"class":129,"line":545},18,[127,547,548],{"class":137},"    }\n",[127,550,552,555,557,559,561,564,566,568,570,572,575,578,581,583,586,589],{"class":129,"line":551},19,[127,553,554],{"class":133},"    return",[127,556,198],{"class":133},[127,558,201],{"class":161},[127,560,204],{"class":137},[127,562,563],{"class":144},"`Hello, ${",[127,565,496],{"class":137},[127,567,263],{"class":144},[127,569,226],{"class":137},[127,571,263],{"class":144},[127,573,574],{"class":137},"peerCertificate",[127,576,577],{"class":144},"?.",[127,579,580],{"class":137},"subject",[127,582,577],{"class":144},[127,584,585],{"class":445},"CN",[127,587,588],{"class":144},"}`",[127,590,591],{"class":137},");\n",[127,593,595],{"class":129,"line":594},20,[127,596,597],{"class":137},"  },\n",[127,599,601],{"class":129,"line":600},21,[127,602,216],{"class":137},[283,604,605],{},[67,606,607,608,610,611,614,615,617],{},"\nWith the default ",[97,609,331],{},", the ",[97,612,613],{},"if (!request.tls?.authorized)"," check above is unreachable — unauthenticated clients are already rejected at the TLS layer. Only set ",[97,616,348],{}," if you intend to enforce authorization yourself in the handler, as shown here.",[67,619,620],{},[71,621,622,227],{},[97,623,303],{},[229,625,626,631,640],{},[232,627,628,630],{},[97,629,276],{},": Trusted CA certificate(s) in PEM format — file path(s) or inline content (optional). When set, replaces the well-known Mozilla CAs.",[232,632,633,636,637,639],{},[97,634,635],{},"requestCert",": Request a certificate from connecting clients (default ",[97,638,446],{},").",[232,641,642,644,645,647,648,650,651,653,654,658,659,662],{},[97,643,320],{},": Reject the TLS handshake itself when the client's certificate is not signed by a trusted ",[97,646,276],{}," (default ",[97,649,446],{},"). When ",[97,652,470],{},", the handshake always completes and an unverified certificate is instead exposed via ",[106,655,656],{"href":311},[97,657,314],{}," with ",[97,660,661],{},"authorized: false",", leaving enforcement to your handler.",[664,665,667],"h3",{"id":666},"requesttls",[97,668,314],{},[67,670,671,672,677],{},"While the plugin is active, ",[106,673,675],{"href":674},"/guide/handler#requesttls",[97,676,314],{}," provides:",[229,679,680,699,710,721,729],{},[232,681,682,684,685,694,695,698],{},[97,683,574],{}," — the client certificate (",[106,686,690,691],{"href":687,"rel":688},"https://nodejs.org/api/tls.html#certificate-object",[689],"nofollow","node:tls ",[97,692,693],{},"PeerCertificate","). An empty object (",[97,696,697],{},"{}",") when none was presented.",[232,700,701,704,705,707,708,263],{},[97,702,703],{},"authorized"," — ",[97,706,446],{}," if the certificate was signed by a trusted ",[97,709,276],{},[232,711,712,715,716,718,719,263],{},[97,713,714],{},"authorizationError"," — why verification failed, when ",[97,717,703],{}," is ",[97,720,470],{},[232,722,723,725,726,263],{},[97,724,111],{}," — negotiated TLS protocol, e.g. ",[97,727,728],{},"\"TLSv1.3\"",[232,730,731,734],{},[97,732,733],{},"cipher"," — negotiated cipher suite.",[664,736,738],{"id":737},"runtime-support","Runtime support",[740,741,742,760],"note",{},[67,743,744,746,747,749,750,752,753,513,756,759],{},[97,745,303],{}," requires an HTTPS server (",[97,748,99],{}," + ",[97,751,103],{},") and srvx's ",[71,754,755],{},"Node.js adapter",[97,757,758],{},"import { serve } from \"srvx/node\"","); it throws otherwise.",[229,761,762,767,780],{},[232,763,764,766],{},[71,765,255],{}," — works natively.",[232,768,769,771,772,775,776,779],{},[71,770,259],{}," (2.8+) — works through the Node adapter, which runs on Deno via ",[97,773,774],{},"node:https",". (Native ",[97,777,778],{},"Deno.serve"," cannot request client certificates.)",[232,781,782,784,785,788,789,792,793,263],{},[71,783,262],{}," — not currently supported: neither native ",[97,786,787],{},"Bun.serve"," nor Bun's ",[97,790,791],{},"node:http(s)"," server exposes the peer certificate to the handler, so the plugin throws. Tracked in ",[106,794,797],{"href":795,"rel":796},"https://github.com/oven-sh/bun/issues/16254",[689],"oven-sh/bun#16254",[799,800,801],"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 .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);}html pre.shiki code .suiK_, html code.shiki .suiK_{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sQHwn, html code.shiki .sQHwn{--shiki-light:#E36209;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}",{"title":123,"searchDepth":151,"depth":151,"links":803},[804,805],{"id":91,"depth":151,"text":92},{"id":297,"depth":151,"text":81,"children":806},[807,808],{"id":666,"depth":158,"text":314},{"id":737,"depth":158,"text":738},"Serve over HTTPS and, optionally, require a client certificate (mutual TLS).","md",{"icon":35},{"icon":35},{"title":32,"description":809},"GhkUFreOZRGO75PhkUVSyhkQPqJz1K01C2Zu1mpC5CM",[816,818],{"title":27,"path":28,"stem":29,"description":817,"icon":30,"children":-1},"Provide additional options to customize listening server.",{"title":37,"path":38,"stem":39,"description":819,"icon":40,"children":-1},"Tips for using srvx with bundlers.",1785425524736]