[{"data":1,"prerenderedAt":711},["ShallowReactive",2],{"blog-how-to/who-patches-node-runtime":3},{"id":4,"title":5,"body":6,"category":679,"date":680,"dateModified":680,"description":681,"draft":682,"extension":683,"faq":684,"featured":682,"headerVariant":695,"image":696,"keywords":697,"meta":698,"navigation":699,"ogDescription":700,"ogTitle":696,"path":701,"readTime":702,"schemaOrg":703,"schemaType":704,"seo":705,"sitemap":706,"stem":707,"tags":708,"twitterCard":709,"__hash__":710},"blog/blog/how-to/who-patches-node-runtime.md","Do I Need to Update Node.js on Vercel? Who Patches Your Runtime (2026)",{"type":7,"value":8,"toc":670},"minimark",[9,13,21,39,44,47,64,67,71,88,102,108,112,118,146,170,189,199,205,209,300,303,307,310,322,401,415,427,431,434,440,446,471,478,482,485,500,529,545,557,574,577,634,658,666],[10,11,12],"p",{},"Node.js shipped 23 CVE fixes across two security releases in 2026: twelve on June 18, eleven more on July 29. Three of the July ones are rated HIGH, including a heap-use-after-free in the HTTP/2 stack.",[10,14,15,16,20],{},"Here's the part nobody writes down. Whether your deployed app picked those fixes up has almost nothing to do with you and almost everything to do with which host you clicked \"deploy\" on. Two apps with identical ",[17,18,19],"code",{},"package.json"," files, deployed the same day, can sit on opposite sides of that line.",[22,23,24],"tldr",{},[10,25,26,27,30,31,34,35,38],{},"Vercel and Cloudflare patch the runtime under you, so there's nothing to do. Netlify, Render, Railway, Fly and anything Docker-based freeze the Node version into your build, so you stay on whatever you shipped until you rebuild. There's a second trap on the rebuild side: if you pinned an exact version like ",[17,28,29],{},"22.23.0"," instead of a major like ",[17,32,33],{},"22",", rebuilding doesn't help either. Check ",[17,36,37],{},"process.version"," on the running app, not your laptop.",[40,41,43],"h2",{"id":42},"two-questions-not-one","Two questions, not one",[10,45,46],{},"Most advice treats this as a single question (\"is my Node up to date?\"). It's really two, and they have different answers per host.",[48,49,50,58],"ol",{},[51,52,53,57],"li",{},[54,55,56],"strong",{},"Does the runtime move on its own?"," If your host manages Node as infrastructure, patches land without a deploy.",[51,59,60,63],{},[54,61,62],{},"If it doesn't, does a rebuild pick up the patch?"," Only if you pinned loosely. An exact version pin survives rebuilds by design. That's what pinning means.",[10,65,66],{},"You need a \"yes\" to one of those. Plenty of apps have neither, which is how a project that gets deployed twice a week can still be running a Node release from last winter.",[40,68,70],{"id":69},"hosts-that-patch-node-for-you","Hosts that patch Node for you",[10,72,73,76,77,80,81,84,85,87],{},[54,74,75],{},"Vercel."," Its version docs are unusually direct about this: \"Only major versions are available. Vercel automatically rolls out minor and patch updates when needed, such as to fix a security issue.\" You choose ",[17,78,79],{},"24.x"," in project settings or ",[17,82,83],{},"engines.node"," in ",[17,86,19],{},", and Vercel picks the specific build. So the answer to \"do I need to update Node on Vercel\" is no, and you couldn't pin an exact patch even if you wanted to.",[10,89,90,93,94,97,98,101],{},[54,91,92],{},"Cloudflare Workers."," Different reason, same outcome. Workers don't run Node at all. They run Cloudflare's own runtime, ",[17,95,96],{},"workerd",", which implements a subset of Node's APIs natively plus polyfills for the rest. There's no Node version to select, because there's no Node. What you set is a ",[17,99,100],{},"compatibility_date"," in your Wrangler config, which controls runtime behavior changes, not security patches. Cloudflare owns patching the engine.",[103,104,105],"info-box",{},[10,106,107],{},"A compatibility date is not a version pin in the security sense. It exists so Cloudflare can change default behavior without breaking old Workers. Setting an old date does not keep you on an old, unpatched engine.",[40,109,111],{"id":110},"hosts-that-freeze-it-into-your-build","Hosts that freeze it into your build",[10,113,114,117],{},[54,115,116],{},"Netlify."," This is the one that surprises people, and the docs say it plainly: \"A build's Node.js version is initially determined by the default version preinstalled on the site's selected build image. We pin the site to that version so your builds won't change even if the build image's defaults change.\"",[10,119,120,121,124,125,128,129,132,133,136,137,140,141,145],{},"Pinned. Not rolling. You can override with ",[17,122,123],{},".node-version",", ",[17,126,127],{},".nvmrc",", or a ",[17,130,131],{},"NODE_VERSION"," variable, and Netlify accepts either an exact version like ",[17,134,135],{},"20.11.1"," or a major like ",[17,138,139],{},"24",", which resolves to the latest 24.x ",[142,143,144],"em",{},"at build time",". The functions runtime normally inherits whatever the build used.",[10,147,148,151,152,154,155,157,158,157,160,84,163,165,166,169],{},[54,149,150],{},"Render."," Resolution order is ",[17,153,131],{}," in the dashboard, then ",[17,156,123],{},", then ",[17,159,127],{},[17,161,162],{},"engines",[17,164,19],{},". Leave all four unset and you get a default tied to ",[142,167,168],{},"when you created the service",", not to today. Render's own docs list different defaults for services created before and after specific dates. A service you spun up two years ago carries a two-year-old default until you say otherwise.",[10,171,172,175,176,157,179,157,181,157,183,157,185,188],{},[54,173,174],{},"Railway."," Railpack resolves the version from ",[17,177,178],{},"RAILPACK_NODE_VERSION",[17,180,83],{},[17,182,127],{},[17,184,123],{},[17,186,187],{},"mise.toml",", falling back to LTS. The important detail is when: the version is resolved during the build and baked into the container image. The running container has no mechanism to swap its own Node out.",[10,190,191,194,195,198],{},[54,192,193],{},"Fly, and anything you ship as a Docker image."," Your ",[17,196,197],{},"FROM node:22-alpine"," line is the whole story. That tag resolved to a specific digest the last time you built. Until you rebuild and re-pull the base image, that's your runtime, forever, no matter how many CVEs land upstream.",[200,201,202],"warning-box",{},[10,203,204],{},"\"But I deploy constantly\" is not a defense on its own. Rebuilding only helps if your version spec is loose enough to resolve forward. An exact pin plus daily deploys keeps you exactly as vulnerable as no deploys at all.",[40,206,208],{"id":207},"the-table","The table",[210,211,212,228],"table",{},[213,214,215],"thead",{},[216,217,218,222,225],"tr",{},[219,220,221],"th",{},"Host",[219,223,224],{},"Runtime moves on its own?",[219,226,227],{},"What a rebuild does",[229,230,231,243,256,267,278,289],"tbody",{},[216,232,233,237,240],{},[234,235,236],"td",{},"Vercel",[234,238,239],{},"Yes, minors and patches roll automatically",[234,241,242],{},"Nothing extra needed",[216,244,245,248,253],{},[234,246,247],{},"Cloudflare Workers",[234,249,250,251],{},"Yes, Cloudflare patches ",[17,252,96],{},[234,254,255],{},"No Node version to manage",[216,257,258,261,264],{},[234,259,260],{},"Netlify",[234,262,263],{},"No, pinned to build image default",[234,265,266],{},"Picks up latest patch only if you set a major-only version",[216,268,269,272,275],{},[234,270,271],{},"Render",[234,273,274],{},"No, resolved per service",[234,276,277],{},"Same, and the unset default tracks service creation date",[216,279,280,283,286],{},[234,281,282],{},"Railway",[234,284,285],{},"No, baked into the image at build",[234,287,288],{},"Re-resolves from your spec",[216,290,291,294,297],{},[234,292,293],{},"Fly / Docker",[234,295,296],{},"No, frozen to the base image digest",[234,298,299],{},"Only if you re-pull the base tag",[10,301,302],{},"Read that middle column as the real question. Four of six rows say no.",[40,304,306],{"id":305},"check-what-youre-actually-running","Check what you're actually running",[10,308,309],{},"Three sources of truth people confuse, in increasing order of usefulness.",[10,311,312,313,316,317,84,319,321],{},"Your local ",[17,314,315],{},"node -v"," tells you about your laptop. ",[17,318,83],{},[17,320,19],{}," tells you what you asked for. Neither tells you what booted in production.",[323,324,326],"code-block",{"label":325},"The only check that counts, from a live route",[327,328,333],"pre",{"className":329,"code":330,"language":331,"meta":332,"style":332},"language-js shiki shiki-themes github-dark","// Add to any API route, hit it once, then remove it.\nexport function GET() {\n  return Response.json({\n    node: process.version,\n    platform: process.platform,\n  });\n}\n","js","",[17,334,335,344,362,377,383,389,395],{"__ignoreMap":332},[336,337,340],"span",{"class":338,"line":339},"line",1,[336,341,343],{"class":342},"sAwPA","// Add to any API route, hit it once, then remove it.\n",[336,345,347,351,354,358],{"class":338,"line":346},2,[336,348,350],{"class":349},"snl16","export",[336,352,353],{"class":349}," function",[336,355,357],{"class":356},"svObZ"," GET",[336,359,361],{"class":360},"s95oV","() {\n",[336,363,365,368,371,374],{"class":338,"line":364},3,[336,366,367],{"class":349},"  return",[336,369,370],{"class":360}," Response.",[336,372,373],{"class":356},"json",[336,375,376],{"class":360},"({\n",[336,378,380],{"class":338,"line":379},4,[336,381,382],{"class":360},"    node: process.version,\n",[336,384,386],{"class":338,"line":385},5,[336,387,388],{"class":360},"    platform: process.platform,\n",[336,390,392],{"class":338,"line":391},6,[336,393,394],{"class":360},"  });\n",[336,396,398],{"class":338,"line":397},7,[336,399,400],{"class":360},"}\n",[10,402,403,404,407,408,410,411,414],{},"For build-time visibility, prepend ",[17,405,406],{},"node -v &&"," to your build command and read it in the deploy log. On Netlify and Render that's the fastest way to confirm whether your ",[17,409,123],{}," file is being honored at all, which is a common silent failure when the file has a stray newline or a ",[17,412,413],{},"v"," prefix the resolver doesn't expect.",[416,417,418],"tip-box",{},[10,419,420,421,423,424,426],{},"Check the running version right after you change a version file, not weeks later. A ",[17,422,123],{}," that the host quietly ignored looks identical to one that worked until you actually read ",[17,425,37],{},".",[40,428,430],{"id":429},"what-was-in-the-2026-releases","What was in the 2026 releases",[10,432,433],{},"Worth knowing so you can judge urgency rather than guessing at it.",[10,435,436,439],{},[54,437,438],{},"June 18, 2026"," fixed 12 CVEs, patched in v22.23.0, v24.17.0 and v26.3.1. Two HIGH: a WebCrypto AES integer overflow causing denial of service, and a TLS wildcard-depth authentication bypass involving unicode dot separator handling.",[10,441,442,445],{},[54,443,444],{},"July 29, 2026"," fixed 11 CVEs, patched in v22.23.2, v24.18.1 and v26.5.1. Three HIGH:",[447,448,449,455,465],"ul",{},[51,450,451,454],{},[17,452,453],{},"CVE-2026-56848",", a re-entrant send heap-use-after-free in HTTP/2.",[51,456,457,460,461,464],{},[17,458,459],{},"CVE-2026-56846",", HTTP/2 retained headers bypassing ",[17,462,463],{},"maxSessionMemory"," limits.",[51,466,467,470],{},[17,468,469],{},"CVE-2026-58043",", Permission Model path matching over-granting filesystem access, where a process granted one directory can reach outside it.",[10,472,473,474,477],{},"Severity here is about your exposure, not just the number. The HTTP/2 bugs need you to be terminating HTTP/2 in Node. If you sit behind a platform proxy that speaks HTTP/1.1 to your process, which is the common shape on Render and Railway, those two are much less interesting than the version number suggests. The Permission Model one only applies if you actually use ",[17,475,476],{},"--permission",". Most vibe-coded apps don't.",[40,479,481],{"id":480},"what-to-do-about-it","What to do about it",[10,483,484],{},"If you're on Vercel or Cloudflare, nothing. Genuinely nothing. Go do something else.",[10,486,487,488,491,492,84,494,496,497,499],{},"If you're on any of the other four, the fix is one decision: ",[54,489,490],{},"pin the major version, not the exact one."," Put ",[17,493,33],{},[17,495,123],{}," instead of ",[17,498,29],{},", then make sure something triggers a rebuild periodically. A dependency bump, a scheduled deploy, anything that runs the build again.",[501,502,504],"step",{"number":503},"1",[10,505,506,509,510,124,512,124,514,84,516,518,519,521,522,525,526,528],{},[54,507,508],{},"Find your current pin."," Look for ",[17,511,123],{},[17,513,127],{},[17,515,83],{},[17,517,19],{},", a ",[17,520,131],{}," variable in your host dashboard, and a ",[17,523,524],{},"FROM node:"," line in a Dockerfile. More than one can exist, and they don't all have the same precedence on every host. The dashboard variable usually wins on Render; ",[17,527,178],{}," wins on Railway.",[501,530,532],{"number":531},"2",[10,533,534,537,538,540,541,544],{},[54,535,536],{},"Loosen an exact pin to a major."," If you find ",[17,539,135],{},", and you had no specific reason for those last two numbers, replace it with ",[17,542,543],{},"20",". If you did have a reason, write it in a comment so the next person doesn't loosen it blindly.",[501,546,548],{"number":547},"3",[10,549,550,553,554,556],{},[54,551,552],{},"Redeploy and confirm."," Read ",[17,555,37],{}," from the running app, not the build log alone. Confirm it moved.",[501,558,560],{"number":559},"4",[10,561,562,565,566,569,570,573],{},[54,563,564],{},"Stop broadcasting the version."," A default Express app sends ",[17,567,568],{},"X-Powered-By: Express",", and some setups leak runtime detail in the ",[17,571,572],{},"Server"," header. That doesn't create a vulnerability, but it turns \"scan everything and see what sticks\" into \"scan the things already known to be behind.\" Our scanner flags these as information disclosure findings with the exact header value it saw, because they're one of the cheapest things to remove.",[10,575,576],{},"The honest summary: this is a ten-minute audit that most people never run, on a question their host's marketing page doesn't answer. The Netlify pinning behavior in particular is documented in one sentence, in a page about dependency management, which is not where anyone looks when they're wondering whether they need to patch.",[578,579,580,590,601,619,625],"faq-section",{},[581,582,584],"faq-item",{"question":583},"Do I need to update Node.js on Vercel?",[10,585,586,587,589],{},"No. Vercel only exposes major versions, and its docs state that it \"automatically rolls out minor and patch updates when needed, such as to fix a security issue.\" You choose ",[17,588,79],{},", Vercel chooses which 24.x. The tradeoff is that you can't pin an exact patch version even if you have a reason to.",[581,591,593],{"question":592},"Does Netlify update Node.js automatically?",[10,594,595,596,598,599,426],{},"No. Netlify pins your site to the default Node version of its build image, and the docs say your builds \"won't change even if the build image's defaults change.\" The version moves only when you rebuild, and only if you specified a major version like ",[17,597,139],{}," rather than an exact one like ",[17,600,135],{},[581,602,604],{"question":603},"How do I check which Node version my deployed app is running?",[10,605,606,607,609,610,612,613,615,616,618],{},"Log ",[17,608,37],{}," from a live route and read the response, or add ",[17,611,315],{}," to your build command and read the deploy log. Don't trust your local ",[17,614,315],{}," or the ",[17,617,162],{}," field. Those describe intent, not what the host actually booted.",[581,620,622],{"question":621},"Is an outdated Node runtime actually exploitable?",[10,623,624],{},"Depends which CVE and how you're deployed. Of the 11 fixed on July 29, 2026, three were HIGH: two HTTP/2 flaws including a heap-use-after-free, and a Permission Model path-matching bug. The HTTP/2 pair only bites if your Node process terminates HTTP/2 directly, which it often doesn't when a platform proxy sits in front of it.",[581,626,628],{"question":627},"Does pinning an exact Node version make me safer?",[10,629,630,631,633],{},"It makes you more reproducible and less patched. An exact pin like ",[17,632,29],{}," means every future rebuild reinstalls that exact version, CVEs included. Pinning the major version lets each rebuild move forward to the latest patch on that line.",[635,636,637,643,648,653],"related-articles",{},[638,639],"related-card",{"description":640,"href":641,"title":642},"The exact compromised versions from the 2026 npm attacks and the lockfile checks that find them","/blog/how-to/check-npm-supply-chain-attack","Is My Project Affected by an npm Supply Chain Attack?",[638,644],{"description":645,"href":646,"title":647},"Environment variables, headers and the settings that actually matter on Vercel","/blog/how-to/deploy-vercel-securely","Deploy to Vercel Securely",[638,649],{"description":650,"href":651,"title":652},"What Railway exposes by default and how to lock a deployment down","/blog/how-to/deploy-railway-securely","Deploy to Railway Securely",[638,654],{"description":655,"href":656,"title":657},"Another host default changing under already-deployed apps, and the one curl that tells you if it affects yours","/blog/how-to/vercel-server-timing-exposed","Vercel Stops Stripping Server-Timing on August 10",[659,660,663],"cta-box",{"href":661,"label":662},"/","Scan Your App Free",[10,664,665],{},"Your Node version is one input. What an attacker sees is the whole surface: leaked keys, missing headers, open endpoints, and the version banners your server volunteers. A CheckYourVibe scan checks your live app from the outside and tells you what's exposed right now.",[667,668,669],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}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);}",{"title":332,"searchDepth":346,"depth":346,"links":671},[672,673,674,675,676,677,678],{"id":42,"depth":346,"text":43},{"id":69,"depth":346,"text":70},{"id":110,"depth":346,"text":111},{"id":207,"depth":346,"text":208},{"id":305,"depth":346,"text":306},{"id":429,"depth":346,"text":430},{"id":480,"depth":346,"text":481},"how-to","2026-08-06","Node shipped 23 CVE fixes in two 2026 security releases. Whether your app got them depends entirely on your host, and most docs never spell out which side you're on.",false,"md",[685,687,689,691,693],{"question":583,"answer":686},"No. Vercel only lets you select a major version, and its docs state that it automatically rolls out minor and patch updates when needed, such as to fix a security issue. You pick 24.x, Vercel decides which 24.x you actually run. The tradeoff is that you cannot pin an exact patch version even if you want to.",{"question":592,"answer":688},"No. Netlify pins your site to the default Node version of its build image, and its docs say builds will not change even if the build image defaults change. The version only moves when you rebuild, and only if you specified a major version like 24 rather than an exact one like 20.11.1.",{"question":603,"answer":690},"Log process.version from a running route and read it in your host's logs, or add node -v to your build command and read the build output. Do not trust your local nvm version or the engines field in package.json. Both describe intent, not what the host actually booted.",{"question":621,"answer":692},"It depends on the CVE. Of the 11 fixed on July 29, 2026, three were HIGH severity: two HTTP/2 bugs including a heap-use-after-free, and a Permission Model path-matching flaw that over-grants filesystem access. A use-after-free in a network-facing parser is the classic raw material for remote code execution, so the HTTP/2 ones matter most if you serve HTTP/2 traffic directly.",{"question":627,"answer":694},"It makes you more reproducible and less safe. An exact pin like 22.23.0 in .node-version means every future rebuild reinstalls that exact version, including its known CVEs. Pinning the major version instead lets each rebuild pick up the latest patch on that line.","yellow",null,"do i need to update node.js vercel, who patches node runtime, node.js security release 2026, netlify node version pinned, railway node version, render node version, node cve patch hosting",{},true,"Vercel rolls Node patches out for you. Netlify, Render and Railway do not until you rebuild. Here is how to tell which one you're running.","/blog/how-to/who-patches-node-runtime","10 min read","[object Object]","HowTo",{"title":5,"description":681},{"loc":701},"blog/how-to/who-patches-node-runtime",[],"summary_large_image","-mK0Ga3S0epvNINZXmINL6wNDRFyVW-6-ytD1XgAiuQ",1786039026830]