{"course":{"slug":"cloud-and-shipping","title":"Shipping It","url":"https://addaly.com/learn/cloud-and-shipping","level":"intermediate","track":"build"},"lesson":{"slug":"one-request-end-to-end","title":"One request, with a stopwatch","url":"https://addaly.com/learn/cloud-and-shipping/one-request-end-to-end","position":8,"of":74,"minutes":10,"answer":"Most of a first request is round trips you cannot code your way out of, so measure where the time actually goes before optimising the part you wrote.","keyIdea":"Most of a first request is round trips you cannot code your way out of, so measure where the time actually goes before optimising the part you wrote."},"body":{"format":"markdown","text":"## Measure first, and it takes one command\n\n`curl` will break a request into its phases. This is the most useful command in this course:\n\n```bash\ncurl -o /dev/null -s -w '\\\ndns      %{time_namelookup}s\ntcp      %{time_connect}s\ntls      %{time_appconnect}s\nttfb     %{time_starttransfer}s\ntotal    %{time_total}s\n' https://addaly.com/\n```\n\nEach number is cumulative from the start, not a duration, which trips people up. Subtract to get the phases. A realistic result from a phone-speed connection in Mumbai hitting a server in Virginia:\n\n```\ndns      0.028s\ntcp      0.211s\ntls      0.402s\nttfb     0.638s\ntotal    0.661s\n```\n\nRead that as: DNS took 28ms, the TCP handshake took 183ms, the TLS handshake took another 191ms, your server took 236ms to produce the first byte, and the body took 23ms.\n\nNow notice what you just learned. Your code is responsible for 236ms of a 661ms request. Doubling the size of the server improves the number that is not the problem.\n\n> Figure: Where 661 ms went: Mumbai to Virginia, cold connection. Bars (ms): DNS lookup 28; TCP handshake 183; TLS handshake 191; Server: time to first byte 236; Body transfer 23. Your code produced the first byte in 236 ms. The other 425 ms was distance and handshakes. A bigger server improves the number that is not the problem.\n\n## The part that is physics\n\nLight in fibre travels at roughly 200,000 km per second, and real routes are not straight. Mumbai to northern Virginia is about 13,000 km, so one way is about 65ms and a round trip about 130ms at the theoretical floor. In practice you see 180 to 220ms.\n\nNow count the round trips before your code runs:\n\n1. DNS: one round trip, if it is not cached.\n2. TCP handshake: one round trip.\n3. TLS handshake: one round trip on TLS 1.3, two on TLS 1.2.\n\nSo a cold connection from India to a US server spends about 400ms doing nothing but agreeing to talk. No amount of optimisation touches that, because it is distance. This is the entire argument for a CDN: not bandwidth, but shortening the round trip. \"The server is fast\" and \"the site is fast\" are different claims, and the gap between them is measured in kilometres.\n\nTwo consolations. TLS session resumption skips a round trip on repeat visits. And keep-alive means the second request on the same connection skips both handshakes, which is why your first page load feels slow and everything after it feels fine.\n\n## The DNS number is lying to you\n\nThe first measurement includes a cold resolver lookup. Run it again and DNS will read close to zero, because your machine cached it. Both numbers are true: the first is what a new visitor gets, the second is what a returning one gets. Report which you measured.\n\n## Cold starts, and why your monitoring disagrees with your users\n\nTwo flavours, both worth knowing:\n\n- **Serverless functions** that have been idle boot on demand: usually 100ms to 1 second, longer for a big dependency tree or a JVM.\n- **Free-tier containers that scale to zero** (the default on several free plans) sleep after about 15 minutes of no traffic and take 5 to 30 seconds to wake. The first visitor of the day gets a page that appears broken.\n\nHere is the trap. An uptime monitor pinging every five minutes keeps the container permanently warm. Your dashboard shows a fast, healthy site. Your users, arriving one at a time to a service nobody else is using, see the cold start every time. The monitoring is not wrong; it is participating.\n\n## One page is not one request\n\nThe HTML is request one. Then come the stylesheets, the fonts, the JavaScript, the images, the analytics tag and the chat widget. A modest page makes 40 to 80 requests, and requests to a new hostname pay their own DNS, TCP and TLS costs, which is why one third-party script can cost more than your entire backend.\n\nOpen the network tab, sort by time, and look at what is on the critical path. A render-blocking stylesheet on a third-party domain is a classic: 400ms of blank screen that has nothing to do with your server.\n\n## Fix in this order\n\n1. **Shorten the distance.** Static assets on a CDN. If your framework allows it, HTML from the edge too. This is usually the largest single win and it is often free.\n2. **Remove round trips.** Keep-alive, HTTP/2 or HTTP/3, fewer distinct origins, no redirect chains: each redirect is a whole extra request including handshakes.\n3. **Then make your code faster.**\n\nAlmost everyone starts at three, because it is the part they wrote and the part they can see."},"check":{"question":"Your server-side timing shows a median response time of 40ms, and your five-minute uptime monitor agrees the site is fast. Users in another country report that the site takes several seconds to load, especially the first visit of the day. Which explanation fits all three observations?","options":[{"text":"The server is under-provisioned, and the 40ms figure is measured before request queueing","correct":false,"tempting":"Queueing would show up in the monitor's own timings too, since the monitor's requests wait in the same queue as everyone else's."},{"text":"Their internet connections are slow, so there is nothing to be done from your side","correct":false,"tempting":"Treats bandwidth as the only client-side variable and ends the investigation, when distance and cold starts are both yours to fix."},{"text":"DNS for your domain has a very long TTL, so their resolvers are answering from a stale cache","correct":false,"tempting":"Misreads caching: a long TTL makes lookups faster for them, and a stale record would break the site rather than slow it."},{"text":"Round trips over distance plus a container that scales to zero, and the monitor's regular pings are keeping it warm, so the monitor never sees the cold start","correct":true}]},"prev":{"slug":"behind-the-proxy","title":"What sits in front of your app","url":"https://addaly.com/learn/cloud-and-shipping/behind-the-proxy"},"next":{"slug":"keeping-the-connection-open","title":"The handshake you keep paying for","url":"https://addaly.com/learn/cloud-and-shipping/keeping-the-connection-open"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'One request, with a stopwatch', in Shipping It. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/cloud-and-shipping/one-request-end-to-end (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"One request, with a stopwatch","courseTitle":"Shipping It","module":{"index":1,"slug":"how-the-internet-reaches-you","title":"How the internet reaches your process"},"answer":null,"keyIdea":"Most of a first request is round trips you cannot code your way out of, so measure where the time actually goes before optimising the part you wrote.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"bars","unit":"ms","title":"Where 661 ms went: Mumbai to Virginia, cold connection","series":[{"label":"DNS lookup","value":28},{"label":"TCP handshake","value":183},{"label":"TLS handshake","value":191},{"label":"Server: time to first byte","value":236,"highlight":true},{"label":"Body transfer","value":23}],"caption":"Your code produced the first byte in 236 ms. The other 425 ms was distance and handshakes. A bigger server improves the number that is not the problem."},"alt":"Figure: Where 661 ms went: Mumbai to Virginia, cold connection. Bars (ms): DNS lookup 28; TCP handshake 183; TLS handshake 191; Server: time to first byte 236; Body transfer 23. Your code produced the first byte in 236 ms. The other 425 ms was distance and handshakes. A bigger server improves the number that is not the problem."}},"blocks":[{"t":"h2","id":"measure-first-and-it-takes-one-command","runs":[{"text":"Measure first, and it takes one command"}]},{"t":"p","runs":[{"text":"curl","code":true},{"text":" will break a request into its phases. This is the most useful command in this course:"}]},{"t":"code","lang":"bash","label":"Bash","text":"curl -o /dev/null -s -w '\\\ndns      %{time_namelookup}s\ntcp      %{time_connect}s\ntls      %{time_appconnect}s\nttfb     %{time_starttransfer}s\ntotal    %{time_total}s\n' https://addaly.com/"},{"t":"p","runs":[{"text":"Each number is cumulative from the start, not a duration, which trips people up. Subtract to get the phases. A realistic result from a phone-speed connection in Mumbai hitting a server in Virginia:"}]},{"t":"code","lang":"","label":"Text","text":"dns      0.028s\ntcp      0.211s\ntls      0.402s\nttfb     0.638s\ntotal    0.661s"},{"t":"p","runs":[{"text":"Read that as: DNS took 28ms, the TCP handshake took 183ms, the TLS handshake took another 191ms, your server took 236ms to produce the first byte, and the body took 23ms."}]},{"t":"p","runs":[{"text":"Now notice what you just learned. Your code is responsible for 236ms of a 661ms request. Doubling the size of the server improves the number that is not the problem."}]},{"t":"h2","id":"the-part-that-is-physics","runs":[{"text":"The part that is physics"}]},{"t":"p","runs":[{"text":"Light in fibre travels at roughly 200,000 km per second, and real routes are not straight. Mumbai to northern Virginia is about 13,000 km, so one way is about 65ms and a round trip about 130ms at the theoretical floor. In practice you see 180 to 220ms."}]},{"t":"p","runs":[{"text":"Now count the round trips before your code runs:"}]},{"t":"ol","start":1,"items":[[{"text":"DNS: one round trip, if it is not cached."}],[{"text":"TCP handshake: one round trip."}],[{"text":"TLS handshake: one round trip on TLS 1.3, two on TLS 1.2."}]]},{"t":"p","runs":[{"text":"So a cold connection from India to a US server spends about 400ms doing nothing but agreeing to talk. No amount of optimisation touches that, because it is distance. This is the entire argument for a CDN: not bandwidth, but shortening the round trip. “The server is fast” and “the site is fast” are different claims, and the gap between them is measured in kilometres."}]},{"t":"p","runs":[{"text":"Two consolations. TLS session resumption skips a round trip on repeat visits. And keep-alive means the second request on the same connection skips both handshakes, which is why your first page load feels slow and everything after it feels fine."}]},{"t":"h2","id":"the-dns-number-is-lying-to-you","runs":[{"text":"The DNS number is lying to you"}]},{"t":"p","runs":[{"text":"The first measurement includes a cold resolver lookup. Run it again and DNS will read close to zero, because your machine cached it. Both numbers are true: the first is what a new visitor gets, the second is what a returning one gets. Report which you measured."}]},{"t":"h2","id":"cold-starts-and-why-your-monitoring-disagrees-with-your-user","runs":[{"text":"Cold starts, and why your monitoring disagrees with your users"}]},{"t":"p","runs":[{"text":"Two flavours, both worth knowing:"}]},{"t":"ul","items":[[{"text":"Serverless functions","b":true},{"text":" that have been idle boot on demand: usually 100ms to 1 second, longer for a big dependency tree or a JVM."}],[{"text":"Free-tier containers that scale to zero","b":true},{"text":" (the default on several free plans) sleep after about 15 minutes of no traffic and take 5 to 30 seconds to wake. The first visitor of the day gets a page that appears broken."}]]},{"t":"p","runs":[{"text":"Here is the trap. An uptime monitor pinging every five minutes keeps the container permanently warm. Your dashboard shows a fast, healthy site. Your users, arriving one at a time to a service nobody else is using, see the cold start every time. The monitoring is not wrong; it is participating."}]},{"t":"h2","id":"one-page-is-not-one-request","runs":[{"text":"One page is not one request"}]},{"t":"p","runs":[{"text":"The HTML is request one. Then come the stylesheets, the fonts, the JavaScript, the images, the analytics tag and the chat widget. A modest page makes 40 to 80 requests, and requests to a new hostname pay their own DNS, TCP and TLS costs, which is why one third-party script can cost more than your entire backend."}]},{"t":"p","runs":[{"text":"Open the network tab, sort by time, and look at what is on the critical path. A render-blocking stylesheet on a third-party domain is a classic: 400ms of blank screen that has nothing to do with your server."}]},{"t":"h2","id":"fix-in-this-order","runs":[{"text":"Fix in this order"}]},{"t":"ol","start":1,"items":[[{"text":"Shorten the distance.","b":true},{"text":" Static assets on a CDN. If your framework allows it, HTML from the edge too. This is usually the largest single win and it is often free."}],[{"text":"Remove round trips.","b":true},{"text":" Keep-alive, HTTP/2 or HTTP/3, fewer distinct origins, no redirect chains: each redirect is a whole extra request including handshakes."}],[{"text":"Then make your code faster.","b":true}]]},{"t":"p","runs":[{"text":"Almost everyone starts at three, because it is the part they wrote and the part they can see."}]}],"check":{"question":[{"text":"Your server-side timing shows a median response time of 40ms, and your five-minute uptime monitor agrees the site is fast. Users in another country report that the site takes several seconds to load, especially the first visit of the day. Which explanation fits all three observations?"}],"options":[{"text":[{"text":"The server is under-provisioned, and the 40ms figure is measured before request queueing"}],"correct":false,"tempting":[{"text":"Queueing would show up in the monitor’s own timings too, since the monitor’s requests wait in the same queue as everyone else’s."}]},{"text":[{"text":"Their internet connections are slow, so there is nothing to be done from your side"}],"correct":false,"tempting":[{"text":"Treats bandwidth as the only client-side variable and ends the investigation, when distance and cold starts are both yours to fix."}]},{"text":[{"text":"DNS for your domain has a very long TTL, so their resolvers are answering from a stale cache"}],"correct":false,"tempting":[{"text":"Misreads caching: a long TTL makes lookups faster for them, and a stale record would break the site rather than slow it."}]},{"text":[{"text":"Round trips over distance plus a container that scales to zero, and the monitor’s regular pings are keeping it warm, so the monitor never sees the cold start"}],"correct":true,"tempting":null}],"order":[0,2,1,3],"why":null},"prev":{"slug":"behind-the-proxy","title":"What sits in front of your app","minutes":9},"next":{"slug":"keeping-the-connection-open","title":"The handshake you keep paying for","minutes":9}}}