lag.dev

How lag.dev measures

Version 3 ยท Last updated

lag.dev measures the latency from your browser to eight servers at once, how far that latency rises while your connection is busy, and how steady it stays over time. This page states how, so every figure can be checked and compared with other tests.

The servers

The servers are URnetwork extenders: machines that URnetwork providers run around the world. Each run uses eight of them. Three are near you, picked at random among the eight nearest and preferring different cities. Five are far away, at least 1,500 km from you and 500 km from the near ones, chosen to cover as many countries and as much of the globe as possible.

Your location for this choice comes from your IP address, looked up in MaxMind's GeoLite2 database when the run starts. The address itself is not stored. The tests run nearest first, and every new run picks a fresh eight, preferring servers you haven't used yet.

Latency

Each server pings your browser over a WebSocket. It sends a ping frame carrying 16 random bytes, your browser's network stack answers with a pong, and the server times the round trip. Page scripts never handle the ping, so a busy page can't delay it, and the random bytes stop a page from answering early to fake a faster result.

  • A server sends its next ping 250 ms after the last one, or as soon as the answer arrives if the round trip takes longer: about four a second.
  • A ping with no answer within 5 seconds counts as lost. Lost pings show as gaps in the plots.
  • Pings run to all eight servers at once, from the start of a run until you close the page or start another run.
  • Each server signs the results it reports, so they can't be altered on the way.

Loads

Five seconds in, the first server runs a load: a 5-second download, then a 5-second upload. Each next load starts 15 seconds after the previous one began, so only one load runs at a time and all eight finish in about two minutes. The pings to every server keep running throughout.

  • A download is four parallel HTTPS streams of random bytes, which can't be compressed. Your browser counts the bytes as they arrive, in 250 ms intervals, and the server caps those counts at what it actually sent.
  • An upload is four parallel WebSocket streams of random bytes that your browser generates from a key the server gives it (AES-128 in counter mode). The server generates the same bytes and compares every message, so compressed or made-up data fails the check and the upload is marked unverified. On hardware without AES instructions it checks a random 1 in 16 messages. The server counts upload bytes as they arrive, and the upload speed you see is the one it signs and stores.
  • In both directions, data arrives in pieces, and each direction is counted over a 5-second window from its first byte. Each piece is counted as it arrives, spread evenly over the time it took to arrive: since the previous piece on the same stream, up to 1 second. A piece whose arrival spans two intervals counts partly in each, so the 250 ms intervals aren't rounded to whole pieces, which would make a slower connection's speed jump in steps. A burst that comes after a stall is spread back over the stall. A stream's first piece is placed at that stream's rate, so bytes that arrived before the window opened don't count, and the window's last piece counts up to its end. Each sender keeps sending until the receiver's window has closed.
  • Loads run only while the page is visible. A hidden tab's timers slow to about once a second, which would starve an upload and record a falsely slow speed. A load interrupted by hiding the page is dropped and run again when you come back.

The figures

  • Download and upload: the median (p50) and 95th percentile (p95) of the 250 ms interval rates during that server's own load, in Mbps.
  • Idle latency: p50 and p95 of the pings whose whole round trip falls outside every load. Each load's span includes 3.5 seconds after its upload, while queues drain.
  • Latency under load: p50 and p95 of the pings to a server sent during its own download and upload.
  • Increase: latency under load p50 minus idle p50, for each server.
  • Best overall: the lowest idle p50, the highest download p50 and the highest upload p50 across your runs.

Because the pings to all eight servers keep running during each load, every latency plot also shows what each load does to your latency elsewhere. On those plots, latency above 1.5 times that server's idle p50 turns gold, then orange at 2.25 times and red from 3 times.

Stability

Pings never stop while the page is open, so each latency plot is a running record of the last minute to that server. Spikes show when latency jumps and gaps show lost pings. Once all eight loads finish, each plot holds the minute around its own load, while the figures keep following new pings.

The ping drift view fits a position to the eight idle p50s by least squares. It assumes a signal covers 133 km per millisecond one way, about 4/9 of the speed of light, plus one last-mile delay shared by every path. It then shows how far that position lands from where your IP address places you. It is a rough guide to where your traffic enters the network, not a location.

What lag.dev doesn't measure

  • Packet loss. Pings travel over TCP, which resends lost packets, so a lost ping means no answer within 5 seconds, not a dropped packet. lag.dev doesn't run a UDP packet-loss test.
  • Your line's maximum speed. Each load measures the path to one server, and a server's own connection can be slower than yours.
  • Jitter as one number. lag.dev reports p95 beside p50 instead, which shows how far latency spreads.

How lag.dev's figures compare

Tests define latency, latency under load and jitter differently. This table maps each lag.dev figure to the nearest equivalent elsewhere, as each publishes it on 13 October 2026.

lag.dev's figures beside other tests and standards
Figurelag.devSpeedtest (Ookla)CloudflareWaveformIETF responsiveness (RPM)New Zealand (MBNZ)
Idle latencyp50 and p95 of pings outside every load, per server, 8 servers at onceInterquartile mean of idle pings to one primary serverMedian of time-to-first-byte pings to the nearest edgeUnloaded latencyReported beside responsiveness99th-percentile HTTP latency under idle conditions
Latency under loadp50 and p95 during each server's own download and uploadInterquartile mean of pings during download and during uploadLatency during the download and upload phasesLatency during download and during uploadWorking latency under saturating load, as RPM"Responsiveness": 99th-percentile latency under maximum load
Increase under loadUnder-load p50 minus idle p50Not reported as one figureNot reported as one figureMean increase, graded A+ (5 ms or less) to F (over 400 ms)Not usedNot used
JitterNo single figure; p95 beside p50Average difference between consecutive pingsAverage change between consecutive pingsMean absolute deviation from the meanNot used"A measure of the stability of latency"
Loss and dropoutsLost pings (no answer in 5 s), shown as gapsPacket loss reportedly in its apps onlyPacket loss over WebRTCNot measuredNot usedA disconnection: measurement packets lost for 30 seconds or longer
Stability over timeContinuous pings to 8 servers while the page is openNoneNoneDisclaimed: "latency under load, not reliability"NoneDisconnections, as a median daily rate
Where3 servers near you and 5 far, at onceNearby servers from a network of over 15,000The nearest edge of its anycast networkCloudflare, Fly.io and Fastly serversThe server under testA panel of measurement units in homes

Converting to RPM

The IETF draft "Responsiveness under Working Conditions" reports latency under load in round trips per minute: RPM is 60,000 divided by the round trip in milliseconds. To express a lag.dev figure in RPM, divide 60,000 by it. A 40 ms latency under load is 1,500 RPM, which the draft rates Good.

The draft's bands (draft-ietf-ippm-responsiveness-09)
BandRPMWorking latency
Poorunder 300over 200 ms
Fair300 to 1,00060 to 200 ms
Good1,000 to 6,00010 to 60 ms
Excellent6,000 or more10 ms or less

The draft is version 09, from July 2026, and is not yet an RFC. Its method differs from lag.dev's: it saturates the link with HTTP transfers and averages its probes with a 95% trimmed mean. Treat the conversion as approximate.

Regulators' definitions

New Zealand's Commerce Commission reports bufferbloat as the 99th-percentile latency under load, and counts a disconnection when "latency measurement packets were lost for 30 seconds or longer". lag.dev reports the 95th percentile, not the 99th. A gap of 30 seconds or more in one of its plots is what that report would count as a disconnection.

Location data

Server and visitor locations come from MaxMind's GeoLite2 databases. They are refreshed within 30 days of each MaxMind release, as its licence requires, and lag.dev shows locations at city level only.

Data use

A full run moves about 5 MB for every Mbps of your combined download and upload speed, if every server reaches your connection's full speed: about 600 MB on a 100/20 Mbps connection. Far servers rarely reach it, so most runs move less. Each server's download and upload are capped at 2 GiB each way. The pings alone, four a second to each of the eight servers, use roughly 35 MB an hour.

Privacy

Measurements are used to improve URnetwork's network performance. lag.dev's server stores no IP address. Each run keeps your network (its ASN) and city-level location for 7 days, then deletes them. The site uses no analytics. See URnetwork's privacy policy for the details.

Validation

After launch, we will compare lag.dev's figures with Flent and Apple's networkQuality on the same connections and publish the results here. Until then, this page describes the method, not its measured accuracy.

Sources

  1. Ookla, Speedtest test methods (updated 15 October 2025)
  2. Cloudflare speed test, about
  3. Waveform bufferbloat test; grades, jitter and the stability disclaimer as in its page script, October 2026
  4. draft-ietf-ippm-responsiveness-09 (July 2026)
  5. Commerce Commission, Measuring Broadband New Zealand, Report 27 (April 2026)
  6. MaxMind GeoLite End User License Agreement

Changes

  • Version 3, 15 October 2026: each load's span now runs 3.5 seconds after its upload, not 2. On a slow uplink the server's window ends up to a second after the page's, and what the browser queued drains for up to 2 seconds after that, so pings in that time were counted as idle while the connection was still loaded.
  • Version 2, 14 October 2026: bytes are counted as they arrive, each piece spread over the time it took to arrive and a burst over the stall before it, and only what falls inside the window counts; each sender keeps sending until the receiver's window has closed; and the upload speed shown is the one the server signs and stores.
  • Version 1, 13 October 2026: first version.

Related: bufferbloat and stability and how lag.dev compares.

Run the test