[{"data":1,"prerenderedAt":728},["ShallowReactive",2],{"switcher-blog-pareja":3,"art-nginx-http3-quic-2026-en":6},{"en":4,"es":5},"\u002Fen\u002Fblog\u002Fnginx-http3-quic-2026\u002F","\u002Fes\u002Fblog\u002Fnginx-http3-quic-2026\u002F",{"id":7,"title":8,"author":9,"body":10,"date":714,"description":715,"extension":716,"image":717,"meta":718,"navigation":282,"pareja":719,"path":720,"seo":721,"stem":722,"tags":723,"__hash__":727},"blogEn\u002Fen\u002Fblog\u002Fnginx-http3-quic-2026.md","Nginx with HTTP\u002F3 and QUIC in 2026: when to turn it on and when not to bother","Paco Cubel",{"type":11,"value":12,"toc":699},"minimark",[13,18,32,47,50,54,57,85,92,104,108,115,126,132,147,151,156,218,236,240,243,394,397,429,433,436,485,492,496,524,527,546,561,573,577,580,613,616,620,659,663,670,673,677,695],[14,15,17],"h2",{"id":16},"the-protocol-that-lived-under-experimental-for-years","The protocol that lived under \"experimental\" for years",[19,20,21,22,26,27,31],"p",{},"Two years ago, when a client asked if we could turn HTTP\u002F3 on for their Nginx, the honest answer was \"we can, but we wouldn't recommend it in production\". The ",[23,24,25],"code",{},"ngx_http_v3_module"," landed in Nginx 1.25.0 in June 2023 marked ",[28,29,30],"strong",{},"experimental",", which meant what it usually means: works in demos, breaks in specific places you'd rather not discover on a Tuesday afternoon.",[19,33,34,35,38,39,42,43,46],{},"In 2026 the conversation has shifted. With ",[28,36,37],{},"Nginx 1.26.x"," and ",[28,40,41],{},"OpenSSL 3.5.x or BoringSSL\u002Fquiche",", HTTP\u002F3 over QUIC is ",[28,44,45],{},"production-ready"," in the meaning sysadmins give the word: you flip it on, watch it for a couple of weeks and stop thinking about it.",[19,48,49],{},"Let's look at when it's worth the move, how to do it, and what still bites.",[14,51,53],{"id":52},"what-http3-actually-buys-you-one-page","What HTTP\u002F3 actually buys you (one page)",[19,55,56],{},"QUIC replaces TCP underneath and rewrites the contract with the network:",[58,59,60,67,73,79],"ul",{},[61,62,63,66],"li",{},[28,64,65],{},"1-RTT handshake"," (or 0-RTT on resume). Versus TCP + TLS, which needs three round-trips before the first byte goes out.",[61,68,69,72],{},[28,70,71],{},"No head-of-line blocking between streams",": if a packet carrying the logo image gets lost, the article HTML doesn't sit waiting.",[61,74,75,78],{},[28,76,77],{},"Transparent connection migration",": phone switches Wi-Fi → 4G and the TLS session survives. Same promise HTTP\u002F2 over TCP made… except it never actually worked.",[61,80,81,84],{},[28,82,83],{},"Encrypted by default",": QUIC doesn't allow plain text. Sounds obvious in 2026, but it forces certificate hygiene that many servers don't have.",[19,86,87,88,91],{},"What HTTP\u002F3 ",[28,89,90],{},"doesn't"," buy you:",[58,93,94,97],{},[61,95,96],{},"It's not a magic fix for sites slow at the backend. If the bottleneck is PHP-FPM or the database, HTTP\u002F3 doesn't touch it.",[61,98,99,100,103],{},"It doesn't replace HTTP\u002F2. The right approach is to serve ",[28,101,102],{},"both",": HTTP\u002F2 over TCP\u002F443 and HTTP\u002F3 over UDP\u002F443, and let the client negotiate.",[14,105,107],{"id":106},"when-to-turn-it-on-and-when-to-stick-with-http2","When to turn it on (and when to stick with HTTP\u002F2)",[19,109,110,111,114],{},"Turning it on ",[28,112,113],{},"is"," worth it when:",[58,116,117,120,123],{},[61,118,119],{},"You have meaningful mobile traffic. That's the scenario where QUIC shows up in real metrics.",[61,121,122],{},"You serve an international site with users on unreliable networks.",[61,124,125],{},"Your Core Web Vitals are sitting right at the edge and you need to claw back tenths in LCP\u002FINP.",[19,127,110,128,131],{},[28,129,130],{},"isn't"," worth it if:",[58,133,134,141,144],{},[61,135,136,137,140],{},"The box is a small VPS with little CPU headroom. QUIC moves work from the kernel (TCP) into user space (QUIC). The compute bill is real: at high load, Nginx does ",[28,138,139],{},"more work"," serving HTTP\u002F3 than HTTP\u002F2 for the same request count.",[61,142,143],{},"You sit behind a corporate firewall that filters UDP\u002F443. Yes, it happens more than people think — especially in enterprise networks with out-of-date SD-WAN.",[61,145,146],{},"You already have a CDN doing HTTP\u002F3 down to the client. In that case the HTTP\u002F2 origin-to-CDN leg doesn't need upgrading.",[14,148,150],{"id":149},"step-by-step-rollout","Step-by-step rollout",[152,153,155],"h3",{"id":154},"_1-check-versions","1. Check versions",[157,158,163],"pre",{"className":159,"code":160,"language":161,"meta":162,"style":162},"language-bash shiki shiki-themes github-light github-dark","nginx -V 2>&1 | grep -oE 'OpenSSL [0-9.]+'   # OpenSSL 3.5.1 or later\nnginx -V 2>&1 | grep ngx_http_v3              # should show up\n","bash","",[23,164,165,199],{"__ignoreMap":162},[166,167,170,174,178,182,185,188,191,195],"span",{"class":168,"line":169},"line",1,[166,171,173],{"class":172},"sScJk","nginx",[166,175,177],{"class":176},"sj4cs"," -V",[166,179,181],{"class":180},"szBVR"," 2>&1",[166,183,184],{"class":180}," |",[166,186,187],{"class":172}," grep",[166,189,190],{"class":176}," -oE",[166,192,194],{"class":193},"sZZnC"," 'OpenSSL [0-9.]+'",[166,196,198],{"class":197},"sJ8bj","   # OpenSSL 3.5.1 or later\n",[166,200,202,204,206,208,210,212,215],{"class":168,"line":201},2,[166,203,173],{"class":172},[166,205,177],{"class":176},[166,207,181],{"class":180},[166,209,184],{"class":180},[166,211,187],{"class":172},[166,213,214],{"class":193}," ngx_http_v3",[166,216,217],{"class":197},"              # should show up\n",[19,219,220,221,223,224,227,228,231,232,235],{},"If ",[23,222,25],{}," doesn't appear, your package wasn't compiled with HTTP\u002F3. Debian 13 ships it in the official package; on Ubuntu 24.04 LTS you need the official Nginx PPA (",[23,225,226],{},"nginx.org\u002Fpackages\u002Fubuntu",") or a recompile. ",[28,229,230],{},"Caveat",": the ",[23,233,234],{},"apt"," version on Ubuntu 22.04 still doesn't include the module on many derived repos.",[152,237,239],{"id":238},"_2-configure-the-server-block","2. Configure the server block",[19,241,242],{},"Minimum working block for a domain that already serves TLS:",[157,244,247],{"className":245,"code":246,"language":173,"meta":162,"style":162},"language-nginx shiki shiki-themes github-light github-dark","server {\n    # HTTP\u002F2 over TCP (keep)\n    listen 443 ssl;\n    listen [::]:443 ssl;\n    http2 on;\n\n    # HTTP\u002F3 over QUIC\n    listen 443 quic reuseport;\n    listen [::]:443 quic reuseport;\n    http3 on;\n    http3_hq off;\n\n    # Advertise HTTP\u002F3 availability to the client\n    add_header Alt-Svc 'h3=\":443\"; ma=86400' always;\n\n    server_name www.example.com example.com;\n\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Fprivkey.pem;\n    ssl_protocols       TLSv1.3;       # QUIC requires TLS 1.3\n    ssl_early_data on;                  # optional, enables 0-RTT\n\n    root \u002Fvar\u002Fwww\u002Fexample;\n    index index.html;\n}\n",[23,248,249,254,259,265,271,277,284,290,296,302,308,314,319,325,331,336,342,347,353,359,365,371,376,382,388],{"__ignoreMap":162},[166,250,251],{"class":168,"line":169},[166,252,253],{},"server {\n",[166,255,256],{"class":168,"line":201},[166,257,258],{},"    # HTTP\u002F2 over TCP (keep)\n",[166,260,262],{"class":168,"line":261},3,[166,263,264],{},"    listen 443 ssl;\n",[166,266,268],{"class":168,"line":267},4,[166,269,270],{},"    listen [::]:443 ssl;\n",[166,272,274],{"class":168,"line":273},5,[166,275,276],{},"    http2 on;\n",[166,278,280],{"class":168,"line":279},6,[166,281,283],{"emptyLinePlaceholder":282},true,"\n",[166,285,287],{"class":168,"line":286},7,[166,288,289],{},"    # HTTP\u002F3 over QUIC\n",[166,291,293],{"class":168,"line":292},8,[166,294,295],{},"    listen 443 quic reuseport;\n",[166,297,299],{"class":168,"line":298},9,[166,300,301],{},"    listen [::]:443 quic reuseport;\n",[166,303,305],{"class":168,"line":304},10,[166,306,307],{},"    http3 on;\n",[166,309,311],{"class":168,"line":310},11,[166,312,313],{},"    http3_hq off;\n",[166,315,317],{"class":168,"line":316},12,[166,318,283],{"emptyLinePlaceholder":282},[166,320,322],{"class":168,"line":321},13,[166,323,324],{},"    # Advertise HTTP\u002F3 availability to the client\n",[166,326,328],{"class":168,"line":327},14,[166,329,330],{},"    add_header Alt-Svc 'h3=\":443\"; ma=86400' always;\n",[166,332,334],{"class":168,"line":333},15,[166,335,283],{"emptyLinePlaceholder":282},[166,337,339],{"class":168,"line":338},16,[166,340,341],{},"    server_name www.example.com example.com;\n",[166,343,345],{"class":168,"line":344},17,[166,346,283],{"emptyLinePlaceholder":282},[166,348,350],{"class":168,"line":349},18,[166,351,352],{},"    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Ffullchain.pem;\n",[166,354,356],{"class":168,"line":355},19,[166,357,358],{},"    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Fprivkey.pem;\n",[166,360,362],{"class":168,"line":361},20,[166,363,364],{},"    ssl_protocols       TLSv1.3;       # QUIC requires TLS 1.3\n",[166,366,368],{"class":168,"line":367},21,[166,369,370],{},"    ssl_early_data on;                  # optional, enables 0-RTT\n",[166,372,374],{"class":168,"line":373},22,[166,375,283],{"emptyLinePlaceholder":282},[166,377,379],{"class":168,"line":378},23,[166,380,381],{},"    root \u002Fvar\u002Fwww\u002Fexample;\n",[166,383,385],{"class":168,"line":384},24,[166,386,387],{},"    index index.html;\n",[166,389,391],{"class":168,"line":390},25,[166,392,393],{},"}\n",[19,395,396],{},"Critical bits:",[58,398,399,411,419],{},[61,400,401,406,407,410],{},[28,402,403],{},[23,404,405],{},"reuseport"," only on the first ",[23,408,409],{},"listen ... quic",". Without it, Nginx workers can't efficiently share the same UDP port.",[61,412,413,418],{},[28,414,415],{},[23,416,417],{},"Alt-Svc"," is how the browser learns you speak HTTP\u002F3. The first request is still HTTP\u002F2; from the second one, the browser tries QUIC. Skip the header and you'll never see HTTP\u002F3.",[61,420,421,424,425,428],{},[28,422,423],{},"TLS 1.3 is mandatory",". Your config can keep ",[23,426,427],{},"TLSv1.2"," for HTTP\u002F2 — QUIC ignores that branch.",[152,430,432],{"id":431},"_3-open-the-firewall","3. Open the firewall",[19,434,435],{},"This one gets forgotten more than you'd expect:",[157,437,439],{"className":159,"code":438,"language":161,"meta":162,"style":162},"sudo ufw allow 443\u002Fudp     # Ubuntu\u002FDebian\nsudo firewall-cmd --permanent --add-port=443\u002Fudp && sudo firewall-cmd --reload  # RHEL family\n",[23,440,441,458],{"__ignoreMap":162},[166,442,443,446,449,452,455],{"class":168,"line":169},[166,444,445],{"class":172},"sudo",[166,447,448],{"class":193}," ufw",[166,450,451],{"class":193}," allow",[166,453,454],{"class":193}," 443\u002Fudp",[166,456,457],{"class":197},"     # Ubuntu\u002FDebian\n",[166,459,460,462,465,468,471,475,477,479,482],{"class":168,"line":201},[166,461,445],{"class":172},[166,463,464],{"class":193}," firewall-cmd",[166,466,467],{"class":176}," --permanent",[166,469,470],{"class":176}," --add-port=443\u002Fudp",[166,472,474],{"class":473},"sVt8B"," && ",[166,476,445],{"class":172},[166,478,464],{"class":193},[166,480,481],{"class":176}," --reload",[166,483,484],{"class":197},"  # RHEL family\n",[19,486,487,488,491],{},"If you sit behind a load balancer (HAProxy, AWS NLB, etc.), the LB has to forward UDP\u002F443. Many cloud LBs ",[28,489,490],{},"only balance TCP by default",". Without UDP\u002F443 open end-to-end, HTTP\u002F3 never reaches the server.",[152,493,495],{"id":494},"_4-reload-and-verify","4. Reload and verify",[157,497,499],{"className":159,"code":498,"language":161,"meta":162,"style":162},"sudo nginx -t\nsudo systemctl reload nginx\n",[23,500,501,511],{"__ignoreMap":162},[166,502,503,505,508],{"class":168,"line":169},[166,504,445],{"class":172},[166,506,507],{"class":193}," nginx",[166,509,510],{"class":176}," -t\n",[166,512,513,515,518,521],{"class":168,"line":201},[166,514,445],{"class":172},[166,516,517],{"class":193}," systemctl",[166,519,520],{"class":193}," reload",[166,522,523],{"class":193}," nginx\n",[19,525,526],{},"External test:",[157,528,530],{"className":159,"code":529,"language":161,"meta":162,"style":162},"curl --http3 -I https:\u002F\u002Fexample.com\n",[23,531,532],{"__ignoreMap":162},[166,533,534,537,540,543],{"class":168,"line":169},[166,535,536],{"class":172},"curl",[166,538,539],{"class":176}," --http3",[166,541,542],{"class":176}," -I",[166,544,545],{"class":193}," https:\u002F\u002Fexample.com\n",[19,547,548,549,552,553,556,557,560],{},"If you see ",[23,550,551],{},"HTTP\u002F3 200",", you're done. If you see ",[23,554,555],{},"HTTP\u002F2 200",", the server answers but the client didn't ask for HTTP\u002F3 (use ",[23,558,559],{},"--http3-only"," to force and diagnose).",[19,562,563,564,566,567,569,570,572],{},"Browser test: Chrome or Firefox → DevTools → Network → the \"Protocol\" column should show ",[23,565,152],{}," on the second reload (the first one is always ",[23,568,14],{}," because of the ",[23,571,417],{}," negotiation).",[14,574,576],{"id":575},"what-weve-seen-in-production","What we've seen in production",[19,578,579],{},"Some real numbers from the last six months, with no sugar:",[58,581,582,589,599,606],{},[61,583,584,585,588],{},"Mid-sized e-commerce site, ~80% mobile traffic: ",[28,586,587],{},"LCP -8%"," measured on Chrome UX Report after enabling HTTP\u002F3. Nice. Not life-changing.",[61,590,591,592,595,596,598],{},"Corporate site, mostly desktop on fibre: ",[28,593,594],{},"no visible difference",". The ",[23,597,417],{}," negotiation adds a couple of bytes to the first response and that's about it.",[61,600,601,602,605],{},"International site with users in Latin America and Africa: ",[28,603,604],{},"200-400ms TTFB improvement on high-latency networks",". Here it shows.",[61,607,608,609,612],{},"Small VPS (1 vCPU, 2 GB RAM) serving a static blog with occasional spikes: ",[28,610,611],{},"CPU went up by about 6% average",", no visible upside. We turned it off again.",[19,614,615],{},"Takeaway: measure before and after, don't assume.",[14,617,619],{"id":618},"what-still-bites","What still bites",[58,621,622,630,636,650],{},[61,623,624,629],{},[28,625,626,628],{},[23,627,405],{}," requires a recent kernel"," (4.5+). Anything you've kept updated has it, but check if you're stuck on an old distro.",[61,631,632,635],{},[28,633,634],{},"Some intermediate CDNs still don't speak HTTP\u002F3 between origin and edge",". If Cloudflare sits in front, the client already sees HTTP\u002F3 (Cloudflare delivers it) and the origin upgrade adds nothing.",[61,637,638,641,642,645,646,649],{},[28,639,640],{},"Diagnosis is more opaque",": capturing QUIC traffic isn't like capturing TCP. You need a recent Wireshark, ",[23,643,644],{},"SSLKEYLOGFILE"," and patience. If you're used to debugging HTTP\u002F2 with ",[23,647,648],{},"tcpdump",", brace yourself.",[61,651,652,655,656,658],{},[28,653,654],{},"Legacy tools (old curl, wget, certain monitoring agents) don't speak HTTP\u002F3",". Make sure ",[23,657,417],{}," doesn't break your monitoring pipeline.",[14,660,662],{"id":661},"should-we-turn-it-on-for-your-server","Should we turn it on for your server?",[19,664,665,666,669],{},"Our advice in one sentence: ",[28,667,668],{},"if you have meaningful mobile traffic and recent kernel + Nginx, yes. Otherwise, wait for the next server refresh and do it then."," It's not a change that fixes a problem; it's a change that adds headroom where there wasn't any.",[19,671,672],{},"If you want us to size it up for your case, drop us a line. The activation itself takes about an hour; the audit beforehand so you don't break monitoring or firewalls, another. The rest is measuring.",[14,674,676],{"id":675},"references","References",[58,678,679,688],{},[61,680,681],{},[682,683,687],"a",{"href":684,"rel":685},"https:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002Fquic.html",[686],"nofollow","Support for QUIC and HTTP\u002F3 — Nginx Documentation",[61,689,690],{},[682,691,694],{"href":692,"rel":693},"https:\u002F\u002Fblog.nginx.org\u002Fblog\u002Four-roadmap-quic-http-3-support-nginx",[686],"Our Roadmap for QUIC and HTTP\u002F3 Support in NGINX — NGINX Community Blog",[696,697,698],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":162,"searchDepth":201,"depth":201,"links":700},[701,702,703,704,710,711,712,713],{"id":16,"depth":201,"text":17},{"id":52,"depth":201,"text":53},{"id":106,"depth":201,"text":107},{"id":149,"depth":201,"text":150,"children":705},[706,707,708,709],{"id":154,"depth":261,"text":155},{"id":238,"depth":261,"text":239},{"id":431,"depth":261,"text":432},{"id":494,"depth":261,"text":495},{"id":575,"depth":201,"text":576},{"id":618,"depth":201,"text":619},{"id":661,"depth":201,"text":662},{"id":675,"depth":201,"text":676},"2026-05-26","HTTP\u002F3 over QUIC is production-ready in Nginx 1.26.x. Here's how to switch it on step by step, what you actually gain and the cases where sticking with HTTP\u002F2 still makes sense.","md","\u002Fog\u002Fog-default.png",{},"nginx-http3-quic-2026","\u002Fen\u002Fblog\u002Fnginx-http3-quic-2026",{"title":8,"description":715},"en\u002Fblog\u002Fnginx-http3-quic-2026",[724,725,726],"Servers","Nginx","Performance","cqTumEWxejIA6aZsgBbkl-q06uGtdCpK7MXSEanOolA",1781154907978]