A SYN flood attack never finishes a single connection. It sends the first packet of the TCP handshake, thousands of times a second, from source addresses that do not exist, then walks away. Each packet leaves the server holding a slot open for a client that will never answer.

It is one of the oldest attacks on the internet and still one of the most common. Cloudflare’s 2025 Q2 DDoS threat report, published 15 July 2025, put SYN floods second among all network-layer vectors at 27% of attacks, down from 31% the quarter before.

The short version

This post is an explainer rather than part of our ten-part simulation series, because a SYN flood is not one of the ten types Obsidio runs, and the reason why is the most useful thing in here. For how the vector families relate to each other, see our overview of the types of DDoS attacks; for how to structure a first authorized run, start with the guide on how to test your DDoS protection.

What is a SYN flood, mechanically?

Every TCP connection opens with a three-step exchange. The client sends a SYN to say it wants to talk. The server replies with a SYN-ACK to say it is willing, and reserves memory for the coming connection. The client sends a final ACK, and the connection is established.

Between step two and step three the connection is half-open. The server has committed state and is waiting. That state lives in a per-listener queue, usually called the SYN backlog, and on Linux each entry sits in SYN_RECV until the ACK arrives or the entry expires.

A SYN flood sends step one and never sends step three. Each packet is 40-odd bytes and costs the sender nothing. Each one costs the server a backlog entry that will be held for as long as the kernel keeps retrying the SYN-ACK.

On Linux the relevant knob is tcp_synack_retries, documented in the kernel’s networking sysctl reference with a default of 5, “which corresponds to 31 seconds till the last retransmission with the current initial RTO of 1 second”. Half a minute of free occupancy per packet, and the packets arrive by the thousand.

When the backlog is full, further SYN packets are dropped. From the outside the site is simply unreachable. From the inside, CPU is low, bandwidth is low, the application logs are quiet, and nothing is broken except the ability to accept a new connection.

Picture a receptionist with a desk that holds twenty message slips. Twenty callers each say “hold on, I’ll be right back with my name” and never return. The receptionist is not busy; the desk is just full, and the twenty-first caller is told to go away.

Why does source-address spoofing matter so much?

The attack depends on the server’s reply going nowhere. RFC 4987, “TCP SYN Flooding Attacks and Common Mitigations”, published in August 2007, is blunt about it: “For an effective attack, it is important that the spoofed IP addresses be unresponsive to the SYN-ACK segments.” If the forged address belongs to a live host, that host receives an unsolicited SYN-ACK, does not recognise it, and sends a reset. The reset frees the backlog entry instantly and the attack collapses.

So attackers forge addresses that are dark: unallocated space, or ranges with nothing listening. That has three consequences worth holding onto.

Which is why the oldest structural fix is not on the victim’s side. RFC 2827, better known as BCP 38, published in May 2000, asks every network to filter outbound traffic so that it can only carry source addresses from the ranges that network actually announces. Adoption is partial after a quarter of a century. CAIDA’s Spoofer project, which continuously measures source-address validation from volunteer vantage points, showed 233 of 1,430 tested IPv4 blocks as spoofable when we checked its summary page on 29 July 2026, roughly one in six once NAT cases are excluded.

The history is worth a line. CERT Advisory CA-1996-21 documented SYN flooding and IP spoofing in September 1996, prompted partly by an attack that took down the mail servers of New York ISP Panix.

Thirty years on, Cloudflare’s 2025 Q3 report of 3 December 2025 still listed SYN floods third among network-layer vectors, behind UDP and DNS floods and ahead of ICMP. Together those four made up just over half of all network-layer attacks in a quarter where Cloudflare mitigated 8.3 million attacks overall.

Which of your controls should have caught this?

It is worth walking the whole chain a connection passes through. For most attacks in this series the answer is uncomfortable. For this one it is mostly reassuring, and saying so plainly matters more than making every control look broken.

Zone 1

Should any of these have stopped it?

  • DDoS protection and scrubbing. This is the vector these services were built for. The scrubbing centre answers the handshake itself and forwards only connections a real client completed. It holds, provided your traffic routes through it and your origin is not reachable directly.
  • Firewall and WAF. Pull the two apart. A WAF inspects requests and no request is ever sent, so it sees nothing. A stateful firewall does see it, and can become the bottleneck itself if its state table is smaller than the server’s backlog.
  • Rate limiting. Keys on source address or on requests. The sources are forged and never repeat, and no request arrives to be counted.
  • Bot detection and challenges. A challenge needs a session to challenge. The connection never opens.
  • CDN cache. Nothing cacheable is in play. It matters only because a CDN keeps your origin address off the path, and that is the part which decides this.

Zone 2

What takes the hit?

  • Load balancer or reverse proxy. Whatever terminates TCP first owns this problem. If the flood reaches you at all, the accept queue and connection state here are what fill up.
  • Connection layer. The SYN backlog is the actual target. SYN cookies turn a memory-exhaustion problem into a cryptographic-CPU one, which is a much better trade but not a free one.
  • Web server. The kernel handles the half-open queue. The server process is never handed a connection, so worker slots and request handling stay untouched.
  • Application and database. Never reached. No request is ever completed, so no query is ever issued.
  • Auto-scaling. Watches request rate, latency and CPU. Half-open connections are not requests, so the signals stay flat while the service is unreachable.

Zone 3

Would you find out?

  • Dashboards. Packets per second and new connections per second do move sharply, unlike a slow attack. Whether you notice depends on whether anyone graphs TCP connection state rather than only HTTP metrics.
  • A check from outside your network. Still the only thing that reliably tells you real users are being refused, since every internal metric can look calm.
  • Logging and your SIEM. No access-log lines, because no request completes. Firewall and flow logs do carry it, if you collect and retain them.
  • Alerting. Fires if you have a threshold on connection state or packet rate. Most alert sets watch error rate and response time, which do not move here.
  • On-call. Usually paged eventually, by a customer or by your scrubbing provider, rather than by your own monitoring.

Purple marks what only a test on your own infrastructure can settle. Everything else follows from how the attack works, not from how well your team configured things.

Compare that grid with the one on the Slowloris post and the verdicts largely invert. Volume-based scrubbing is blind to a slow attack and decisive against a SYN flood. That inversion is the argument for coverage across vector families.

How do you mitigate a SYN flood?

Four controls carry almost all of the weight, and three of them are already on in a default modern deployment.

That last precondition is where real deployments fail. A protected hostname in front of an origin whose address still answers on port 443 is not protected, and nothing in a configuration review makes that visible.

Why is a SYN flood attack a poor test of connection capacity?

Here is the part that matters if you are planning a resilience test rather than reading a glossary. A SYN flood measures one thing: whether the first device that terminates TCP can survive being asked to remember handshakes that never complete. In 2026 the answer is usually yes, because SYN cookies exist and edge proxying works.

Completed connections are a different problem. A fully established, idle TCP connection consumes a socket and a file descriptor on the server, an entry in the connection-tracking table of every stateful device on the path, and a slot against any per-IP or global connection limit. SYN cookies do nothing about any of that, because from the stack’s point of view these connections are entirely legitimate. They were opened by real hosts that answered honestly.

This is why Obsidio’s catalogue includes TCP Connection Flood and not a SYN flood. The simulation opens raw TCP connections, completes the handshake, and holds each socket idle for up to the configured timeout before recycling it, with a default hold of ten minutes and up to a hundred sockets per worker. Because the traffic comes from 100,000+ globally distributed real devices with real routable addresses, per-IP connection caps get tested against genuine source diversity rather than against a handful of datacenter hosts, and the conntrack tables on every device in the path fill with entries that no cookie trick can shed.

A TLS Flood goes one layer further again, completing the TLS negotiation and paying the server roughly fifteen times the client’s crypto cost before abandoning the socket. Half-open, fully open and fully negotiated test three different resources.

What you can check yourself, and what it will get wrong

Two of these questions you can answer today without any traffic, and it is worth knowing which.

Read the configuration. Confirm tcp_syncookies is enabled, look at your actual backlog and retry values, and check the state-table limits on every stateful device in the path, not just the server. This tells you the settings exist. It does not tell you which limit is smallest, and the smallest one is the one that decides the outcome.

Confirm your origin is unreachable directly. If an edge service is the control you are relying on, verify from outside that the origin address does not answer. This is a genuine test of a genuine dependency and it needs no attack traffic.

Test alert routing on its own. Fire a synthetic alert at the on-call rotation and see whether anyone acts. That verifies the plumbing, and says nothing about whether a threshold exists on TCP connection state, which is the signal that would have moved.

What a small self-run test gets wrong is more interesting. Traffic from one or two hosts is caught by per-source controls immediately, so the front of the chain appears to work perfectly. That conclusion is backwards: those are precisely the controls that behave differently against real source diversity.

How Obsidio tests the connection layer

Obsidio runs connection-exhaustion simulations as controlled, authorized tests against infrastructure the customer owns. Domain ownership is verified by DNS TXT record before any traffic flows. Per the platform FAQ, most institutions start in a controlled window with conservative limits and ramp up as confidence grows, and live metrics let a run be aborted instantly.

For a TCP Connection Flood the numbers to watch are not the request-rate graph, which counts socket opens rather than throughput and will look trivially small while the test is succeeding. Watch active and peak concurrent connections, and watch them alongside the connection-table utilisation on your own load balancer and firewall. That pairing is what tells you which limit is binding.

Two answers stay on your side of the line. Obsidio can report what your edge controls did and how many connections were held, because that is observable from outside. Whether your SIEM recorded anything and whether anyone was paged live only in your own consoles, so somebody has to be watching them during the window.

Strength is proven, not promised. A SYN flood asks whether your kernel can forget a handshake. The harder question, and the one Obsidio actually measures, is what happens when a hundred thousand real devices complete one and refuse to hang up.

Where this fits in a testing programme

Treat the classic SYN flood as a control you verify rather than a scenario you rehearse. Check that cookies are on, that your edge proxies handshakes, that your origin is not reachable around it, and move on. Cloudflare’s 2025 Q4 report of 5 February 2026 counted 47.1 million DDoS attacks across the year, network-layer attacks rising to 34.4 million from 11.4 million in 2024, so the volume is real. The mitigation is also mature, and a test window spent here buys little.

The exhaustion tests worth a window are the ones that use legitimate connections, because no cookie, no challenge and no reputation list can distinguish them from your customers. Those are the runs where a per-IP cap, a conntrack limit or a load balancer’s session table turns out to be the number that decides whether your service stays up.

For regulated institutions the run also has to leave a record. Obsidio produces cryptographically attested, tamper-evident reports generated inside Trusted Execution Environments and mapped to FINMA, DORA and NIS2, so the outcome of a test is an artifact you can file rather than a screenshot you have to vouch for.

To scope an authorized connection-exhaustion simulation against your own infrastructure, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.

The Digital Operational Resilience Act has applied since 17 January 2025, and it is a regulation, not a directive. Regulation (EU) 2022/2554 binds financial entities in every EU member state directly, with no national transposition to wait for and no room to read it as guidance.

Most of DORA can be satisfied on paper: policies, registers, contract clauses, reporting templates. One pillar cannot. Article 24(6) requires tests to actually run, at least yearly, on every ICT system supporting a critical or important function.

That is the part of DORA compliance a binder full of policies does not cover, and it is the part this guide treats in depth.

The short version

What is the Digital Operational Resilience Act?

DORA is the EU’s response to a plain observation: a financial institution can be perfectly solvent and still be down. Capital buffers say nothing about whether the e-banking front end survives a traffic surge or whether a core provider’s outage cascades through the sector.

The regulation was adopted on 14 December 2022, and Article 64 sets the application date: it applies from 17 January 2025. Since that date, supervisors can examine any in-scope entity against the full set of DORA requirements.

Article 3(1) defines digital operational resilience around a financial entity’s ability to build, assure and review its own operational integrity. The verb “review” does the real work in that definition.

DORA does not ask you to be resilient. It asks you to keep checking that you are, and to keep records of the checking.

Who does DORA apply to?

Article 2(1) lists twenty-one categories of entity, points (a) through (u). Credit institutions, payment institutions, e-money institutions and investment firms are the obvious ones. The list continues through crypto-asset service providers, central counterparties, trading venues, fund managers, insurance and reinsurance undertakings, occupational pension institutions, credit rating agencies, crowdfunding service providers and securitisation repositories.

Article 2(2) then does something worth noticing: points (a) to (t) are collectively defined as “financial entities”, while point (u), ICT third-party service providers, sits in scope separately. Providers designated as critical come under a dedicated EU oversight regime, which means a cloud or software vendor serving the financial sector can face supervisory attention without being a financial firm at all.

Microenterprises get proportionate treatment. Several testing obligations in Chapter IV open with “financial entities, other than microenterprises”, and Article 25(3) lets microenterprises combine a risk-based approach with strategic planning of their testing instead.

Swiss institutions are not directly in scope. Swiss groups still meet DORA through their EU subsidiaries and branches, and Swiss vendors meet it through the third-party pillar the moment an EU financial entity depends on them. More on the Swiss position below.

What are the five pillars of DORA?

The DORA requirements fall into five blocks, usually called the five pillars:

Four of the five are mostly governance work. They produce documents, registers and reporting lines, and a capable compliance function can drive them.

The testing pillar is different in kind. It requires something to happen to production-relevant systems on a schedule, and it produces findings that someone must fix and then prove fixed.

What does DORA require for resilience testing?

Article 24 sets the frame. Financial entities other than microenterprises must establish, maintain and review a sound and comprehensive digital operational resilience testing programme, as an integral part of the ICT risk-management framework. The purpose is stated plainly: assess preparedness, identify weaknesses and gaps, and implement corrective measures promptly.

The article then adds four conditions that decide whether a programme survives supervisory scrutiny:

Then comes the sentence that drives most testing calendars in the EU financial sector. Article 24(6): financial entities, other than microenterprises, shall ensure, at least yearly, that appropriate tests are conducted on all ICT systems and applications supporting critical or important functions.

Not a sample. All of them, every year.

Article 25(1) says what counts as testing, and the list is broad: vulnerability assessments and scans, open source analyses, network security assessments, gap analyses, physical security reviews, questionnaires and scanning software solutions, source code reviews where feasible, scenario-based tests, compatibility testing, performance testing, end-to-end testing and penetration testing. The list is introduced with “such as”, so it is illustrative rather than exhaustive, but it makes one thing clear: DORA’s idea of testing goes well beyond an annual penetration test. Availability under load belongs in it just as much as exploitable code does.

One sharper rule sits in Article 25(2): central securities depositories and central counterparties must perform vulnerability assessments before any deployment or redeployment of applications, infrastructure components or ICT services supporting critical or important functions. For market infrastructure, testing gates the release process itself.

What is threat-led penetration testing under DORA?

Article 3(17) defines TLPT as a framework that mimics the tactics, techniques and procedures of real-life threat actors. In practice it is a supervised red-team exercise built on live threat intelligence, and DORA turns it from a voluntary maturity exercise into a legal obligation for part of the sector.

Article 26 sets the terms. Designated financial entities must carry out advanced testing by means of TLPT at least every three years, and the competent authority can adjust that frequency up or down based on the entity’s risk profile.

Supervisors decide who is in, based on impact-related factors, financial stability concerns and the entity’s ICT risk profile and maturity. TLPT is not a universal DORA requirement; it is targeted at the entities whose failure would matter most.

The test itself is deliberately uncomfortable. It must cover several or all critical or important functions and be performed on live production systems.

Where those functions run on outsourced ICT, the third-party providers are included in scope, with the financial entity keeping full responsibility; where direct participation would endanger service quality or confidentiality, pooled TLPT across several financial entities is the fallback. The regulatory standards for how these tests run were developed by the European Supervisory Authorities in accordance with TIBER-EU, the ECB-originated framework for threat-intelligence-based red teaming.

Article 27 governs who may test. Testers need demonstrated expertise in threat intelligence, penetration testing and red-team operations, certification or adherence to formal codes of conduct, independent assurance on their own risk management, and professional indemnity insurance.

Internal testers are allowed, but Article 27 requires the competent authority’s approval and an external threat intelligence provider, and Article 26(8) adds that external testers must be contracted every three tests. Significant credit institutions must use external testers exclusively.

After the test, the authority issues an attestation confirming it was performed in accordance with the requirements, which other EU supervisors then recognise. That detail says a lot about DORA’s mindset: the output of a test is a verifiable artifact that travels between regulators, not a private slide deck.

What evidence do regulators expect?

Read Articles 24 to 27 again with an auditor’s eye and a pattern appears. The programme must be documented. Findings must be classified and prioritised.

Remediation must be validated, not asserted. TLPT results come with a formal attestation precisely so that a second authority can rely on them without re-running the exercise.

So the practical question for a compliance officer is not “did we test?” but “can we prove what we tested, what we found, and what we fixed?” A screenshot of a monitoring dashboard answers none of that. It has no independent origin and no tamper protection, and it says nothing about how the test was run.

Evidence that stands up in a supervisory review is generated by the test itself, by a party with no stake in the result, in a form that cannot be quietly edited afterwards. That standard is worth applying to every vendor in your testing programme, whatever they test. Ask how their report proves the test happened as described, and what stops anyone from touching the numbers between the run and the filing.

Where does DDoS resilience testing fit into DORA compliance?

Availability is the resilience property that denial-of-service attacks target, and the exposure keeps growing. Cloudflare’s 2026 Threat Report, published in March 2026, counted 47.1 million DDoS attacks mitigated across its network in 2025, a 121 percent increase over the previous year. The same provider’s Q1 2025 DDoS threat report had already logged 20.5 million attacks blocked in a single quarter, a 358 percent increase year over year, with banking and financial services climbing its ranking of most-attacked sectors.

For a bank, the systems those attacks aim at are the ones Article 24(6) names: the e-banking portal, the payment gateway, the trading front end. Systems supporting critical or important functions, due for appropriate tests at least yearly.

Look back at the Article 25(1) list and the fit is direct. A controlled DDoS simulation against your own infrastructure is a scenario-based test, a performance test and an end-to-end test of the availability path, all at once. It answers the question none of the paper exercises can: does the defence stack you paid for actually hold when realistic attack traffic arrives?

Our guide on how to test your DDoS protection covers how to structure that first run, and our DDoS stress test explainer covers what a controlled run looks like in practice.

Obsidio runs exactly this class of test, built for the constraints regulated entities operate under. Domain ownership is verified by DNS TXT record before any traffic flows, so no simulation can ever touch infrastructure you do not control. Runs ramp up gradually and can be aborted live.

Traffic comes from 100,000+ globally distributed real devices rather than a handful of datacenter IPs, which is what makes the result meaningful: defences that key on source concentration behave very differently against one machine than against a realistic distributed pattern. Ten simulation types cover application-layer floods, slow attacks, connection exhaustion and protocol-specific vectors, so the yearly programme can rotate scenarios instead of repeating one.

The evidence side is where the DORA fit becomes concrete. Obsidio produces cryptographically attested, tamper-evident reports generated inside Trusted Execution Environments and mapped to DORA, FINMA and NIS2.

The report of a run is filing-grade: an independent, verifiable record of what was tested, when, at what intensity and with what outcome, which is precisely the shape of evidence Articles 24(5) and 24(6) assume. Retesting after remediation then closes the validation loop the regulation asks for.

Strength is proven, not promised. DORA turned that from a principle into a legal test: since 17 January 2025, the question a supervisor asks is not whether you believe your critical systems are resilient, but what evidence you can put on the table.

What about Swiss institutions?

Switzerland is not an EU member state, so DORA does not apply to Swiss entities directly. The Swiss regulator got there on its own track: FINMA’s Circular 2023/1 on operational risks and resilience for banks, in force since 1 January 2024, embeds the Basel Committee’s operational resilience principles, with transition periods for the resilience provisions running until the start of 2026. The supervisory expectation on both sides of the border now points the same way: identify critical functions, test the systems behind them, and evidence the results.

The EU’s NIS2 Directive (Directive (EU) 2022/2555) rounds out the picture. Member states had to transpose it by 17 October 2024, and it covers essential and important sectors well beyond finance. For financial entities, DORA takes precedence as the more specific regime, so a bank’s ICT risk and testing obligations run through DORA rather than NIS2, while group companies outside the financial perimeter can still be caught by NIS2 itself.

For a Swiss institution with EU operations, that is three regimes with one common denominator. A resilience test worth running should produce evidence portable across all of them, which is why Obsidio maps every attested report to FINMA, DORA and NIS2 at once rather than to a single framework.

How do you make a testing programme DORA-ready?

The sequencing follows from the articles themselves:

To put an authorized DDoS resilience test on that calendar, with evidence you can file rather than vouch for, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.

Two events can both be filed as “DDoS” and have almost nothing in common. One saturates an uplink with terabits per second of spoofed UDP packets. The other holds a few hundred sockets open from one laptop and sends a few kilobytes a minute.

The control that stops the first cannot see the second at all. That is why the taxonomy is worth getting right. Sorting attacks by the way they cause failure, rather than by name, tells you which part of your stack decides each outcome and which whole class you have never put under load.

The short version

This post is the map. Each of the ten simulation types Obsidio runs has its own detailed post covering mechanics, mitigation and how to test it, and they are listed below. For how to structure a first run across the classes, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

How are DDoS attacks classified?

The industry standard split follows the OSI layers, and it is useful as far as it goes.

Volumetric attacks work at layer 3 and 4 and try to fill a pipe. Protocol or state-exhaustion attacks also sit at layer 3 and 4 but aim at a table rather than a link. Application-layer attacks work at layer 7, where every request costs the target real computation.

Slow attacks are technically layer 7 as well, and are often filed under the application-layer heading. In operational terms that is a mistake.

An application-layer flood is loud, and every rate-based control has something to trigger on. A slow attack sends less traffic than a single real user and defeats those controls by staying below every threshold they measure. Treating them as one class is how teams end up believing a flood test covered both.

So four classes: volumetric, protocol and state exhaustion, application-layer, and slow. Obsidio’s public product page groups its ten simulation types into four platform categories that map onto this closely, splitting off DNS and scripted browser work into their own group rather than treating volumetric floods as testable.

Note that both fours are counts of groupings. The catalogue itself is ten.

Volumetric attacks: can your pipe absorb the traffic?

A volumetric attack does not care what your application does. It sends more bits per second than your uplink, your edge router or your scrubbing contract can carry, and everything behind that bottleneck becomes unreachable whether or not it is healthy. Success is measured in gigabits or terabits per second.

The main techniques in this class:

Scale in this class has moved fast. Cloudflare’s 2025 Q4 report describes a record 31.4 terabits per second attack that lasted 35 seconds, and NETSCOUT’s DDoS Threat Intelligence Report for the second half of 2025 counted more than 8 million DDoS attacks across 203 countries in that period alone.

What decides the outcome. Not your servers. Upstream capacity, your transit providers, and whether your scrubbing provider detects and diverts fast enough. Anything you configure on the host is downstream of the bottleneck and therefore irrelevant to this class.

Testability, honestly. Obsidio does not simulate spoofed-source or reflection attacks, and neither should anyone else. Forging source addresses and pointing third-party servers at your network abuses infrastructure that belongs to other people, which cannot be authorized by you and is not something we will build.

What is worth testing is the detection and diversion path: whether your provider notices, how long the divert takes, and whether anyone on your side is told. Parts of that are answerable with authorized application-layer load, and the rest belongs in a tabletop exercise with your provider.

Protocol and state-exhaustion attacks: how many connections can you hold?

This class does not try to fill your bandwidth. It fills a table.

Every established or half-established connection occupies a slot somewhere: a kernel socket, a conntrack entry, a file descriptor, a load balancer session, a TLS session cache. Those slots are finite and usually much smaller than people assume.

SYN floods sat third among network-layer vectors in Cloudflare’s 2025 Q3 report, behind UDP and DNS floods, with those four vectors together accounting for just over half of all network-layer attacks that quarter.

What decides the outcome. SYN cookies, backlog and conntrack sizing, per-source connection limits, and whether your load balancer terminates connections or passes them through. A CDN in front helps only if your origin address cannot be reached directly.

Testability. Partly. Obsidio’s TCP Connection Flood and TLS Flood test the fully-established end of this class directly and thoroughly, from real distributed devices, so per-source connection caps face the source diversity that a real attack would bring. Half-open SYN floods are a different matter: they require source-address spoofing, so they are outside what an authorized test can reproduce. The SYN flood post covers what you can verify by other means.

Application-layer attacks: how much work does one request cost you?

At layer 7 the economics change. Every request is valid, arrives over a completed TCP and TLS handshake, and asks the target to do something: run a query, render a page, check a session. A few thousand requests per second aimed at a search endpoint can be far more damaging than a hundred times that volume of packets aimed at a router.

These attacks are also harder to distinguish from customers, because structurally they are customers. Cloudflare’s 2025 Q3 report found nearly 70% of HTTP DDoS attacks came from botnets it already recognised, and around 20% came from fake or headless browsers or carried suspicious HTTP attributes. The remainder looked like nothing in particular.

What decides the outcome. Rate limiting granularity, bot management, WAF rules, and how far the damage travels once a request is accepted. Application-layer attacks reach the parts of your system a network attack never touches, which usually means the database connection pool.

Testability. Fully. Four of Obsidio’s ten simulation types live in this class, and Puppeteer Script covers the adjacent question of how a real user journey behaves while the system is under pressure.

Slow attacks: what happens when the traffic never arrives?

The fourth class inverts every assumption the other three rely on. A slow attack sends as little as possible, as slowly as possible, for as long as possible. Rate-based defenses have nothing to count, volumetric detection sees no spike, and the request rate graph stays flat while the service is already unreachable.

What decides the outcome. Timeouts with a minimum transfer rate rather than a bare deadline, per-source connection limits, and whether your reverse proxy fully buffers requests before forwarding them. This class is settled by configuration values in files, which is exactly why it drifts.

Testability. Fully, and this is the class most worth testing, because a configuration review tells you the setting exists and nothing about whether the pool holds under real concurrency.

Which types can you actually test?

Being clear about this is more useful than a longer capability list. Across the four classes:

Any vendor claiming to test the whole taxonomy is either being loose with words or doing something you should not want your name attached to.

Why does the class matter more than the name?

Attack names come and go. The classes do not, because they describe which finite resource runs out, and that set is small.

The practical consequence is coverage. A team that runs one volumetric test a year has measured a single bottleneck. Their timeouts, their connection limits and their per-endpoint rate limiting remain entirely unmeasured, and each of those is a different way to be offline.

Cloudflare’s 2025 Q3 report also noted that 71% of HTTP DDoS attacks and 89% of network-layer attacks end in under ten minutes, so the question is rarely whether you would survive a long siege. It is whether anything at all happens automatically in the first two minutes.

Class membership predicts your blind spots too. A volumetric attack is obvious in every graph you own, and settled entirely by someone else. An application-layer flood generates so much alerting that the signal disappears inside it.

Connection exhaustion shows up as a connection count that nobody has a dashboard for, and a slow attack produces no graph movement at all, often not even an access-log line. Those are four different detection failures, and fixing one does nothing for the others.

Strength is proven, not promised. Knowing the taxonomy tells you what could happen to you. Only a controlled, authorized simulation against your own infrastructure tells you what does.

Where should you start if a class has never been tested?

Pick the class where you have the least evidence, not the one that sounds most frightening. For most organisations that is the slow class, followed by established-connection exhaustion, because both are decided by settings nobody has looked at since installation.

Then run something real against it. Obsidio runs each of its ten simulation types as a controlled, authorized simulation against infrastructure you own. Domain ownership is verified by DNS TXT record before any traffic flows, runs ramp up gradually from conservative limits, and live metrics let you abort instantly.

Load comes from 100,000+ globally distributed real devices rather than a handful of datacenter instances, which is the difference between testing a per-source rate limit and testing whether it means anything against source diversity. Our post on running a DDoS stress test covers the sequencing, and the pillar guide covers scoping a first programme.

Banking and financial services ranked fourth among the most-attacked industries in Cloudflare’s 2025 Q3 report, and for regulated institutions the result of a run has to be filable rather than screenshotted. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so a test leaves an artifact an auditor can check independently.

To scope authorized simulations across the classes that apply to your stack, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.

Most organizations that own DDoS protection have never watched it handle an attack. The appliance was installed, the scrubbing contract was signed, the WAF rules were tuned once, and since then the whole stack has been waiting for its first real event. Cloudflare’s 2026 Threat Report counted 47.1 million DDoS attacks mitigated in 2025, more than double the year before, so the first real event is a matter of when.

A DDoS stress test is how you find out whether your defenses hold before an attacker does. It also happens to be a term with a split personality: the same words describe a legitimate, regulator-expected security exercise and a category of criminal service. This guide covers both meanings, because knowing where the line sits is the first thing a security team, a compliance officer, or an auditor will ask.

The short version

What is a DDoS stress test?

A DDoS stress test is a controlled simulation of a distributed denial-of-service attack, run against systems you own or operate, with documented authorization, to measure whether your defenses detect and absorb it. The traffic is real. The sources are distributed. The only difference from an actual attack is consent, control, and a report at the end.

That definition carries three load-bearing words. Controlled means the traffic ramps up on a schedule you set and stops the moment you say stop. Authorized means the target has proven, in a verifiable way, that the infrastructure is theirs to test. Measure means the point is evidence: which controls fired, which stayed blind, at what traffic level the service degraded, and whether anyone on your side noticed.

Remove any one of those three and you no longer have a stress test. Traffic without control is an outage you scheduled for yourself. Traffic without authorization is a crime, whoever launches it. And traffic without measurement teaches you nothing you can act on or show an auditor.

Is a DDoS stress test legal?

Testing infrastructure you own, with documented authorization, is legal. It is the same legal footing as penetration testing, and regulators have moved from tolerating it to expecting it.

DORA, the EU’s Digital Operational Resilience Act, requires financial entities to test ICT systems supporting critical or important functions at least yearly under Article 24, and its list of test types explicitly includes scenario-based testing of the kind a DDoS simulation delivers. FINMA expects Swiss-regulated institutions to demonstrate operational resilience with evidence. NIS2 extends similar testing expectations across EU essential and important entities.

Attacking infrastructure you do not own is a crime in essentially every jurisdiction, and the services that sell it get dismantled with some regularity. Booter and stresser sites, which offer to flood an arbitrary target for a few dollars, are the version of “DDoS stress testing” that ends in prosecution. Europol announced on 11 December 2024 that Operation PowerOFF, a multi-year international action against DDoS-for-hire services, had taken 27 of the most popular platforms offline across 15 countries, with three administrators arrested in France and Germany.

The part many people miss: enforcement does not stop at the operators. In that same December 2024 action, more than 300 users of the seized platforms were identified for follow-up, Dutch prosecutors charged four people for attacks run through booters, and the US Department of Justice charged platform administrators. Several national police forces even bought search-engine ads against booter-related keywords, so a person searching for one of these services now finds a warning where the storefront used to be.

The line the law draws runs through authorization and ownership, and nothing else. A bank commissioning a 200,000-request-per-second simulation against its own e-banking portal, with change-management sign-off and a signed scope, is doing what its regulator asked. An individual paying to send a fraction of that traffic at someone else’s server is committing an offense. Same packets, opposite legal categories.

This is also why the authorization step in a legitimate test is not paperwork theatre. A provider that will send traffic anywhere a customer points it is a booter with an invoice. A provider that refuses to send a single packet until domain ownership is cryptographically proven has made the abuse case structurally impossible, which is exactly the property you want to show an auditor.

How is a stress test different from a load test?

Teams often assume their load testing covers this ground. It does not, and the gap is worth spelling out because it is where budgets go wrong.

A load test answers a capacity question: how many well-behaved users can this system serve before response times degrade? The traffic is friendly by design. It follows the happy path, completes every request, reuses connections politely, and comes from a handful of cloud instances your firewall has probably been told to ignore.

A DDoS stress test answers a defense question: what happens when the traffic is shaped to hurt? Attack traffic does things legitimate users never do. It holds connections open without finishing requests, busts caches with randomized queries, burns CPU with handshakes it never uses, or arrives as complete, valid requests from tens of thousands of sources at once. Each of those behaviors targets a different layer, and none of them shows up in a load test.

The consequence is that the two tests fail in different places. A load test finds an undersized database pool or a slow endpoint. A stress test finds that your rate limiter keys on requests per second while the attack sends almost none, that your scrubbing provider needed twelve minutes to engage, or that your dashboards looked normal for the entire outage. Our post on the types of DDoS attacks walks through why each attack class defeats a different control, and the record for sheer scale keeps moving: the largest attack in Cloudflare’s 2026 Threat Report, a 31.4 terabit-per-second flood from the Aisuru botnet in late 2025, was nearly six times the biggest attack of 2024.

One more difference matters for realism. Load generators run from a few datacenter IP ranges, which per-IP limits and reputation filters catch trivially. Real attacks come from botnets of tens of thousands of residential and mobile devices. A test that comes from three cloud instances validates your defenses against three cloud instances, and nothing else.

This is the false-pass problem, and it is the single most common way a cheap check produces a dangerous conclusion. Our pillar guide on how to test your DDoS protection covers it in detail.

How does an authorized DDoS stress test actually run?

A legitimate test has a recognizable shape. If you are evaluating providers, or building an internal case for a first run, these are the steps that should exist. Where one is missing, that is a finding about the provider.

Scoping before anything else

You define which domains and services are in scope, which simulation types will run, what traffic levels the run will reach, the time window, and who on your side is watching. You also define the abort criteria in advance: at what point does the run stop even if nobody presses the button. Regulated institutions typically route this through change management, and that paper trail is what makes the exercise auditable later.

Ownership verification, cryptographic rather than contractual

Before any traffic flows, you prove the target is yours. On the Obsidio platform this is a DNS TXT record: you place a verification token in the DNS zone of the domain under test, which only someone with administrative control of that domain can do. No token, no traffic. A signature on a contract asserts ownership; a DNS record demonstrates it. This single step is what separates an authorization-first platform from a booter, because it makes pointing the traffic at a third party impossible rather than merely forbidden.

Gradual ramp, live metrics, instant abort

A proper run does not open at full scale. It starts in a controlled window with conservative limits and ramps up as confidence grows, while you watch real-time metrics: requests per second, success rate, data transferred, and how your edge responds. Any run can be aborted live, at any second. That combination, ramp plus abort, is what turns a simulated attack into a controlled experiment. You find the level at which degradation begins without being forced to ride through it.

A report you can hand to an auditor

The run ends with evidence, not a screenshot. Obsidio generates cryptographically attested, tamper-evident reports inside Trusted Execution Environments, mapped to FINMA, DORA, and NIS2 expectations. The distinction matters more than it first appears: an attested report is an artifact a third party can verify, while an internal test log is a claim your own team has to vouch for. Auditors know the difference.

What should a stress test actually cover?

One test type is not coverage. Attacks fail in genuinely different places, and a defense tuned for one class can be structurally blind to another. A volumetric flood exercises your bandwidth and scrubbing; a slow attack like Slowloris sends almost no traffic at all and walks past every rate-based control; a TLS flood burns CPU on handshakes; a flood of real browser sessions defeats bot detection because there is no bot to detect.

Obsidio runs ten simulation types across four categories: application-layer floods, slow attacks, connection exhaustion, and protocol-specific or scripted traffic. A sensible first programme does not run all ten at once. It starts with the classes most relevant to your architecture, then expands. The attack-type overview explains which class stresses which layer, and the product page lists the full catalog.

Coverage also has a second axis that teams forget: detection. Every run should answer not only “did the defenses hold?” but “did anyone find out?” Whether your dashboards moved, whether your SIEM logged anything, and whether on-call was paged are answers that live on your own consoles, so someone on your side needs to be watching during the window. A control that fails silently and a control that works invisibly are both findings.

How often should you stress test?

A single test is a snapshot, and infrastructure does not stand still. The CDN contract gets renegotiated, an engineer relaxes a timeout to fix a customer complaint, a new API ships outside the WAF policy. Any of those can quietly undo a result that was true six months ago.

For regulated financial entities the floor is set externally: DORA expects ICT systems supporting critical or important functions to be tested at least yearly, and our guide to DORA compliance covers what evidence that testing has to produce. Treat that as a minimum rather than a target.

Beyond the regulatory floor, two triggers matter more than the calendar. Retest after you fix something, because a mitigation that was configured under pressure and never exercised is exactly the kind of control that fails twice. And retest after material changes to the edge: a new CDN, a migration, a new public-facing service. The cadence the platform is built around is a loop, “Configure. Test. Adapt. Retest.”, precisely because the second run is where you learn whether the first one changed anything.

What does a stress test find that a config review cannot?

Configuration reviews verify that settings exist. They cannot verify that settings hold under concurrency, that vendor claims survive contact with realistic traffic, or that the interaction between your CDN, your load balancer, and your origin behaves the way the diagram says. Those properties only exist under real traffic.

The typical findings from a first authorized test are rarely “the protection is missing.” They are quieter and more expensive than that.

The scrubbing service engaged, but minutes after degradation began. The rate limiter worked, and also throttled the payment callback from a partner. Failover worked, and the failover environment had a quarter of production capacity. Auto-scaling scaled, and the invoice arrived. None of those appears in a config review, and every one of them changes what you do next.

Strength is proven, not promised. Buying DDoS protection settles what you spent. A stress test settles what you can survive.

There is also the organizational finding. An attack at 3 a.m. is a people event as much as a technical one, and the first authorized run is usually the first time the escalation path gets exercised end to end. Finding out that the runbook names a person who left last year is cheap during a scheduled window. It is very expensive during a real attack.

Where to start

Start small and legitimate. Pick one service, get sign-off, verify ownership, and run one attack class at conservative scale during a window your team is watching. The first run is less about the traffic level than about proving the loop works: authorize, configure, simulate, report. Every later run gets faster and more ambitious.

Obsidio runs DDoS stress tests as controlled, authorized simulations from 100,000+ globally distributed real devices, so per-IP limits and reputation filters get tested the way an actual distributed attack would test them. Domain ownership is verified by DNS TXT record before a single request is sent, runs ramp up gradually, any run can be aborted live, and the result is a cryptographically attested report mapped to FINMA, DORA, and NIS2.

To scope an authorized stress test against your own infrastructure, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.

In November 2025, Cloudflare absorbed the largest DDoS attack ever publicly disclosed: 31.4 terabits per second, sustained for 35 seconds. The traffic did not come from a data center. It came from a botnet, an estimated one to four million hijacked consumer devices, most of them cheap Android TV boxes whose owners had no idea they were participating.

That is the defining property of a botnet, and the reason it is the delivery mechanism behind almost every large DDoS attack: the attacker never owns the hardware. You do. Or your customers do.

The short version

What is a botnet?

A botnet is a network of internet-connected devices that have been infected with malware and placed under the remote control of a single operator, the “bot herder”. Each infected device is a “bot” (or “zombie”). The owners almost never know: the device keeps working, and the malware runs quietly in the background.

Three components make the system work:

How does a device become a bot?

Not through anything the owner did. Botnet malware finds devices on its own, by scanning the entire internet for three kinds of weakness:

Every new bot immediately starts scanning for further victims, which is why botnet growth is exponential. Mirai infected nearly 65,000 devices in its first 20 hours, doubling in size roughly every 76 minutes early on.

What are botnets used for?

The combined resources of a million devices are the product: bandwidth, IP addresses, compute. The operator sells or uses them for:

Why is botnet traffic so hard to filter?

Because each request, taken alone, looks legitimate. This is the property that breaks naive DDoS defenses:

The uncomfortable conclusion for defenders: a filter tuned to catch “obviously fake” traffic will pass most of what a modern botnet sends.

From Mirai to AISURU: how big do botnets get?

Three data points, each a public record in its day, show the trajectory:

And the pace is accelerating. The public record fell four times in the thirteen months to November 2025 alone:

Line chart: record botnet DDoS attacks over time, from 5.6 Tbps in October 2024 to 31.4 Tbps in November 2025

The growth is driven by supply: every year adds billions of cheap, unpatched, internet-connected devices, and every one of them is a potential bot.

What do botnets mean for regulated institutions?

For a bank, insurer or financial market infrastructure, the botnet threat translates into a concrete regulatory expectation: prove that your services stay available under exactly this kind of load.

None of these frameworks accepts “we have a DDoS protection contract” as evidence. They ask whether the defense works. The only way to know is to subject it to realistic pressure.

How do you test against botnet-scale traffic, without a botnet?

This is the problem Obsidio was built to solve. A realistic test has to reproduce the properties that make botnet traffic dangerous (real devices, residential networks, global distribution, genuine browser behavior) without ever touching a hijacked machine.

Obsidio runs DDoS simulations from 100,000+ real, ethically sourced devices distributed worldwide: hardware whose owners deliberately contribute capacity, orchestrated inside Trusted Execution Environments with full audit logging. The traffic is indistinguishable from a genuine botnet attack, because it has the same shape. But every simulation requires explicit ownership verification of the target, runs under your control with progressive ramp-up, and can be aborted instantly from live metrics.

In other words: the realism of a botnet, with the authorization, control and evidence a compliance team needs. Every run produces a cryptographically attested report that maps to FINMA, DORA and NIS2 expectations.

If you want to know how your defenses behave when a million polite-looking requests arrive at once, start with our guide on how to test your DDoS protection, or talk to our team about a controlled simulation against your own infrastructure.

The other nine simulation types in this series answer one question: does your edge hold when hostile traffic arrives. Worth knowing, and not the only question. There is a second one no flood can settle. Does your login still work when a thousand people sign in at once, and does the session store cope.

Puppeteer Script is the entry in Obsidio’s catalogue built for that question. It is not a flood and not measured in requests per second. It takes a script you wrote, runs it against a real browser page on every worker at once, and reports whether each run finished. Synthetic user testing under genuine concurrency, reaching parts of a banking stack raw traffic never gets near.

The short version

This is one entry in a series covering each of the ten simulation types Obsidio runs, one post per type. Nine are adversarial. This one is not, which is why it belongs in the set: it covers what the other nine leave unmeasured. For how the categories fit together, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them. The last entry is functional load testing rather than an attack.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

  • DNS Floodcoming soon
  • Puppeteer Scriptyou are here

What Puppeteer Script testing actually does

Each worker starts a single Chromium tab and attaches Puppeteer to it. Puppeteer’s documentation describes it plainly: connect() “attaches Puppeteer to an existing browser instance”. The worker gets back a page object, which the docs define as providing “methods to interact with a single tab or extension background page in the browser”. Navigation, clicking, typing, evaluating JavaScript in the page context: all available, because this is a real browser, not a request generator wearing a browser’s user agent.

Your script runs as the body of an async function, once per iteration, in a loop, until the run duration expires. Every iteration is a separate attempt with its own timeout, and an overrunning iteration gets killed and recorded as a failure with the reason attached. A syntax error throws once at startup and fails the whole run.

There is no traffic pattern to describe here, which is what separates this from every other type in the catalogue. A flood has a shape; this has whatever shape your journey has. A payment initiation might be a dozen calls, two of them to systems you do not own.

The plain version: you write a small browser bot that does one job, log in, fill a form, click a button, and the platform clones it onto every worker and runs it on repeat for the duration of the simulation.

What synthetic user testing is good for

Floods answer questions about capacity and filtering. Scripted journeys answer questions about correctness under load, a separate property that fails separately. Grafana’s k6 project, which offers browser-level load testing alongside the protocol-level kind, puts the same point plainly: browser-level testing “provides a way to measure user experience and find issues that are difficult to catch on the protocol level”. Four things are worth testing this way.

Business flow verification under concurrency. The question is not whether the page returned a response but whether the transfer was created and the confirmation showed the right amount. Assertions inside the script record that per iteration, so the result is a pass rate on the flow, not a latency chart.

Authentication and session behaviour. Login is where banks accumulate the most fragile machinery: token issuance, session storage, step-up verification, limits meant to stop credential stuffing. Under a thousand concurrent sign-ins those interact in ways nobody designed, and a protocol-level test replaying a captured token exercises none of it.

The parts of the stack a flood never touches. A slow attack such as Slowloris stops at the web server and never reaches your application. A scripted journey writes to your database, occupies your session store, and calls whatever downstream service the flow depends on.

Your own protective controls, seen from the customer’s side. If bot detection challenges the journey, or your WAF starts blocking a form submission at volume, you have learned how your controls treat genuine automated traffic, which every bank carries in its partner integrations.

Which parts of your stack does this exercise

For the attack simulations, the useful question is which control should have caught it. Here it inverts. Nothing is trying to get past anything, so the question becomes coverage: which layers this test genuinely reaches, and which it leaves alone.

Zone 1

What sits in front, and why it barely matters

  • DDoS protection and scrubbing. Not in play. The traffic volume is ordinary and the test is not trying to look hostile.
  • Firewall and WAF. Sees complete, well-formed requests from your own journey. If it starts blocking form submissions at volume, that is a finding about your customers, not an intruder.
  • Rate limiting. Relevant only if your journey trips a per-account or per-endpoint limit, which some login flows do.
  • Bot detection and challenges. A real browser under automation is the ambiguous case. Only a run against your own configuration shows whether it gets challenged.
  • CDN cache. Absorbs the static parts of the page, quietly reducing how much of the journey reaches your origin.

Zone 2

What the test actually reaches

  • Load balancer or reverse proxy. Carries the traffic without being under pressure.
  • Web server. Serves the pages and their sub-resources at moderate volume.
  • Application tier. The target. Authentication, form handling, business logic, whatever your flow calls downstream.
  • Database and session store. Genuinely exercised, writes included. This and Browser Flood are the only two of the ten that get here, because they run your real pages and journeys.
  • Auto-scaling. Scales on whatever metric you configured. Whether your journey moves one of those metrics is a per-stack answer.

Zone 3

Would you find out what broke?

  • Dashboards. Request rate stays unremarkable throughout. Whether failed logins or slowing transfers show up depends on what you chose to graph.
  • Per-iteration results. What carries this test: iteration success rate and iteration duration, not throughput.
  • Logging and your SIEM. The journey produces ordinary log lines at unusual volume. Check whether anything correlates them into a signal.
  • Infrastructure alerting. Usually silent. A flow returning the wrong answer crosses no CPU or bandwidth threshold.
  • Fraud and security monitoring. Hundreds of simultaneous sign-ins from many countries should register somewhere. If nothing does, that is a finding of its own.

Purple marks what only a run on your own infrastructure can settle. Everything else follows from how the test works.

The parameters, and how this differs from Browser Flood

Four things are configurable, and one does most of the work.

Two constraints matter. One browser per worker, with no parallelism inside a worker: iterations run back to back, so concurrency is the worker count alone. And it requires the browser runtime on the worker device, with no HTTP fallback. Without the runtime it throws immediately instead of quietly degrading into something less realistic, which is what you want from a test whose whole value is the real browser.

Browser Flood is the sibling that confuses people, since both drive real Chromium. The difference is what they load. Browser Flood loads your page repeatedly and counts every sub-resource the engine fetches, so the load takes the shape of the HTML, CSS, JavaScript, images and background calls your page pulls. Puppeteer Script ignores the sub-resource graph and follows the journey instead: navigate, type, submit, check the result. One produces realistic volume, the other realistic behaviour.

What you can check yourself, and where the gap is

The honest answer here differs from the other nine posts. Your team can script these journeys. If you already do web application load testing with the mainstream tools, or keep end-to-end browser suites in your pipeline, you have the skill and most of the code. The gap sits in two other places.

Concurrency at a realistic scale. Every browser session costs real memory and CPU on the machine driving it. The k6 documentation is direct about the consequence, noting “an additional performance overhead when it comes to spinning up a browser VU” and offering as a less resource-intensive alternative “combining a small number of virtual users for a browser test with a large number of virtual users for a protocol-level test”. That compromise is sensible advice for a CI budget. It also leaves your original question unanswered: what your session store does at a thousand concurrent real journeys.

Geographic and network distribution. A hundred browser sessions from one cloud region share a handful of addresses, one network path and one latency profile. A busy Monday morning looks nothing like that, and that setup cannot tell you how your geo-based rules, bot scoring or fraud monitoring behave when the same flow arrives from thirty countries at once.

So run your own scripted journeys against staging. Just do not read a clean result at low concurrency from one location as evidence the flow holds at scale.

How to run this against production safely

Authorization comes first, and it is not a checkbox. Obsidio verifies domain ownership through a DNS TXT record before any traffic flows, runs ramp up gradually rather than starting at full worker count, and any run can be aborted live. A scripted journey adds one piece of homework: your script creates real records, so decide in advance which account it uses, what it writes and who clears up afterwards. A journey submitting a thousand transfer requests needs the same change-management sign-off as any other production activity.

Where the workers come from is what closes the gap above. Obsidio runs simulations across 100,000+ globally distributed real devices, so a scripted journey arrives from many countries and networks at once rather than from one rented region.

Then watch the right numbers, because two habits carry over badly from flood testing.

The reporting split is the same as everywhere else. Obsidio measures what is visible from outside: whether each iteration completed, how long it took, which assertions passed, how your edge controls responded. Whether your database connection pool saturated, whether your SIEM correlated anything and whether anyone was paged are answers only your consoles hold. Have someone watching during the window, or the run produces half a finding.

Strength is proven, not promised. A flood asks whether your defences hold. A scripted journey asks whether your bank still works while they do.

Where this fits in a testing programme

A programme built only from attack simulations has a large blind spot. Each of the other nine types tells you something about capacity, filtering or connection handling. None tells you whether the transfer completed. For a regulated institution, resilience means customers could still do what they came for, and verifying that needs a test that does the thing.

The pairing that makes it concrete: run a flood against the front of the stack and a scripted journey alongside it, then see whether the journey still completes while the flood lands. That is as close as a controlled simulation gets to the question a board asks after an incident, and it is the pairing the k6 documentation recommends too, a small browser workload next to a large protocol-level one rather than a choice between them.

For regulated institutions the record matters as much as the result. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so an authorized functional load run leaves an artifact you can file rather than a screenshot somebody has to vouch for.

To scope an authorized synthetic user test against your own infrastructure, see the Obsidio platform or get in touch. Independent, neutral, verifiable: resilience you can prove.

A TCP connection flood never sends any data. It opens connections, completes the handshake, then says nothing for as long as the socket is allowed to live. There is no request to inspect, no payload to filter and no signature to match, because nothing above the transport layer ever happens.

That makes it awkward to defend against and easy to miss. Bandwidth stays flat and requests per second stay flat. What fills up is a set of counters most teams have never put on a dashboard: connection-table entries, file descriptors, connection-tracking slots and the queue of connections waiting to be accepted. The service stops answering while every graph anyone is watching reads normal.

The short version

This is one post in a series covering each of the ten simulation types Obsidio runs. They fail in genuinely different places, which is why a test programme needs coverage across the categories rather than volume in one of them. For how they fit together, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

What a TCP connection flood actually does

Each parallel slot opens one raw TCP connection, lets the handshake complete, and holds the socket idle until a configured timeout expires. Then it closes and opens a fresh one. There is no HTTP layer and no TLS layer above it, and no application data is written in either direction. A TCP flood attack of this kind is about occupancy rather than packet volume, and the connections it holds are fully established rather than half-open.

What that consumes is a set of finite slots. Every established connection occupies an entry in the server’s connection table, an open file descriptor in the process holding it, a connection-tracking entry in every stateful device on the path, and a share of whatever per-source socket limit applies. Connections arriving while the accept queue is backed up wait in it, and once it is full they are refused. None of those are CPU or bandwidth, which is why the attack is so quiet.

Twenty people each phone the shop and say nothing. Every line is occupied, the staff are sitting there ready to help, and real customers get a busy signal.

A socket that has connected and not yet spoken is not suspicious on its own. A mobile client on a bad connection looks the same. That is why servers do not simply hang up, and why the defense has to be a limit rather than a rule.

Which of your controls should have caught this

It is worth walking the whole chain a connection passes through, because the answer here is narrower than most teams expect. What sits in front, what absorbs whatever gets through, and whether anything tells you it happened.

Zone 1

Should any of these have stopped it?

  • Web application firewall. Inspects HTTP requests. No request is ever made, so it has nothing to evaluate.
  • Network firewall. The one control at the right layer. Whether it caps connections per source, and whether its own tracking table has headroom, is a question about your configuration.
  • Volumetric DDoS protection. Watches bandwidth and packet rate. An idle socket generates neither.
  • Rate limiting and bot detection. One counts requests, the other profiles client behaviour. There is neither.
  • A proxy or scrubbing layer that terminates TCP for you. Real protection where it exists, provided your origin cannot be reached directly.

Zone 2

What takes the hit?

  • Load balancer or reverse proxy. If it terminates connections, its own limits decide the outcome. Confirm which component holds the socket.
  • Connection tracking and the accept queue. One entry per socket, and new arrivals queue behind everything already held.
  • The listening service. Where it bites. The per-worker connection ceiling and the open-file limit are hard walls, and the lower one is what you hit.
  • Application and database. Never reached. No request is made, so no handler runs and no query is issued.
  • Auto-scaling. CPU, bandwidth and request rate all look idle, so nothing scales. More nodes would not help a shared stateful device anyway.

Zone 3

Would you find out?

  • Bandwidth and request-rate dashboards. Flat throughout. Both are measuring the wrong thing.
  • A concurrent-connections or tracking-table graph. The one internal metric that shows this clearly. Whether it exists, and whether anyone watches it, is a real question.
  • A check from outside your network. Catches it reliably, because new connections stop being accepted. Only useful if you run one.
  • Access logs and your SIEM. No request means no access-log line. Anything recorded lands in an error log, under a resource-limit message rather than an attack.
  • Alerting and on-call. Nothing crosses a threshold. The service is refusing connections and the room is quiet.

Purple marks what only a test on your own infrastructure can settle. Everything else follows from how the attack works, not from how well your team configured things.

The firewall and the WAF are worth separating properly, because this is the attack where treating them as one box gets people badly wrong. A web application firewall reads HTTP: request lines, headers, query strings, bodies. A connection that completes the handshake and then stays silent gives it none of that, and a client which has not spoken yet is not an anomaly a rule can be written against. The WAF is not misconfigured. The traffic is below the layer it works at.

A network firewall works with addresses, ports and connection state, which is what this attack manipulates, so it can count how many established connections one source holds and refuse the next. In netfilter that is the connlimit match, where --connlimit-above sets the threshold and --connlimit-mask groups sources by prefix length; nftables has ct count. It pays for being in the game, though: every connection it tracks costs an entry in its own table, so the control best placed to see the attack is also a resource the attack consumes.

The parameters, and what this is not

Four settings, each mapping to a resource on your side:

A TLS flood also abandons connections, but it makes the server complete a full handshake first, including key exchange, certificate and session setup, and that asymmetric crypto costs the server roughly fifteen times what it costs the client. The target is CPU. A TCP connection flood spends nothing on crypto at either end, and its cost to the target is one table entry and one descriptor.

Slowloris is different again. It speaks HTTP, sending a partial request and then trickling, which is why header-read timeouts and a buffering proxy defeat it. A TCP connection flood sends no HTTP at all, so anything reasoning about request content is out of scope here, not merely weak.

How to defend against a TCP connection flood

Everything useful here is a number, and every number can be stale, wrong, or lower than you assume. These are the ones to check, with documented defaults where they exist:

A listener with a 60-second header timeout will close these sockets, since the simulation holds them for ten minutes by default. That moves the interesting question rather than answering it. What happens in front of the listener, in the firewall’s table, the load balancer and the accept queue, is where the run gets decided.

What you can check yourself, and what it will get wrong

Two of these you can do this afternoon. The third is the one that misleads.

Read the numbers and do the arithmetic. Pull the connection-tracking maximum, the descriptor limits, the per-worker connection ceiling, the listen backlog and somaxconn. The smallest is your real capacity. This is free, takes an hour, and often surprises people. It tells you where the ceiling is, not what happens when you reach it.

Test alert routing on its own. Fire a synthetic alert and see whether it reaches the right rotation and whether anybody acts on it. That needs no attack traffic.

Open a lot of connections from one machine against staging infrastructure you own, with change-management sign-off. Legitimate, and this is where the trap is.

The arithmetic is worth spelling out. Say your per-source cap is 100 concurrent connections. One machine tries to open five thousand and is stopped at 100. Refusals appear in the firewall counters, the connection count barely moves, the service stays healthy, and the test is written up as a pass. The result is accurate. The conclusion drawn from it is wrong, because the control that just proved itself is close to the least relevant one for the scenario you are afraid of.

Now distribute it. Ten thousand sources each hold twenty connections. Every source sits far below the cap, so not one breaks a rule, and your table is carrying two hundred thousand established connections. Same firewall, same configuration, opposite outcome. A per-source cap does not reduce the total capacity that can be consumed; it limits any one source’s share. Against enough sources the binding constraint moves to your tracking table and descriptor ceiling, the numbers the single-source test never came close to because the cap stopped it at 100 first. Prefix masking has the same problem: grouping by network catches a rented block at one hosting provider and does little against residential addresses spread across thousands of unrelated networks.

Once the cap looks like the defense, nobody raises the underlying limits and nobody graphs the tracking table, so the finding that mattered is never generated. And no configuration review tells you how the stack behaves at its ceiling. Some services refuse new connections cleanly, some stall, some fail their health check and get pulled from rotation while still running.

How Obsidio tests a TCP connection flood

Obsidio runs this as a controlled, authorized simulation against infrastructure you own. Domain ownership is verified by DNS TXT record before any traffic flows, runs ramp up gradually rather than starting at full load, and any run can be aborted live. You choose the sockets per worker, the idle timeout, the think time and the port.

Load comes from 100,000+ globally distributed real devices rather than a handful of datacenter instances. That is the part a self-test cannot reproduce: a per-source cap gets exercised the way a distributed flood would exercise it, across many networks, each source individually unremarkable.

Be clear about the split before you start. Obsidio measures how many connections were held, the peak, the open rate and whether the service stayed reachable from outside, because that is visible externally. Whether your descriptor ceiling or tracking table actually saturated, what your error log recorded and whether anyone was paged live only on your own consoles. Have someone watching them during the window.

Strength is proven, not promised. A TCP connection flood does not test your bandwidth or your rules. It tests whether the limits your stack was sized with are still the limits it is running.

Where this fits in a testing programme

TCP Connection Flood belongs in the connection-exhaustion category alongside TLS Flood. Both aim at the connection rather than the request: one buys CPU time with the server’s own cryptography, the other buys table space with silence. A stack that absorbs one can fail the other, which is why the category needs both.

Across the ten simulation types the failure point moves considerably. An application-layer flood gives your front defenses something to act on, reaches as far as your database connection pool, and turns the detection problem from silence into noise. This one stops at the transport layer, is invisible to your WAF, and is decided by five or six integers in configuration files.

For regulated institutions the run also has to leave a record. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so the outcome is an artifact you can file rather than a screenshot you vouch for.

To scope an authorized TCP connection flood simulation against your own infrastructure, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.

A TLS flood never asks your server for anything. It opens a connection, lets the server complete the full cryptographic handshake, then destroys the socket. No request line, no Host header, no path. Nothing your web application firewall can read, because a firewall that inspects requests needs a request to exist.

That is what makes this one awkward. The traffic is well-formed. Every connection is a legitimate TLS negotiation your server chose to complete. The cost is real and it lands on your side, because the arithmetic of public-key cryptography is not symmetrical. The client spends very little to ask. The server spends a great deal to answer.

The short version

This is one of a series covering each of the ten simulation types Obsidio runs, one post per attack. They fail in genuinely different places, which is why a test programme needs coverage across the categories rather than volume in one. For how they fit together, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

What a TLS flood actually does

The client opens a TLS connection and lets the server run the negotiation to the end: key exchange, certificate, session setup. The moment the secure connection is established, a very short idle timeout expires and the socket is destroyed. Then the slot opens another. The server has done the expensive part and learned nothing about what the client wanted, because the client never said.

The expense is worth naming precisely. In a TLS 1.3 full handshake the server sends its key share, its certificate, and a CertificateVerify message that RFC 8446 describes as “a signature over the entire handshake using the private key”. TLS 1.3 also removed the static RSA and static Diffie-Hellman cipher suites, so every full handshake now includes an ephemeral key exchange. Private-key signing plus ephemeral key agreement, once per connection, before the server has any idea whether the connection is worth serving.

The client’s half is cheap. It does its side of the key exchange, signs nothing, and throws the session away microseconds later without validating the certificate. Obsidio’s own measurements put the server’s CPU cost at around fifteen times the client’s. That figure is our instrumentation, not a published standard, and the multiple moves with your key type, cipher selection and whether offload hardware sits in the path. The direction never moves.

Twenty people walk up to a high-security door. The guard runs the full identity check on each one, every document, every signature. Each then turns around and leaves. The guard burns out on visitors who never enter, and there is no visitor record afterwards because nobody was admitted.

The naming is historical. Obsidio’s internal identifier is ssl-flood and the attack is still widely discussed as an SSL flood, but the implementation uses modern TLS with Server Name Indication set to the target host.

Which of your controls should have caught this

It is worth walking the whole chain a connection passes through, because the answer is rarely the control people expect. Three groups matter: what sits in front, what absorbs whatever gets through, and whether anything tells you it happened.

Zone 1

Should any of these have stopped it?

  • DDoS protection and scrubbing. Bandwidth stays low, so a volume threshold is the wrong trigger. Ask your provider whether it watches handshake rate per source.
  • Firewall and WAF. A WAF sees literally nothing. AWS describes its own as monitoring “the HTTP and HTTPS requests that are forwarded to your protected web application resources”. No request is ever forwarded.
  • Rate limiting. Usually keyed on requests. nginx documents that a connection is counted “only if it has a request being processed by the server and the whole request header has already been read”.
  • Bot detection and challenges. A challenge is delivered in an HTTP response. Nothing gets that far.
  • CDN. Potentially decisive, for one reason: it terminates TLS. Caching is irrelevant. Whether your origin still answers on its own IP settles it.

Zone 2

What takes the hit?

  • Load balancer or reverse proxy. Absorbs the whole attack if it terminates TLS itself. Worth confirming yours does and that nothing bypasses it.
  • Connection layer. Churns rather than fills. Sockets appear and vanish, so the table never looks alarming.
  • TLS stack and CPU. This is where it bites. Asymmetric crypto per handshake, at whatever rate the fleet can sustain.
  • Application and database. Never reached, because no request is ever routed to them.
  • Auto-scaling. Depends on your scaling metric. Scale on CPU and it may respond. Scale on request rate and it never wakes.

Zone 3

Would you find out?

  • Dashboards. The request-rate graph reads flat. The CPU graph on your TLS terminators does not. Whether anyone watches the second is the finding.
  • Access logs and your SIEM. Access logs are written per request. There are no requests, so little to correlate afterwards.
  • A check from outside your network. The one thing that reliably catches this, if you run one.
  • Alerting. Depends on whether a CPU threshold exists and where it sits.
  • On-call. Follows from the above. Nothing crosses a threshold, nobody is paged.

Purple marks what only a test on your own infrastructure can settle. The rest follows from how the attack works, not from how well your team configured things.

Zone 3 is unusual here. The signal exists, on a CPU graph most teams already have. The question is whether anyone ever wired it to an alert.

What you control, and how this differs from a TCP connection flood

Obsidio exposes four parameters, and two of them change the character of the run:

The two attacks sit in the same category and behave nothing alike. A TCP connection flood does no negotiation beyond the TCP handshake, sends no payload, and holds each idle socket for minutes. Its target is your connection table: file descriptors, connection-tracking entries, the accept queue. A TLS flood is the mirror image. Its sockets live for milliseconds and its target is your processor. Generous connection limits with a modest CPU fail the TLS flood and shrug off the connection flood, and a fast CPU behind a tight file-descriptor limit does the reverse. Running one and calling the category covered is how that gets found late.

How to defend against a TLS flood

Where TLS terminates decides almost everything, so start there.

What you can check yourself, and what it will get wrong

Several of these you can answer today without help. It is worth knowing where a cheap check misleads you.

Read the configuration. Establish where TLS terminates, whether your origin answers directly on its own IP, and whether your per-source connection limit sits at the HTTP layer or below it. This takes an afternoon and it tells you the design is sound. It does not tell you the processor holds.

Test alert routing on its own. Fire a synthetic CPU alert and see whether it reaches the right rotation and whether anyone acts. That verifies the plumbing and needs no attack traffic.

Run a single-source test against a staging environment, with change-management sign-off, on infrastructure you own. This is legitimate and useful. It is also where the trap sits.

A test from one machine does not merely fail to answer the Zone 1 questions. It answers several of them wrongly, in a reassuring direction. Your per-source connection limit catches one address immediately. Reputation filtering handles a datacenter IP without breaking stride. The asymmetry works against you too: one client’s processor caps how many handshakes it can initiate, so the peak you reach says more about your test machine than your defenses. Every control in Zone 1 appears to hold, and you conclude you are covered.

Then real traffic arrives from thousands of residential addresses across dozens of countries, no single source doing anything remarkable, and those same controls behave completely differently, because they key on source concentration and traffic realism. One machine reproduces neither. The cheap check produces a false pass on the controls you most need to trust, and a synthetic alert cannot tell you whether anyone would have read a rising processor curve as an attack rather than a bad deploy.

How Obsidio tests a TLS flood

Authorization comes first. Domain ownership is verified by DNS TXT record before any traffic flows, runs ramp up gradually, and any run can be aborted live. You choose the concurrency, the idle timeout and the pacing. Load comes from 100,000+ globally distributed real devices rather than a handful of datacenter instances, so a per-source cap gets tested the way distributed traffic would test it.

Two things to watch:

The split is worth stating plainly. Obsidio reports how many handshakes completed, how many failed, how your edge controls responded and what the connection counts did, because all of that is visible from outside. Whether the processors on your TLS terminators saturated, whether your SIEM recorded anything and whether anyone was paged live only on your own consoles. Have someone watching them during the window, or those questions go home unanswered.

Strength is proven, not promised. A TLS flood does not ask your server for anything. It asks it to prove who it is, thousands of times over, and then walks away without a word.

Where this fits in a testing programme

TLS Flood belongs in the connection-exhaustion category alongside TCP Connection Flood. Both bypass the application entirely, and between them they separate two failure modes teams habitually conflate. One exhausts the crypto path, the other the connection table. Testing only the second leaves your handshake capacity an open question, and no application-layer flood test closes it, because an HTTP flood amortises handshake cost across reused connections.

The ten simulation types fail in genuinely different places. Slow attacks like Slowloris starve your connection pool by never finishing a request. A TLS flood finishes everything it starts and leaves. Coverage across the categories is the point, not volume in any one of them.

For regulated institutions the test also has to leave a record. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so the result of a run is an artifact you can file rather than a screenshot you have to vouch for.

To scope an authorized TLS flood simulation against your own infrastructure, see the Obsidio platform or get in touch. Independent, neutral, verifiable: resilience you can prove.

A RUDY attack does not look like an attack. The request line is valid, the header block is finished, and the Content-Length tells the server exactly how many bytes are coming. That number is true. The only odd thing is how slowly the body arrives: one byte at a time, with a long pause between each one.

That is what makes it harder to catch than Slowloris. Slowloris sends a request it never finishes, so anything waiting for a finished request notices something is missing. RUDY finishes the part your controls inspect and starves the part they do not. Your WAF finds nothing wrong, because on the evidence available to it, nothing is wrong.

The short version

This is one post in a series covering each of the ten simulation types Obsidio runs. They fail in genuinely different places, which is why a test programme needs coverage across the categories rather than volume in one of them. For how they fit together, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

What a RUDY attack actually does

RUDY, short for R-U-Dead-Yet, opens parallel HTTP POST connections and gives each one a complete request head: request line, Host, a content type, and a Content-Length stating the exact size of the body to follow. Then it delivers that body one byte at a time.

The server does the only reasonable thing. It knows how many bytes are coming, it has received some, and the client is plainly still there, so it keeps the request handler assigned and waits. That handler, a thread or a worker slot, serves nobody else meanwhile. Multiply by a few hundred connections and the pool is gone. Real users get a timeout, not an error page.

Think of twenty people filling out a long form at the counter, writing one letter every ten seconds. The clerk has to wait. They cannot move on until the form is done, and it is being done, just not this year.

The arithmetic explains why nothing recycles. The default declared Content-Length is 100,000 bytes and the default gap between bytes is 10 seconds, so the body needs 1,000,000 seconds to complete, about 11.6 days. No socket in a realistic run gets there. Each slot holds its socket until the server tears it down or the run ends, so at 20 sockets per worker the wedged-request count is roughly 20 multiplied by the worker count, and it stays there.

Which of your controls should have caught this

It is worth walking the whole chain a request passes through, because the answer is rarely the control people expect. Three groups matter: what sits in front, what absorbs whatever gets through, and whether anything tells you it happened.

Zone 1

Should any of these have stopped it?

  • DDoS protection and scrubbing. Waits for a traffic spike. A byte every ten seconds is not one.
  • Firewall and WAF. The hard part. Headers are complete and the declared length is honest, so inspection sees a valid POST and passes it. It helps only if it buffers the whole body before forwarding, and plenty of deployments stream instead.
  • Rate limiting. Counts requests. Each source sends a handful, then goes quiet.
  • Bot detection and challenges. No page is ever loaded, so there is nothing to challenge.
  • CDN cache. A POST is not cacheable, so the cache is not in the path.

Zone 2

What takes the hit?

  • Load balancer or reverse proxy. Absorbs the whole attack if it buffers request bodies, taking the connection cost itself. Worth confirming yours does.
  • Connection layer. Fills with sockets that are active, not idle.
  • Web server and application server. Where it bites. Handlers sit pinned waiting for a body unless a rate floor on the body read cuts them off.
  • Application and database. Never reached. The handler is stuck in front of them holding half a form.
  • Auto-scaling. Sees no load spike, so it never scales.

Zone 3

Would you find out?

  • Dashboards. Requests per second look completely normal.
  • A check from outside your network. The one thing that reliably catches this, if you run one.
  • Logging and your SIEM. A request reaches the access log only once it ends, and then it looks like a client on a poor connection that timed out.
  • Alerting. Nothing crosses a threshold.
  • On-call. Nobody is paged. The service is down and the room is quiet.

Purple marks what only a test on your own infrastructure can settle. Everything else follows from how the attack works, not from how well your team configured things.

Zone 1 holds the difference between this attack and Slowloris. A WAF that waits for a complete request has a defensible reason to hold Slowloris traffic: the request is unfinished. RUDY takes that reason away. A control asked to decide whether this request is legitimate will decide it is, and be right. The only signal left is how fast the body arrives, and most request-inspection products have no opinion about that.

What separates RUDY from Slowloris

Five differences matter. Each points at a different control.

There are only a handful of parameters. Sockets per worker defaults to 20 and runs from 1 to 100, setting how many handlers you are trying to pin. The gap between body bytes defaults to 10 seconds and ranges from 1 to 60: shorter recycles sockets more often, longer makes each one harder to time out but risks the server closing it for inactivity. The declared Content-Length defaults to 100,000 bytes and ranges from 1,000 to 10,000,000, fixing each socket’s lifetime alongside the interval.

Host, Content-Type and Content-Length are forced and cannot be overridden. The truthful declared length is the mechanism, so letting it be changed would make this a different simulation.

How to defend against a RUDY attack

What decides this is a rule about how slowly a client may deliver a body it already promised. Three controls do the work; two popular candidates do not.

What you can check yourself, and what it will get wrong

Some of this you can settle today without help. It is worth knowing which parts, and where a cheap check misleads you.

Read the configuration. Confirm whether a body-read timeout exists, whether it enforces a rate floor or only an idle gap, whether the module is loaded rather than merely available, and whether your proxy buffers bodies. That tells you the setting exists, not that it holds under concurrency.

Test alert routing on its own. Fire a synthetic alert and watch whether it reaches the right rotation and whether anybody acts. That verifies the plumbing and needs no attack traffic.

Run a single-source test against a staging environment, with change-management sign-off, on infrastructure you own. That is legitimate and useful, and it is also where the trap is.

A test from one machine does not simply fail to answer the Zone 1 questions. It answers several wrongly, in a reassuring direction. Your per-source connection limit catches one address holding twenty sockets almost immediately, and reputation filtering handles a lone unfamiliar client. Every control in the front zone appears to work, so you conclude you are covered.

Then a distributed version arrives from thousands of residential addresses holding a few sockets each, and those controls behave differently, because they key on source concentration rather than the behaviour doing the damage. The cheap check produces a false pass on the controls you most need to trust.

How Obsidio tests a RUDY attack

Whether your handler pool holds depends on how your proxy, web server, application server and connection limits interact under real concurrency, and that appears in no single config file. Obsidio runs RUDY as a controlled, authorized simulation against infrastructure you own. Domain ownership is verified by DNS TXT record before any traffic flows, runs ramp up gradually, and any run can be aborted live. You set the sockets per worker, the interval between bytes and the declared content length, then watch your stack. Because load comes from 100,000+ globally distributed real devices rather than a handful of datacenter instances, a per-source connection cap gets tested the way a distributed attack would test it.

Watch the right number. For RUDY that is active and peak held connections per bucket, because it counts how many handlers are wedged at any moment. The request-rate figure counts socket opens, and each slot opens one and keeps it, so the rate reads close to nothing. A team watching the rate graph will decide the test is not working while the handler pool empties underneath them.

Be clear about the division of evidence. Obsidio reports how many connections were held and how your edge responded, because both are visible from outside. Whether your SIEM recorded anything, whether a dashboard moved and whether anyone was paged live only on your own consoles, so have someone watching during the window.

Strength is proven, not promised. RUDY does not test your bandwidth. It tests whether anything in your stack has an opinion about how slowly a client is allowed to keep its promises.

Where this fits in a testing programme

RUDY and Slowloris are the two halves of the slow-attack category, and running one does not cover the other. Slowloris starves a header block and is answered by a header-read timeout. RUDY starves a declared body and is answered by a rate floor on the body read. Different settings, usually in different places, often owned by different teams. Neither is exercised by an application-layer flood test, so a programme built only on HTTP floods leaves this class unmeasured.

The ten simulation types fail in genuinely different places. An HTTP flood inverts nearly everything above: the front defenses finally have something to act on, the damage reaches your database connection pool, and the detection problem flips from hearing nothing to drowning in alerts. Coverage across the categories is the point, not volume in one of them.

For regulated institutions the test also has to leave a record. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so a run leaves an artifact you can file rather than a screenshot you vouch for.

To scope an authorized RUDY simulation against your own infrastructure, see the Obsidio platform or get in touch. Independent, neutral, verifiable: resilience you can prove.

Apache Killer is the rare denial-of-service attack that needs almost no traffic to work. One small GET request, carrying a Range header that asks for dozens of overlapping slices of the same file, can push a vulnerable Apache HTTP Server to allocate the response body in memory over and over until the process runs out of room. The flaw is old and it has a name and a number: CVE-2011-3192, disclosed in August 2011.

That age is why it still belongs in a testing programme. The vulnerability is patched in every current Apache build, so the question is no longer “can this hurt me” but “am I actually running what I think I am running, everywhere, including the forgotten appliance and the vendor box nobody has rebuilt in years.” Apache Killer is a clean way to ask, because the server’s own reply is the answer.

The short version

This is one post in a series covering each of the ten simulation types Obsidio runs, one per attack. They fail in genuinely different places, which is the reason a test programme needs coverage across the categories rather than volume in one of them. For how they fit together and how to structure a first run, start with our guide on how to test your DDoS protection.

The ten simulation types

One post each, grouped the way the platform groups them.

Application-layer floods

Slow attacks

Connection exhaustion

Protocol-specific and scripted

What an Apache Killer attack actually does

The HTTP Range header exists for a good reason. It lets a client ask for part of a file rather than the whole thing, which is how a paused download resumes and how a video player fetches only the segment it is about to play. A well-formed request says Range: bytes=0-1023 and gets back the first kilobyte, and the specification even lets a client ask for several ranges at once.

Apache Killer abuses that last part. It sends a request whose Range header lists many overlapping byte ranges of the same file, something like bytes=0-1,5-1,5-2,5-3, and so on. A vulnerable server takes the list literally and allocates a separate buffer for every range in it. Because the ranges overlap and repeat, a tiny file gets materialised in memory many times over. The request costs the attacker almost nothing to send; the reply costs the server a multiple of the file size in heap and resident memory to assemble.

That is why the attack behaves so differently from a flood. It does not lean on bandwidth or request rate. It leans on memory, so a handful of these requests in parallel can drive an unpatched httpd process into exhaustion while the traffic graph barely moves.

The plain-language version: imagine ordering a single page from a librarian, but asking for it sliced into fifty mostly-overlapping pieces. The librarian photocopies the page fifty times to assemble your answer. Do that a few times at once and the librarian runs out of paper, even though you only ever asked about one page.

Which of your controls should have caught this

It is worth walking the whole chain a request passes through, because the answer here is unusual for this series. Three groups matter: what sits in front, what absorbs whatever gets through, and whether anything tells you it happened.

Zone 1

Should any of these have stopped it?

  • DDoS protection and scrubbing. Waits for a traffic spike. This attack does not produce one, so there is nothing for it to trigger on.
  • Firewall and WAF. The exception in the series, and worth saying plainly. The overlapping-range pattern is a known signature, not traffic that only looks legitimate, so a tuned rule genuinely can match it and block the request. That works only if the rule is present and current, and many deployments carry none.
  • Rate limiting. Counts requests. This sends far too few to cross a threshold, because the amplification is inside each request, not in their number.
  • Bot detection and challenges. It is a plain GET with no page load and no session to profile, so there is nothing to challenge.
  • CDN or fronting cache. A CDN that normalises or coalesces ranges shields the origin. One that forwards the Range header untouched does not. Worth confirming which yours does.

Zone 2

What takes the hit?

  • Load balancer or reverse proxy. Absorbs the attack if it coalesces or rejects egregious range sets before forwarding. Worth confirming yours does rather than passing the header through.
  • Connection layer. Not the pressure point. The attack reuses a few connections and concentrates the load on memory, not sockets.
  • Web server. Where it bites, and only on a vulnerable Apache build. Resident memory climbs with every overlapping-range request until the process cannot allocate more.
  • Application and database. Never reached. The body is assembled in the server’s byte-range handling before any application logic runs.
  • Auto-scaling. Keys on request rate or CPU, neither of which spikes. Memory climbs quietly on one host, so a rate-based policy never scales.

Zone 3

Would you find out?

  • Dashboards. Requests per second look normal, even low. The strain is in memory, which is not where most traffic dashboards point.
  • A check from outside your network. The single most useful signal here. The response to one crafted request tells you plainly whether you are patched.
  • Logging and your SIEM. These requests complete, so unlike a slow attack they do leave access-log entries. But nothing flags an odd Range header unless someone wrote a rule for it.
  • Alerting. Nothing crosses a request-rate or bandwidth threshold.
  • On-call. If the process runs out of memory, host-level monitoring may page someone for a crash or restart, even though nothing recognised the attack behind it.

Purple marks what only a test on your own infrastructure can settle. Everything else follows from how the attack works, not from how well your team configured things.

The picture here is less bleak than for most attacks in the series, and that is the honest point: a patched server and a tuned edge rule really can shut this down. The risk is not that the attack is unstoppable, but that you assume you are patched everywhere and have never proved it.

The one request that tells you whether you are patched

Apache Killer has an unusually clean diagnostic property. You do not need to overwhelm anything to learn where you stand. Send the crafted request and read the reply, because the response category maps directly to your state.

A patched server refuses to play along. Faced with a set of overlapping ranges that would cost more than the file is worth, it ignores them and returns the complete file with a normal 200 response, or it rejects the request outright. An unpatched server does the dangerous thing: it returns a multipart range response with one part per requested range, which is the full amplification in action. A rejection by an upstream proxy or hardened stack, refusing the oversized header before it reaches the origin, is a third and equally informative outcome. Each is a different, readable answer to the same question.

In the Obsidio simulation this is driven by two parameters. rangeCount is the amplification knob: it sets how many overlapping byte-range entries each request carries, and a vulnerable server allocates one response copy per entry. Its default is a deliberately mild 10, enough to demonstrate the effect without straining anything. Raise it in steps rather than jumping to the maximum, and watch which response category comes back at each one. A count in the hundreds stresses an unpatched server hard; a very large count may simply be refused by a modern stack, which is itself a useful signal. The second parameter, parallelRequestsPerWorker, defaults to 20 and sets how many run at once, multiplying the memory copies in flight without changing the per-request amplification.

One detail matters for reading results: the Range header is computed from rangeCount and forced onto every request. You cannot override it, because it is the attack. Anything in your own headers sits alongside it.

How to defend against Apache Killer

Defence starts with the version number and works outward from there. Each layer is verifiable, which is the useful part.

Each of these is a setting or a version string somewhere, which means each can be stale, missing on one host, or overridden by a change nobody re-checked.

What you can check yourself, and what it will get wrong

Apache Killer is the one attack in this series where a self-check is genuinely worth a lot. Two checks you can run today:

Read the version and the config. Confirm the Apache build on each host and check whether the range-handling directives are set. This tells you the setting exists and the version is current on the box you looked at.

Send one request to staging and read the reply, with change-management sign-off on infrastructure you own. The diagnostic lives in the response itself rather than in whether the server fell over, so you learn the true state of that one endpoint immediately.

The trap is not that the self-check misleads you about the endpoint you tested. It is what a single source cannot see. One request from one machine tells you nothing about the endpoints you did not think to test, and it is the forgotten ones, the legacy appliance and the unrebuilt vendor box, that carry the risk. Nor does it tell you whether anyone would notice memory climbing during a sustained run. A single request answers the version question. It does not answer the coverage question or the detection question, and those are the ones a real programme has to close.

How Obsidio tests it

Obsidio runs Apache Killer as a controlled, authorized simulation against infrastructure you own. Domain ownership is verified by a DNS TXT record before any traffic flows. Runs ramp up gradually, starting from the mild default range count and raising it in steps, and any run can be aborted live. You choose the range count and concurrency, then watch how each endpoint answers as the pressure rises. Because load comes from 100,000+ globally distributed real devices rather than a handful of datacenter instances, your estate is probed the way a real attacker would, not from a single predictable source your filters would trivially catch.

The thing to watch is the response category, not the request-rate graph, which stays low even while the attack is working. What matters is the mix of replies: a clean complete-file response or a rejection means that endpoint is holding, and a multipart range response means it is amplifying and exposed. Watching that mix shift as the range count climbs is the whole point of the run.

There is an honest split in what a test can measure. Obsidio measures the response category and edge behaviour from outside, because those are visible from the client side of the connection. Whether resident memory actually climbed on the host, whether your SIEM recorded the burst of range requests, and whether anyone was paged when a process restarted are answers that live only on your own consoles. Have someone watching them during the window, so the internal and external pictures line up into one finding.

Strength is proven, not promised. Apache Killer does not test your bandwidth. It tests whether the version you believe you are running is the version answering the request.

Where this fits in a testing programme

Apache Killer sits in the application-layer group alongside HTTP Flood, GoldenEye and Browser Flood, but it is the odd one of the four. The floods win on volume and hit request-processing capacity. Apache Killer wins on a specific implementation flaw and hits memory with almost no volume at all. A programme that only runs floods measures how much traffic your stack can take, but never asks the version-and-coverage question this attack answers, so that whole class of exposure goes unmeasured.

For regulated institutions, the test also has to leave a record. Obsidio produces cryptographically attested, tamper-evident reports mapped to FINMA, DORA and NIS2, generated inside Trusted Execution Environments, so the result of a run is an artifact you can file rather than a screenshot you have to vouch for. For an attack whose entire finding is which of your servers answered which way, a verifiable record of exactly that is worth having.

To scope an authorized Apache Killer simulation against your own infrastructure, see the Obsidio platform or get in touch with the team. Independent, neutral, verifiable: resilience you can prove.