[{"data":1,"prerenderedAt":701},["ShallowReactive",2],{"blog-how-to/vercel-server-timing-exposed":3},{"id":4,"title":5,"body":6,"category":667,"date":668,"dateModified":668,"description":669,"draft":670,"extension":671,"faq":672,"featured":670,"headerVariant":683,"image":684,"keywords":685,"meta":686,"navigation":689,"ogDescription":690,"ogTitle":684,"path":691,"readTime":692,"schemaOrg":693,"schemaType":694,"seo":695,"sitemap":696,"stem":697,"tags":698,"twitterCard":699,"__hash__":700},"blog/blog/how-to/vercel-server-timing-exposed.md","Vercel Stops Stripping Server-Timing on August 10: Check Yours First",{"type":7,"value":8,"toc":658},"minimark",[9,13,24,34,39,42,84,87,90,100,104,115,118,121,125,128,198,204,210,214,236,243,246,255,265,286,290,293,302,309,316,319,331,335,338,348,363,511,537,541,559,562,569,627,646,654],[10,11,12],"p",{},"Vercel's CDN has been quietly deleting a header from your responses. On August 10, 2026, it stops.",[10,14,15,16,20,21,23],{},"The header is ",[17,18,19],"code",{},"Server-Timing",", and the change was announced on July 30 in a four-paragraph changelog entry that reads as a feature. It mostly is one. But it has a second effect that entry doesn't mention: if anything in your stack has been setting ",[17,22,19],{},", that content has been landing in the bin, and starting August 10 it lands in your visitors' browsers instead.",[25,26,27],"tldr",{},[10,28,29,30,33],{},"Vercel's changelog says the CDN will \"stop stripping the Server-Timing response header and begin passing it through to the client\" on August 10, 2026. Its description of today's behaviour is that the header is stripped from every response. So run one ",[17,31,32],{},"curl"," against your production URL now and see whether you have one. Most apps won't, because nothing in the common stack sets this header by default. If you do have one, decide before Monday whether you want its contents public.",[35,36,38],"h2",{"id":37},"first-the-check","First, the check",[10,40,41],{},"This takes about ten seconds and settles the question for your app specifically.",[43,44,46],"code-block",{"label":45},"Run against your production URL",[47,48,53],"pre",{"className":49,"code":50,"language":51,"meta":52,"style":52},"language-bash shiki shiki-themes github-dark","curl -sI https://your-app.vercel.app | grep -i server-timing\n","bash","",[17,54,55],{"__ignoreMap":52},[56,57,60,63,67,71,75,78,81],"span",{"class":58,"line":59},"line",1,[56,61,32],{"class":62},"svObZ",[56,64,66],{"class":65},"sDLfK"," -sI",[56,68,70],{"class":69},"sU2Wk"," https://your-app.vercel.app",[56,72,74],{"class":73},"snl16"," |",[56,76,77],{"class":62}," grep",[56,79,80],{"class":65}," -i",[56,82,83],{"class":69}," server-timing\n",[10,85,86],{},"No output means nothing in your stack is setting the header, and August 10 changes nothing for you. You can stop reading.",[10,88,89],{},"Output means you have one, and the string it prints is exactly what the public gets after the change.",[91,92,93],"info-box",{},[10,94,95,96,99],{},"Run this against a deployed URL, not ",[17,97,98],{},"localhost",". The stripping happens at Vercel's CDN, so locally you have been seeing the header all along. That gap is precisely how someone ends up surprised: it looked fine in development because in development it was never being removed.",[35,101,103],{"id":102},"what-is-actually-changing","What is actually changing",[10,105,106,107,114],{},"Vercel's ",[108,109,113],"a",{"href":110,"rel":111},"https://vercel.com/changelog/server-timing-header",[112],"nofollow","changelog entry",", published 2026-07-30, states it plainly: \"On August 10, 2026, Vercel's CDN will stop stripping the Server-Timing response header and begin passing it through to the client.\"",[10,116,117],{},"The current behaviour is described in the same entry, in the sentence offering the opt-out: keeping things as they are means keeping the behaviour \"where the header is stripped from every response.\" Every response. That is a strong default, and it is going away.",[10,119,120],{},"Nothing in that entry mentions security or information disclosure. That's an observation, not an accusation. Passing through a header the developer chose to set is a defensible thing for a CDN to do, and arguably Vercel is removing a non-standard behaviour rather than introducing a risk. The gap worth naming is narrower: developers who set this header under a platform that was silently discarding it never had to think about who could read it.",[35,122,124],{"id":123},"who-sets-this-header-and-who-doesnt","Who sets this header, and who doesn't",[10,126,127],{},"This is where I expected to find that some popular framework turns it on for you. It doesn't, and the specifics matter enough to spell out, because \"my framework might be leaking this\" is the wrong thing to worry about.",[129,130,131,148,154],"ul",{},[132,133,134,138,139,141,142,147],"li",{},[135,136,137],"strong",{},"Next.js does not emit it."," Native ",[17,140,19],{}," support is still an ",[108,143,146],{"href":144,"rel":145},"https://github.com/vercel/next.js/issues/12382",[112],"open feature request",", filed in May 2020 and never implemented. People reach for third-party middleware instead.",[132,149,150,153],{},[135,151,152],{},"Prisma and Drizzle cannot emit it."," They're ORMs. They have no HTTP response layer, so setting a response header isn't something they can do. Prisma surfaces query duration to your own logger, and what you do with it from there is your code.",[132,155,156,159,160,165,166,169,170,169,173,169,176,169,179,169,182,169,185,169,188,169,191,194,195,197],{},[135,157,158],{},"Vercel's runtime does not add it."," Vercel ",[108,161,164],{"href":162,"rel":163},"https://vercel.com/docs/headers/response-headers",[112],"documents every header it adds automatically",": ",[17,167,168],{},"cache-control",", ",[17,171,172],{},"content-length",[17,174,175],{},"content-type",[17,177,178],{},"date",[17,180,181],{},"server",[17,183,184],{},"strict-transport-security",[17,186,187],{},"x-robots-tag",[17,189,190],{},"x-vercel-cache",[17,192,193],{},"x-vercel-id",". ",[17,196,19],{}," is not on that list. It also could hardly be, given it has been stripping the thing.",[10,199,200,201,203],{},"So the header is on your responses only because you added it, or because a library or observability SDK you installed did. That's a much narrower blast radius than \"everyone on Vercel\", and it's why the ",[17,202,32],{}," matters more than any general advice.",[205,206,207],"warning-box",{},[10,208,209],{},"The most likely way to get bitten here is a debug or instrumentation middleware added during development and never removed, emitting query names, cache keys, or internal route identifiers. It has been invisible in production this whole time because Vercel was deleting it. On August 10 it stops being invisible.",[35,211,213],{"id":212},"what-ends-up-in-the-header-and-who-can-read-it","What ends up in the header, and who can read it",[10,215,216,218,219,224,225,169,228,231,232,235],{},[17,217,19],{}," carries named metrics with an optional duration and description. The ",[108,220,223],{"href":221,"rel":222},"https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing",[112],"documented examples on MDN"," are things like ",[17,226,227],{},"db;dur=53",[17,229,230],{},"cpu;dur=2.4",", and ",[17,233,234],{},"cache;desc=\"Cache Read\";dur=23.2",". Vercel's entry gives database query time and cache hits as its examples too.",[10,237,238,239,242],{},"Those are documentation examples rather than a measurement of what real sites emit, so treat them as the shape of the thing, not a prediction of your contents. The ",[17,240,241],{},"desc"," field is the part to look at hardest, because it's free text and it's where people put names: an internal service, a cache key, a query label.",[10,244,245],{},"MDN's own warning is direct:",[247,248,249],"blockquote",{},[10,250,251,252,254],{},"The ",[17,253,19],{}," header may expose potentially sensitive application and infrastructure information. Decide which metrics to send, when to send them, and who should see them based on the use case. For example, you may decide to only show metrics to authenticated users and nothing on public responses.",[10,256,257,258,261,262,264],{},"On visibility, one point gets misunderstood often enough to be worth stating flatly. ",[135,259,260],{},"Anyone who receives the response can read this header."," Not just the site owner, not just someone with developer tools open. A one-line ",[17,263,32],{}," from anywhere shows it.",[10,266,267,270,271,275,276,279,280,282,283,285],{},[17,268,269],{},"Timing-Allow-Origin"," does not change that. It governs whether a ",[272,273,274],"em",{},"cross-origin"," page can read the values programmatically through the ",[17,277,278],{},"PerformanceServerTiming"," API. The header itself is transmitted regardless of what ",[17,281,269],{}," says. If you've been treating that header as a privacy control over ",[17,284,19],{},", it isn't one.",[35,287,289],{"id":288},"is-it-dangerous-or-just-untidy","Is it dangerous, or just untidy?",[10,291,292],{},"Mostly untidy, with one real edge worth understanding, and being accurate about the difference is more useful than alarm.",[10,294,295,296,301],{},"There's published research: ",[108,297,300],{"href":298,"rel":299},"https://www.ndss-symposium.org/wp-content/uploads/2023/02/madweb2023-23087-paper.pdf",[112],"Can You Tell Me the Time? Security Implications of the Server-Timing Header",", by Vanderlinden, Joosen and Vanhoef at imec-DistriNet, KU Leuven, presented at MADWeb 2023.",[10,303,304,305,308],{},"What it found is narrower than the title suggests, and the nuance is the interesting part. The header does not create a timing side channel. It ",[272,306,307],{},"amplifies one that already exists in your application",", by handing the attacker server-side timings directly instead of making them infer timing from round trips polluted by network jitter. That means fewer measurements to reach the same conclusion, and the authors note it's especially effective against geographically distant servers. If your app has no timing-dependent secret to begin with, this is not your problem.",[10,310,311,312,315],{},"The prevalence number is the one I'd keep. The researchers measured the header on ",[135,313,314],{},"5.44% of sites",", and 3.50% with an actual duration value, which they describe as rather low. More than half of that adoption traced back to a single platform, Shopify. Their conclusion about the sites that do emit it on public pages is blunt: those sites go directly against the advice of the specification, which recommends against exposing it publicly.",[10,317,318],{},"That research is from 2022 and 2023 and has nothing to do with Vercel or this change. It's the best available answer to \"does this actually matter\", and the answer is: rarely, but not never, and the spec already told you not to do it.",[91,320,321],{},[10,322,323,324,326,327,330],{},"For contrast, Cloudflare adds ",[17,325,19],{}," entries automatically, including ",[17,328,329],{},"cfCacheStatus"," and edge and origin durations, but only for customers with Real User Monitoring enabled. Automatic emission does happen on some platforms. It just isn't happening to you by way of Next.js.",[35,332,334],{"id":333},"if-you-want-it-gone","If you want it gone",[10,336,337],{},"Three options, in the order I'd try them.",[339,340,342],"step",{"number":341},"1",[10,343,344,347],{},[135,345,346],{},"Stop setting it in production."," The header exists because something in your code or your dependencies adds it. Removing that at the source is the only fix that also covers every other host you might deploy to later, and it's usually a one-line change to a middleware you'd forgotten about.",[339,349,351],{"number":350},"2",[10,352,353,356,357,359,360,362],{},[135,354,355],{},"Keep it, but drop the descriptions."," If the durations are genuinely useful to you and the sensitivity is all in the ",[17,358,241],{}," strings, emit ",[17,361,227],{}," without the label. You keep the performance signal and lose the internal vocabulary.",[339,364,366,372,504],{"number":365},"3",[10,367,368,371],{},[135,369,370],{},"Delete it at the edge."," Vercel documents a response-header transform for exactly this, and its changelog gives it as the way to preserve today's behaviour.",[47,373,377],{"className":374,"code":375,"language":376,"meta":52,"style":52},"language-json shiki shiki-themes github-dark","{\n  \"routes\": [\n    {\n      \"src\": \"/(.*)\",\n      \"transforms\": [\n        {\n          \"type\": \"response.headers\",\n          \"op\": \"delete\",\n          \"target\": { \"key\": \"server-timing\" }\n        }\n      ]\n    }\n  ]\n}\n","json",[17,378,379,385,394,400,414,422,428,441,454,474,480,486,492,498],{"__ignoreMap":52},[56,380,381],{"class":58,"line":59},[56,382,384],{"class":383},"s95oV","{\n",[56,386,388,391],{"class":58,"line":387},2,[56,389,390],{"class":65},"  \"routes\"",[56,392,393],{"class":383},": [\n",[56,395,397],{"class":58,"line":396},3,[56,398,399],{"class":383},"    {\n",[56,401,403,406,408,411],{"class":58,"line":402},4,[56,404,405],{"class":65},"      \"src\"",[56,407,165],{"class":383},[56,409,410],{"class":69},"\"/(.*)\"",[56,412,413],{"class":383},",\n",[56,415,417,420],{"class":58,"line":416},5,[56,418,419],{"class":65},"      \"transforms\"",[56,421,393],{"class":383},[56,423,425],{"class":58,"line":424},6,[56,426,427],{"class":383},"        {\n",[56,429,431,434,436,439],{"class":58,"line":430},7,[56,432,433],{"class":65},"          \"type\"",[56,435,165],{"class":383},[56,437,438],{"class":69},"\"response.headers\"",[56,440,413],{"class":383},[56,442,444,447,449,452],{"class":58,"line":443},8,[56,445,446],{"class":65},"          \"op\"",[56,448,165],{"class":383},[56,450,451],{"class":69},"\"delete\"",[56,453,413],{"class":383},[56,455,457,460,463,466,468,471],{"class":58,"line":456},9,[56,458,459],{"class":65},"          \"target\"",[56,461,462],{"class":383},": { ",[56,464,465],{"class":65},"\"key\"",[56,467,165],{"class":383},[56,469,470],{"class":69},"\"server-timing\"",[56,472,473],{"class":383}," }\n",[56,475,477],{"class":58,"line":476},10,[56,478,479],{"class":383},"        }\n",[56,481,483],{"class":58,"line":482},11,[56,484,485],{"class":383},"      ]\n",[56,487,489],{"class":58,"line":488},12,[56,490,491],{"class":383},"    }\n",[56,493,495],{"class":58,"line":494},13,[56,496,497],{"class":383},"  ]\n",[56,499,501],{"class":58,"line":500},14,[56,502,503],{"class":383},"}\n",[10,505,506,507,510],{},"One caveat on timing: ",[17,508,509],{},"vercel.json"," ships as part of a deployment, so expect to deploy for this to take effect. If you're going to want it, it's worth landing before the 10th rather than after.",[205,512,513],{},[10,514,515,516,519,520,169,523,169,526,169,529,532,533,536],{},"Don't rely on advice that ",[17,517,518],{},"routes"," cannot be combined with ",[17,521,522],{},"headers",[17,524,525],{},"rewrites",[17,527,528],{},"redirects",[17,530,531],{},"cleanUrls"," or ",[17,534,535],{},"trailingSlash",". That restriction used to exist and Vercel's current configuration docs say those keys can now coexist. It's a common piece of stale guidance that will send you rewriting a config you didn't need to touch.",[35,538,540],{"id":539},"the-wider-habit","The wider habit",[10,542,543,544,546,547,550,551,554,555,558],{},"Response headers are the cheapest information an attacker gets, and they're the part of your app nobody reviews, because nobody chose most of them. ",[17,545,19],{}," joins a familiar list: ",[17,548,549],{},"X-Powered-By"," announcing your framework, a ",[17,552,553],{},"Server"," header naming a version, ",[17,556,557],{},"X-Runtime"," publishing how long your request took.",[10,560,561],{},"None of these is a vulnerability. All of them narrow the search. A scanner that knows your framework and your response timings is choosing what to try next from a much shorter list than one starting cold. Our scanner reports these as information-disclosure findings with the exact header value it saw, because removing them is close to free and there's rarely a reason to keep them.",[10,563,564,565,568],{},"August 10 is a good excuse to read your whole response header list once. ",[17,566,567],{},"curl -sI https://your-app.vercel.app"," prints all of it. Most people have never looked.",[570,571,572,582,594,609,615],"faq-section",{},[573,574,576],"faq-item",{"question":575},"What is changing with Server-Timing on Vercel?",[10,577,578,579,581],{},"Vercel's changelog of July 30, 2026 says that on August 10, 2026 its CDN will stop stripping the ",[17,580,19],{}," response header and begin passing it through to the client. Vercel's own wording for the current behaviour is that the header is stripped from every response, so until August 10 anything your app sets in that header has not been reaching visitors.",[573,583,585],{"question":584},"Does Next.js or Prisma add a Server-Timing header automatically?",[10,586,587,588,590,591,593],{},"No. Native ",[17,589,19],{}," support in Next.js is still an open feature request, filed in May 2020 and not implemented. Prisma and Drizzle are ORMs with no HTTP response layer, so they cannot set a response header at all. Vercel's own list of headers it adds automatically does not include ",[17,592,19],{}," either. The header only exists on your responses if you or a library you installed explicitly set it, which is why running the check matters more than any assumption.",[573,595,597],{"question":596},"Can anyone actually see the Server-Timing header?",[10,598,599,600,602,603,605,606,608],{},"Anyone who receives the response can. It's an ordinary HTTP response header, so a plain ",[17,601,32],{}," shows it, no browser or developer tools needed. The ",[17,604,269],{}," header does not change this. That header only governs cross-origin access to the values through the ",[17,607,278],{}," JavaScript API, not whether the header itself is transmitted.",[573,610,612],{"question":611},"Is Server-Timing itself a vulnerability?",[10,613,614],{},"No, and it's worth being precise. MDN warns it may expose potentially sensitive application and infrastructure information, which is an information-disclosure concern rather than a flaw. Academic work at MADWeb 2023 found that the header amplifies a timing side channel that must already exist in your application, by removing network jitter and cutting the number of measurements an attacker needs. It does not create the side channel.",[573,616,618],{"question":617},"How do I stop the header from being sent?",[10,619,620,621,623,624,626],{},"The cleanest fix is to stop setting it in production, since the header only exists because something in your stack adds it. If you want it for internal use but not the public, Vercel documents a response-header transform in ",[17,622,509],{}," that deletes it. Note that ",[17,625,509],{}," ships as part of a deployment, so plan on deploying for the opt-out to take effect.",[628,629,630,636,641],"related-articles",{},[631,632],"related-card",{"description":633,"href":634,"title":635},"The other thing your host decides for you, and the 23 CVEs that rode on it","/blog/how-to/who-patches-node-runtime","Who Patches Your Node.js Runtime?",[631,637],{"description":638,"href":639,"title":640},"The headers Vercel does not set for you, and the vercel.json to add them","/blog/how-to/vercel-headers","How to Configure Security Headers on Vercel",[631,642],{"description":643,"href":644,"title":645},"Environment variables, preview protection, and the settings that actually matter","/blog/how-to/deploy-vercel-securely","Deploy to Vercel Securely",[647,648,651],"cta-box",{"href":649,"label":650},"/","Scan Your App Free",[10,652,653],{},"Your response headers are the first thing an attacker reads and the last thing anyone reviews. A CheckYourVibe scan checks your live app from the outside and reports every header that volunteers something about your stack.",[655,656,657],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}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 pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}",{"title":52,"searchDepth":387,"depth":387,"links":659},[660,661,662,663,664,665,666],{"id":37,"depth":387,"text":38},{"id":102,"depth":387,"text":103},{"id":123,"depth":387,"text":124},{"id":212,"depth":387,"text":213},{"id":288,"depth":387,"text":289},{"id":333,"depth":387,"text":334},{"id":539,"depth":387,"text":540},"how-to","2026-08-06","Vercel's CDN has been removing the Server-Timing header from every response. On August 10, 2026 it stops. Here is the one command that tells you whether that matters for your app.",false,"md",[673,675,677,679,681],{"question":575,"answer":674},"Vercel's changelog of July 30, 2026 says that on August 10, 2026 its CDN will stop stripping the Server-Timing response header and begin passing it through to the client. Vercel's own wording for the current behaviour is that the header is stripped from every response, so until August 10 anything your app sets in that header has not been reaching visitors.",{"question":584,"answer":676},"No. Native Server-Timing support in Next.js is still an open feature request, filed in May 2020 and not implemented. Prisma and Drizzle are ORMs with no HTTP response layer, so they cannot set a response header at all. Vercel's own list of headers it adds automatically does not include Server-Timing either. The header only exists on your responses if you or a library you installed explicitly set it, which is why the check below matters more than any assumption.",{"question":596,"answer":678},"Anyone who receives the response can. It is an ordinary HTTP response header, so a plain curl shows it, no browser or developer tools needed. The Timing-Allow-Origin header does not change this. That header only governs cross-origin access to the values through the PerformanceServerTiming JavaScript API, not whether the header itself is transmitted.",{"question":611,"answer":680},"No, and it is worth being precise. MDN warns it may expose potentially sensitive application and infrastructure information, which is an information-disclosure concern rather than a flaw. Academic work at MADWeb 2023 found that the header amplifies a timing side channel that must already exist in your application, by removing network jitter and cutting the number of measurements an attacker needs. It does not create the side channel.",{"question":617,"answer":682},"The cleanest fix is to stop setting it in production, since the header only exists because something in your stack adds it. If you want it in place for internal use but not the public, Vercel documents a response-header transform in vercel.json that deletes it. Note that vercel.json ships as part of a deployment, so plan on deploying for the opt-out to take effect.","yellow",null,"vercel server-timing header, server-timing exposed, vercel stops stripping server-timing, is server-timing a security risk, hide server-timing header, vercel.json delete response header, server-timing information disclosure",{"trendTrigger":687,"trendDate":688},"vercel-changelog-server-timing-cdn-passthrough","2026-07-30",true,"A header you may have set for local debugging, believing Vercel was stripping it, reaches the public on August 10. One curl tells you if you have one.","/blog/how-to/vercel-server-timing-exposed","8 min read","[object Object]","HowTo",{"title":5,"description":669},{"loc":691},"blog/how-to/vercel-server-timing-exposed",[],"summary_large_image","j8iFoQkg61n7qLOZZal-D0kz4FVttGaIFflf3wwzegA",1786039026830]