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