Black Friday 2026 falls on 27 November: the German dictionary Duden defines it as the Friday after Thanksgiving Day, and according to the U.S. Office of Personnel Management, Thanksgiving 2026 falls on Thursday, 26 November. For an online shop, however, the problem is not the day but the minute in which the offers go live. That is when newsletters, ads and regular customers hit the cart, the stock records and the payment interface at the same time. A virtual waiting room lets visitors in, in an orderly sequence and at the pace the shop can actually process. This article shows where a waiting room sits technically, how pass, session and cart work together, which status codes browsers and search engines should see, how to set the trigger threshold and which schedule is realistic until the end of November.
Why the effort pays off before Black Friday
The German Retail Federation (HDE) expected combined revenue of 5.8 billion euros for Black Friday and Cyber Monday 2025, which would be the first decline. The forecast was just under two percent below the previous year; for 2024 the federation had expected 5.9 billion euros. Both are federation forecasts based on surveys, not measured revenue, and an HDE forecast for 2026 had not yet been published at the time of writing. For the technology, the slight dip hardly matters anyway. What matters is that business worth billions is concentrated on a few promotion days – and within those days on the hours in which discounts are switched on.
Demand, too, is largely online. According to Bitkom, roughly one in two internet users aged 16 and over (52%) wanted to take advantage of the offers around Black Friday 2025. Of those who planned to shop on Black Friday and during Cyber Week, 69% intended to do so exclusively online, 26% planned to buy online and in store, and 4% only in a physical store (survey in calendar weeks 40 to 41/2025). In the HDE survey of online shoppers, 48% said they wanted to go bargain hunting on Black Friday. For the shop this means: people who come online do not stand in front of a shop door where a queue forms on its own. Without a precaution of your own, every visitor hits the server immediately.
The Federal Statistical Office (Destatis) had already linked the November development in 2024 to the promotion days: part of the Christmas business has shifted into November through promotions around Black Friday and Cyber Monday, above all in online and mail-order retail. For November 2025, Destatis reported, based on provisional figures, real revenue growth of 5.9% in online and mail-order retail compared with the same month of the previous year, and calendar- and seasonally adjusted real growth of 1.1% for retail as a whole. Online retail therefore grew considerably faster than retail overall in that month. Anyone selling online often experiences their highest load not in December but at the end of November.
Forecasts, surveys and monthly revenue describe the market, not your shop. How many visitors actually arrive in the minute after the offers go live, and at what load your checkout slows down, can only be shown by your own server logs from previous years and a load test with realistic shopping paths. Derive the threshold of the waiting room from these measurements, not from industry figures.
What a waiting room is – and what it is not
A virtual waiting room is an admission control in front of the shop. As long as there is enough capacity, nobody notices it: visitors go straight to the page they requested. Only when a defined limit is reached do new visitors receive a lean waiting page showing their position, and they are let in automatically as soon as a slot becomes free. Anyone already in the shop is not disturbed – customers in checkout in particular must never fall back into the queue. This sets the waiting room apart from three related measures: the maintenance page, which locks everyone out; rate limiting, which slows down individual clients that are too fast; and automatic scaling for traffic peaks, which provides more computing power. Scaling helps as long as the bottleneck is in the application servers. The database, locks on stock records and the payment provider interface, however, do not grow automatically – and that is exactly where the waiting room comes in.
| Measure | What it controls | Effect for visitors | Limit |
|---|---|---|---|
| Waiting room | Number of simultaneously active visitors | New visitors wait briefly, those in the shop buy undisturbed | Needs exceptions for payment, crawlers and interfaces |
| Rate limiting (429) | Requests per client and time window | Only conspicuously fast clients are slowed down | Does not help when many real visitors arrive at once |
| Automatic scaling | Number of application servers | No waiting as long as reserves suffice | Database, locks and payment interface do not grow along |
| Maintenance page (503 for everything) | The entire shop | Nobody gets in | No revenue and a risk for visibility in search |
| Disabling only the cart | The purchase process | Browsing possible, buying not | No revenue in that period, but the catalogue stays reachable |
For the case where a business temporarily cannot sell, the Google documentation describes two poles. Disabling only the cart functionality is, according to Google, the simplest approach and does not change anything for visibility in search. Google advises against disabling the whole website; anyone who nevertheless urgently has to do so for 1 to 2 days should return an informational page with the 503 status code instead of all content. A waiting room sits between these poles: it does not switch anything off but meters access – and if it is built correctly, search engines see as little of it as possible.
The 503 (Service Unavailable) status code indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.
RFC 9110, Section 15.6.4
The architecture: where the waiting room sits
The decision about who may enter is made in front of the shop application – in the reverse proxy, in the load balancer or in an upstream edge layer. There it is cheap: checking a signed cookie and incrementing a counter costs little computing time, whereas every request that reaches the application keeps PHP, the database and the caches busy. The waiting page itself is a static file delivered without the application and without the database. Building the waiting room as a plugin inside the shop, by contrast, lets the load in first and sorts it afterwards – that hardly relieves anything. As part of our Shopware hosting with its own upstream layer we therefore plan this layer in front of the application and without a third-party service.
Admission control at the edge
The decision is made in the reverse proxy or load balancer before PHP, the database or the search index do any work. A waiting visitor therefore costs the shop almost nothing.
Static waiting page
A single HTML file with embedded CSS and a few lines of script. It needs no database and can be served directly from memory.
Signed pass
A cookie with an expiry time and a signature. The edge checks it without a database query; without the key it cannot be forged or extended.
Counter for active visitors
A shared counter, for example in Redis, records how many passes are currently valid. It is the only point that all edge nodes share.
There are two models for control. In the rate model, the waiting room admits a fixed number of new visitors per minute, regardless of how many are already in the shop. This is simple and predictable but does not react to visitors staying for different lengths of time. In the capacity model, the waiting room counts the valid passes and only admits more when one becomes free – for example because an order has been completed or a pass has expired. This reflects the actual load better but can release many slots at once after a surge. In practice a combination works well: capacity as the upper limit, plus a cap on inflow per minute so that the shop does not immediately get hit by the next wave after the first one.
A clear order makes the waiting room fair. If an offer starts at a fixed time, a pre-queue is advisable: anyone arriving before the start receives a waiting token, and at the start time the order of those waiting is set at random. That way nobody gains an advantage by reloading the page every second for minutes beforehand. Anyone arriving after the start joins the back of the queue. Reloading must neither worsen nor improve the position – the waiting token lives in the cookie, not in the individual request. Technically, this results in the following flow:
- The request is first checked against the exception list: robots.txt, sitemaps, static files, the return from the payment provider and status requests always pass through.
- If the request carries a valid, signed pass, it goes straight into the shop and the expiry of the pass is extended.
- If the counter still has free capacity, the visitor receives a new pass and lands directly on the requested page.
- Otherwise the visitor gets a waiting token and, under the same URL, the static waiting page, delivered with status code 503 and Retry-After.
- At intervals, the waiting page queries a lean status endpoint and shows the position and estimated waiting time.
- When it is the visitor’s turn, the status endpoint swaps the waiting token for a pass, and the page reloads the same URL – without a redirect.
// Decision at the edge: pass through, admit or let wait
const EXCEPTIONS = [
/^\/robots\.txt$/,
/^\/sitemap/,
/^\/(theme|media|bundles|thumbnail)\//,
/^\/payment\/(finalize-transaction|webhook)/,
/^\/api\/_info\/health-check$/,
/^\/waiting-room\/status$/
];
async function decide(request, counter, key) {
const path = new URL(request.url).pathname;
if (EXCEPTIONS.some((pattern) => pattern.test(path))) {
return 'pass';
}
// Valid pass: extend its expiry and continue into the shop
const pass = readCookie(request, 'wr_pass');
if (pass && await signatureValid(pass, key)) {
return extend(pass);
}
// Free slot: issue a new pass and deliver the page directly
if (await counter.claim()) {
return newPass({ validMinutes: 45 });
}
// No slot: waiting page under the same URL, 503 instead of 200
const token = readCookie(request, 'wr_token') || await counter.enqueue();
return new Response(WAITING_PAGE, {
status: 503,
headers: {
'Retry-After': '30',
'Cache-Control': 'no-store',
'Content-Type': 'text/html; charset=utf-8',
'Set-Cookie': `wr_token=${token}; Path=/; Secure; HttpOnly; SameSite=Lax`
}
});
} Keep pass, session and cart separate
It would be tempting simply to attach the pass to the shop session. That is not a good idea for two reasons. First, the edge does not know the session without asking the application – and that is exactly what it is supposed to avoid. Second, the lifetime of a PHP session is not a reliable clock: without a setting of your own, session data is regarded as garbage after 1440 seconds, i.e. 24 minutes, according to the PHP manual and may be deleted from then on during garbage collection; whether that runs at session start depends on session.gc_probability and session.gc_divisor. The 24 minutes are therefore an earliest limit, not a fixed expiry. According to its own documentation, Shopware refers to the same PHP value as long as Symfony does not set a lifetime of its own.
The pass is therefore given its own, deliberately chosen lifetime, which the edge checks itself. It should be generous enough for a visitor to compare and order at leisure, and short enough for abandoned slots to become free again promptly. Every request with a valid pass extends it. Anyone entering checkout receives a longer period so that nobody falls back into the queue between entering the address and confirming payment. The counter releases a slot as soon as the pass expires or the order is completed.
After payment, the payment provider sends the customer to a return URL of the shop and, in parallel, reports the payment status via an interface. Both must bypass the waiting room – even if the customer’s pass has expired in the meantime. If the return lands on the waiting page, the payment may have gone through while the order in the shop remains open. The same applies to callbacks from the merchandise management system and shipping.
Even behind the waiting room, the cart remains a possible bottleneck. According to the Shopware documentation, the database as cart storage can become a bottleneck at high throughput, for example thousands of orders per minute; for such scenarios the documentation recommends Redis as storage for the cart. The switch is not something to flip the evening before: Redis must be configured for persistence so that carts survive a restart, existing carts have to be migrated, and the whole setup belongs in the load test. How Redis also takes load off pages and queries is described in our article on Redis caching for Shopware.
- Pass as a separate, signed cookie, independent of the shop session
- Lifetime of the pass defined and tried out in the load test with real shopping paths
- Longer period from the moment checkout is entered
- Return URLs and interfaces of the payment providers on the exception list
- Cart storage prepared for high throughput and configured for persistence
- For B2B shops with customer accounts: login and price queries sit behind the waiting room, not in front of it
Status codes: what browsers, crawlers and caches see
The response the waiting page carries determines how browsers, search engines and caches interpret it. The HTTP standard RFC 9110 provides the 503 status code for a temporary overload. The Retry-After header can additionally state when a new attempt makes sense – either as a date or as a number of seconds. The 429 status code from RFC 6585 means something else: a single client has sent too many requests in a given time, and responses with 429 must not be stored by a cache. This results in a clear division of labour: 503 with Retry-After for the waiting page, because the shop as a whole is currently at capacity, and 429 for rate limiting individual clients.
| Situation | Status code | Standard | Note for operations |
|---|---|---|---|
| Visitor has to wait | 503 with Retry-After | RFC 9110 | Waiting page under the same URL, delivered with no-store |
| Single client requests too fast | 429 | RFC 6585 | Must not be cached, add Retry-After |
| Visitor has been admitted | 200 | RFC 9110 | Normal shop response, the pass is extended |
| Redirect to a separate waiting page | 302 or 307 | RFC 9110 | Avoid: the requested URL is lost and the chain gets longer |
| robots.txt and sitemaps | 200, never 503 | RFC 9309 | Never behind the waiting room, never answered with 503 |
How Google handles this is set out in the documentation of its crawling infrastructure. Google’s crawlers treat 429 as a signal that the server is overloaded and therefore as a server error. Server errors of the 5xx class and the 429 status code prompt the crawlers to temporarily slow down crawling. For the 503 page, Google recommends the Retry-After header with a best-effort date or duration. The classification matters: 503, 429 and Retry-After are signals. Neither browsers nor crawlers are obliged to respect the stated time, and no search engine commits to how it will assess a particular response in an individual case.
To search engines and caches, a waiting page with status code 200 looks like the actual content of the product page. In the worst case, the waiting text ends up in the index or in a cache and is still delivered from there long after the rush is over. So for every response carrying the waiting page: 503, Retry-After and Cache-Control: no-store.
It is just as important not to send visitors to a separate waiting URL via a redirect. By default, Google’s crawlers follow up to 10 redirect hops, but every additional hop costs time, and the originally requested product page is lost when the visitor is admitted later. It is better to serve the waiting page under the requested URL itself and reload the same URL after admission. There is a further limit on duration: Google advises against serving 500, 503 or 429 to crawlers for longer than 1 to 2 days, because this may have a negative effect on how the site appears in Google products. A waiting room that only kicks in for minutes or hours at peak times stays well below that. The 1 to 2 days, however, are an assessment from the documentation, not a commitment regarding rankings.
Crawlers, bots and robots.txt
The robots.txt never belongs behind the waiting room. Under the Robots Exclusion Protocol (RFC 9309), a crawler that cannot reach the robots.txt because of server or network errors must assume a complete disallow. Google explicitly advises against answering the robots.txt with 503 because this blocks all crawling. How this plays out in detail is described in Google’s robots.txt documentation: in the event of a server error, Google stops crawling the site for the first 12 hours and keeps trying to fetch the file; if that still fails afterwards, Google uses the last good version for the following 30 days. The standard thus describes what a crawler must assume, the Google documentation how one particular crawler implements it. For the shop the conclusion is the same in both cases: robots.txt is at the very top of the exception list.
Conversely, robots.txt is no short-term valve for keeping crawlers away during the offer launch either. RFC 9309 states that crawlers should not use a cached version for more than 24 hours unless the file is unreachable. Google generally caches the content for up to 24 hours, and longer in the case of timeouts or 5xx errors. A change on the morning of Black Friday may therefore only take effect once the rush is over – and a forgotten block still has an effect days later. The exception list of the waiting room should therefore contain at least:
- robots.txt and all sitemaps
- Static files such as images, fonts, stylesheets and scripts, which come from the cache anyway
- Return URLs, notifications and interfaces of the payment providers as well as callbacks from merchandise management and shipping
- Health checks from the load balancer and monitoring
- The status endpoint of the waiting room and the waiting page itself
- Legal notice, privacy policy and contact page, so that mandatory information remains reachable at all times
Not every visitor in the queue is a human. An example from outside retail shows how strongly bots can distort load: at the Wikimedia projects, at least 65% of the expensive traffic that reaches the core data centres came from bots, although bots accounted for only about 35% of all pageviews (Wikimedia Foundation, article of April 1, 2025). This is not shop traffic and allows no conclusion about the share of bots in a shop. It does show, however, that load and visitor numbers can diverge: a few automated clients requesting uncached pages such as search, filters or the cart put more strain on the server than many humans on cached category pages. How to detect and manage such traffic is described in our article on bot traffic and scraping in online shops.
This results in three rules for the waiting room. First: rate limiting in front of the waiting room slows down individual clients that send conspicuously many requests in a short time, using 429 and Cache-Control: no-store – under RFC 6585 responses with 429 must not be stored by a cache anyway. Second: cart, stock and search interfaces can only be reached with a valid pass, so that scripts cannot bypass the queue via the interface. Third: known search engine crawlers get a separate, small quota outside the queue. They are recognised via a reverse DNS lookup followed by a forward lookup, not via the user agent string, which any client can set freely.
Setting thresholds: when the waiting room kicks in
The most important number in the whole setup is the capacity limit – and it cannot be read off a table. It comes from a load test with realistic shopping paths: home page, category, filters, product page, cart and checkout, in the mix your logs from the previous year show. What you are looking for is the tipping point at which response times and error rate rise sharply. The limit of the waiting room sits well below it, because in a real peak effects come into play that no test fully reproduces. How such a test is set up and which emergency plan belongs with it is described in our article Peak season: load testing and emergency plan for the shop.
For triggering during operation, response times are better suited than visitor numbers because they reflect the actual load. The reference values from web.dev offer a starting point: a good user experience means a Largest Contentful Paint within 2.5 seconds of the page starting to load, measured at the 75th percentile of page loads, segmented across mobile and desktop. For Time to First Byte, web.dev considers 0.8 seconds or less good and values above 1.8 seconds poor. These values are intended for assessing field data, not as the trigger threshold of a waiting room. They are still useful as a starting point for a proposal, because they describe when visitors experience a page as slow. Why field data says more than individual test runs is explained in our article Field data over lab scores.
| Level | Signal at the server (75th percentile, rolling five minutes) | Reaction of the waiting room |
|---|---|---|
| Normal | Server response time up to 0.8 seconds, error rate unremarkable | Admission up to the defined capacity, the counter keeps track |
| Observe | Server response time rises clearly above the usual value | Limit inflow per minute, do not raise capacity further |
| Slow down | Server response time above 1.8 seconds or rising error rate | Lower capacity step by step, new visitors wait longer |
| Emergency brake | Errors in checkout or payment interface | Stop inflow, serve only checkout and return from the payment provider |
The levels work as a control loop: raise capacity slowly when response times remain stable over several minutes, and lower it quickly when they rise. Adjusting in large steps creates oscillation between overload and an empty shop. At a minimum, you should monitor the response times of the checkout, the error rate, the number of active passes, the length of the queue and orders per minute. From the admission rate and queue length you can also estimate the value for Retry-After and the displayed waiting time. How to record these values continuously and set sensible alerts is shown in our article on monitoring uptime and performance.
The waiting page: static, mobile, honest
The waiting page is often the first impression a visitor gets of the shop that day – and usually it is seen on a smartphone. According to the Bitkom study report on digital retail, the smartphone is the device most frequently used for online shopping, at 73% clearly ahead of all other devices. The page is therefore designed mobile first and must load quickly even over a weak mobile network: a single HTML file of a few kilobytes, with embedded CSS, without web fonts, without catalogue images and without third-party scripts. How pages become fast on mobile devices is described on our PageSpeed optimisation page.
- A clear headline: the shop is in high demand right now, you are in the queue.
- Position or progress bar, plus the estimated waiting time as a range rather than to the second
- A note to keep the page open, because reloading speeds nothing up and does not improve the position
- A sentence about the cart if the shop restores saved carts on the next visit
- Accessibility: sufficient contrast, status changes via a live region for screen readers, no colour coding alone
- Links to the legal notice, privacy policy and contact
- No tracking scripts and no consent prompt – the page should load as little as possible
The shop generates part of the peak itself: a newsletter that goes to all recipients at exactly 8 pm sends a large share of its readers to the same page in the same minute. Staggered sending in waves smooths the curve without anyone learning about the offer noticeably later. Figures from Bitkom show how well prepared many buyers are: 61% of Black Friday and Cyber Week shoppers in 2025 already had a clear idea of their budget, and this group planned to spend EUR 312 on average; in the corresponding group in 2024 the figure was EUR 280, when 53% had a fixed budget. People who arrive with a fixed budget and a shopping list want to get to the checkout quickly. A short wait with an honest estimate is the lesser evil compared with a checkout that breaks off in the middle of payment.
Schedule for Black Friday 2026
Between the beginning of October and 27 November there are a good seven weeks. That is enough for a clean setup, but not for experiments in the final week. A realistic schedule looks like this:
- Week 42: define the architecture – where the waiting room sits, which control model applies and which URLs belong on the exception list.
- Weeks 43 to 44: build the waiting room, waiting page and status endpoint, switch the cart storage and set up measuring points.
- Week 45: load test with realistic shopping paths, determine the tipping point, set the capacity limit and trigger levels.
- Week 46: trial run in live operation with a low limit, for example during a smaller promotion, including the payment return and crawler access.
- Week 47: change freeze for shop, plugins and infrastructure; only bug fixes after approval.
- 27 November: waiting room live, team on standby, status overview in view.
- Afterwards: evaluate the logs and adjust the limits for next year and for other promotions.
The change freeze is not a formality. Every update in the days before Black Friday changes the load behaviour you measured in the test. Larger projects such as preparing the upgrade to Shopware 6.8 therefore belong either before the load test or in December. And if something does have to be rolled out, then with a procedure that switches over without downtime and can be reversed immediately, as described in the article on zero-downtime deployments with blue-green switching.
Common mistakes with a waiting room
- The waiting page is delivered with status code 200 and ends up in caches or in the search index.
- The robots.txt or the sitemaps are behind the waiting room.
- The return from the payment provider runs into the queue, and paid orders remain open.
- The pass is tied to the shop session and expires in the middle of checkout.
- The waiting room is built as a plugin in the shop application and lets the load in before sorting it.
- The waiting page loads fonts, images and tracking scripts and comes under the same load as the shop.
- Reloading improves the position, and visitors with scripts overtake everyone else.
- The capacity limit comes from an estimate instead of a load test.
How we approach it
We start with an assessment: with the shop check we look at the setup, response times, caches and the paths to the checkout and establish where the bottleneck lies today. This results in a plan for the upstream layer – in the existing hosting or on our cloud infrastructure. As part of Shopware maintenance we take care of updates, the change freeze and standby around the promotion days; with hosting for Shopware, waiting room, counter and monitoring can become part of ongoing operations. If you would like to know what is still realistic for your shop by the end of November, write to us via the contact form.
This article draws on the press releases of the German Retail Federation (HDE) of November 19, 2024 and November 17, 2025, the Bitkom press release of November 24, 2025 and the Bitkom study report “Digitaler Handel in Deutschland”, press releases of the Federal Statistical Office (Destatis), the standards RFC 9110, RFC 6585 and RFC 9309, the documentation of Google Search Central and Google’s crawling infrastructure, the PHP manual, the Shopware documentation, the reference values from web.dev, an article by the Wikimedia Foundation of April 1, 2025, the Duden dictionary and the holiday calendar of the U.S. Office of Personnel Management. All information was checked on 23.09.2026.
Automatic scaling provides more application servers and helps as long as the bottleneck is there. The database, locks on stock records and the payment provider interfaces, however, do not grow at the same pace, and new servers need time to start. A waiting room complements scaling: it limits inflow to what the slowest component can process.
Built correctly, the risk stays limited: waiting page with 503 and Retry-After, no redirect, robots.txt and sitemaps on the exception list and a separate quota for verified crawlers. Google advises against serving 500, 503 or 429 to crawlers for longer than 1 to 2 days – a waiting room that only kicks in at peak times stays well below that. Nobody makes commitments about rankings, including search engines themselves.
Long enough for a visitor to compare and order at leisure, and short enough for abandoned slots to become free again soon. The value is determined in the load test with real shopping paths, every request extends it, and checkout gets a longer period. You should not rely on the PHP session for this: according to the PHP manual, its default of 1440 seconds is only an earliest limit for clean-up, not a fixed expiry.
The 503 status code with the Retry-After header, as RFC 9110 provides for a temporary overload, plus Cache-Control: no-store. The 429 status code from RFC 6585 is intended for individual clients that send too many requests, not for a rush of many real visitors.
The load test decides: the limit sits well below the tipping point at which response times and errors rise sharply. During operation, response times serve as the signal. The web.dev values, for example 0.8 seconds as a good Time to First Byte, are suitable as a proposal for one level, but they are intended for assessing field data and are not a standard for waiting rooms.
It can be useful wherever demand meets a fixed point in time: limited products, pre-sales, TV advertising, newsletters to large mailing lists or discount promotions at a specific time. Once the waiting room has been built and tested, it stays invisible in everyday operation and uses hardly any resources until the next peak comes.