Nuno
Nuno
All posts

What 1.3M Requests in 2 Hours Taught Me About TLS

Before launching redir.rocks I ran a 2-hour soak of 1.3M HTTPS requests. 62 errors, exact metering, and a ceiling of about 190 new TLS connections a second.
Vander Nunes
November 12, 2026
XInstagramYouTubeLinkedIn
Line chart of requests per second over a two-hour load test, flat throughout, with a small number of error markers

The result first

On 8 and 9 October 2026, a few days before launch, I load-tested redir.rocks, the redirect service I run with my co-founder. He writes the application; I own the infrastructure, the edge and operations, so the launch test was mine.

In total I sent about 2.1M requests. The main run was a 2-hour soak: 1.3M HTTPS requests, 62 errors. That is about 5 errors in every 100,000 requests. Response times did not drift over the two hours, and memory did not grow. All 33 redirect rule checks passed in each of 7 rounds.

That is the part that went well. The useful part was what the test found.

Why HTTPS and not plain HTTP

A redirect service has an unusual traffic shape. Most visitors arrive once, from a link in an email, an ad or a QR code, get a redirect and leave. There is no second request on the same connection. So almost every request is a new TLS connection, with a full handshake.

TLS handshakes cost CPU. Serving a redirect once the connection is open is cheap. If you load-test with keep-alive connections, which most load tools use by default, you are measuring the cheap part and skipping the expensive one.

So I tested both. Plain HTTP reached about 2,100 requests a second. New HTTPS connections topped out at about 190 a second. That gap, about 11 times, is the cost of the handshake, and it is the number that matters for a redirect service.

The ceiling was one core

190 new TLS connections a second was lower than I expected for the instance. Looking at the host during the test, the gateway was using one of the instance's two cores.

That changes the conversation. The answer is not "buy a bigger instance". A bigger instance with the same behaviour would have the same ceiling per core and more idle cores. The first job is to make the gateway use the cores it already has.

The point for anyone running a load test: look at per-core CPU, not the host average. An instance at 50% average CPU can be completely saturated if the work is pinned to one core.

Count what you bill for

Every redirect is counted, because usage is what customers pay for. Usage events go to a separate TimescaleDB database, and billing reads the month-to-date total from it.

A load test is a perfect time to check that counting is exact, because you know exactly what you sent. I sent 1,000 requests to one test domain and checked the count. It went up by exactly 1,000. Not 998, not 1,003.

This sounds obvious, but it is easy to skip. Most load tests check that responses come back. Very few check that the side effects (rows written, events emitted, invoices affected) match the requests. For a product that bills on usage, the side effects are the product.

One vantage point lied

Part of the test checked what the service does with the visitor's location. Running it from one place gave a strange result: requests from a home connection in Brazil were treated as coming from Australia.

The geo database mapped that home IP address to the wrong country. The code was right. The data was wrong for that address.

If I had only tested from that one connection, I would have either "fixed" correct code or shipped believing country rules were broken. Testing anything based on location needs several vantage points, and when one of them disagrees, check the database lookup for that IP before touching the code.

The blocker it found

The test also found something that would have hurt us after launch.

After each application deploy, the edge stopped syncing its configuration from the cluster, so changes made after the deploy would not reach it. The cause was how the cluster database rejoined after the deploy. Restarting the gateway to recover it caused a 2.5-minute outage.

This is the kind of fault a load test finds almost by accident. It is not about load at all. It shows up because a long test spans a deploy, and because you are watching the system closely for two hours.

The workaround was to restart the gateway after every application deploy, so it joins the new node cleanly. The lasting fix belongs in the application, which is my co-founder's side.

What I would do the same way again

  • Test HTTPS with new connections, not only keep-alive.
  • Run a soak of hours, not minutes. Leaks and drift need time.
  • Check side effects: send N requests, count N rows.
  • Test location from more than one place.
  • Look at per-core CPU.
  • Write down the ceiling you found, so the first time real traffic reaches it is not a surprise.

The 62 errors were worth knowing about too. At about 5 in 100,000, they were not a launch blocker, but they are now a baseline. If the next test shows 600, something changed.

A short plan for your own launch test

  1. Decide which number matters: new connections, requests, or something you bill for.
  2. Make the test traffic look like real traffic in that number.
  3. Run a short test to find the ceiling, then a long soak below it.
  4. Count the side effects.
  5. Do a deploy during the soak, on purpose.

If you have a launch coming and want help planning the test, get in touch.