My site is on a VPS in Frankfurt. How can a Cloudflare Worker send my visitors somewhere before they reach it?
The question carries an assumption. It treats the Worker as a place where the site has to live, the way a hosting account is. A Worker on a route is something else. It runs on Cloudflare’s edge, on the path between the visitor and wherever your DNS points, and it sees every request to that hostname before your server does.
That position is what a traffic distribution system needs. A TDS looks at each visitor, checks a list of rules and decides where the visitor goes: to a country page, to the mobile offer, to one of three landing pages under test, or on to your server as if nothing had happened. The server can be anything: a VPS, shared hosting, a page builder. The TDS does not care, because it only ever forwards to it. The pages it sends visitors to can be anywhere as well. A site made with Site Generator, our builder for affiliate sites, is a set of static files on Cloudflare Pages, and a rule only needs its address.
This guide explains how that works on Cloudflare, using the TDS in 301.st as the worked example, read from its source code. Every Cloudflare limit below was checked against Cloudflare’s documentation on 6 October 2026.
Where the Worker sits
When a DNS record in a Cloudflare zone is proxied (the orange cloud), the visitor’s browser does not connect to your server. It connects to Cloudflare, which terminates TLS, applies the zone’s rules and then opens its own connection to the address in the record. A Worker route plugs into that middle step. Cloudflare’s documentation on routes describes what happens when the Worker passes the request on:
Calling
fetch()on the incomingRequestobject will trigger a subrequest to your application server, as defined in the DNS settings of your Cloudflare zone.
So the Worker does not replace your server. It stands in front of it, and the server stays where the DNS record says it is.
Four conditions make this true, and each of them is a place where a setup quietly fails.
The zone is on Cloudflare. The domain’s nameservers point to Cloudflare. A route can only be created inside a zone you have.
The record is proxied. Routes need “a DNS record set up for the domain or subdomain proxied by Cloudflare”. A grey cloud record sends visitors straight to your server’s IP, and the Worker never runs for them.
The route pattern covers the hostname. example.com/* matches example.com and every
path under it, but not www.example.com. That is a separate hostname and needs its own
route or a pattern like *example.com/*.
No more specific route claims the request. “When more than one route pattern could
match a request URL, the most specific route pattern wins”, and only that one Worker runs.
A Worker you already have on example.com/api/* keeps /api/ for itself.
Your server needs nothing from this. It answers Cloudflare the way it answered before. The usual origin settings still apply: the SSL mode of the zone has to match what the server can do, or Cloudflare answers with a 525 or 526 (the article on those errors covers the fixes).
What 301.st puts in your account
The TDS does not run on our infrastructure. It runs in your Cloudflare account, and that is a deliberate choice: the requests count against your Workers quota, the rules live in your database, and the Worker keeps working if our API is unreachable.
When you connect an account, you give 301.st an API token. The token covers the whole platform, DNS and redirects included; the TDS part of it is Workers Scripts, Workers Routes, D1 and Pages read. With those, the platform creates in your account:
| What | Name | What it is for |
|---|---|---|
| Worker | 301-tds |
the TDS itself |
| Worker | 301-health |
domain health checks, separate from the TDS |
| D1 database | 301-client |
your rules, domain settings and hourly counters |
| Route | yourdomain.com/* |
one per domain that has at least one active rule |
A domain without rules gets no route, so its traffic never touches the Worker. The platform refuses to put a route on a domain served by a Cloudflare Pages project, and it will not take over a route that already belongs to another Worker. A Pages site can still be the target of a rule, like the generator sites above; it just cannot sit behind the route.
Note the pattern. The route is created for the hostname you added, exactly as written. If
visitors arrive on both example.com and www.example.com, add both as domains.
One request, step by step
Here is what the 301-tds Worker does with a request, in the order the code does it.
Two paths are taken. The Worker answers /health and /_health on your hostname
itself, for the platform’s checks. If your site has its own page at one of those paths,
visitors will get the Worker’s answer instead.
Static files go straight through. Requests for stylesheets, scripts, images, fonts and similar files skip the rules entirely. They still invoke the Worker, because the route covers every path, but they never touch the database.
Rules come from your D1, cached for a minute. The Worker reads all active rules in one query and keeps them in memory for 60 seconds. The cache lives in one Worker instance, and Cloudflare runs many, so after a change different visitors may see the old and new rules for up to a minute.
Changes reach the edge when you press Apply. Editing a rule in 301.st saves a draft. Apply writes the rules into your D1 through Cloudflare’s API. Once an hour the Worker also pulls the full rule set from the platform, as a backstop if a push failed.
The first matching rule wins. Rules are tried in a fixed order: rules that target bots first, then the other traffic shield rules, then SmartLink rules. Inside each group the priority you set decides, highest first. The first rule whose conditions all hold runs its action, and no other rule is looked at.
The groups come before the priority, and that matters. A rule counts as a bot rule only if it requires “bot”. Suppose you run Bot Shield, which blocks every bot, and a datacenter rule with a higher priority number that sends datacenter traffic to another page. An uptime monitor from a cloud server is a bot, so Bot Shield sees it first and blocks it. The datacenter rule never gets the chance.
Nothing matched means your server. The request goes on with fetch(request),
unchanged, with its method, headers, cookies and body. The visitor sees your site as if
the TDS were not there. The Worker only adds one to the hourly pass counter.
What a rule can check
A rule is a set of conditions. All of them have to hold for the rule to match. A condition left empty is not checked at all, so a rule with only a country list matches every device, every browser and every hour in those countries.
| Condition | What the Worker reads |
|---|---|
| Country, or countries to exclude | request.cf.country, set by Cloudflare from the visitor’s IP |
| Device: mobile or desktop | the Sec-CH-UA-Mobile client hint, otherwise the user agent; an iPad counts as desktop |
| Operating system, browser | the user agent |
| Bot, bot category, verified, network class | the bot classifier, described in the next section |
| Hour and day of the week | the clock in UTC, not the visitor’s local time |
utm_source, other query parameters |
the query string; a source list and a parameter list on the same rule match if either does |
utm_campaign |
the query string, as a separate condition |
| Path | a regular expression on the path, without the query string |
| Referrer | a regular expression on the Referer header |
Hours are UTC on purpose. The counters are stored by UTC hour, and a rule in the visitor’s local time would spread over every hour of the report. If you want “nights in Germany”, convert to UTC yourself and remember the clock change in March and October.
The utm_source or parameter pair is the one place where a rule says “either”. The
Facebook preset matches utm_source=facebook or the presence of fbclid, because paid
social clicks often carry fbclid and no UTM tags at all.
The TDS does not match on the visitor’s IP address, a specific ASN number, the browser language or cookies. If you need one of those today, a rule in 301.st will not do it.
What a rule can do
A redirect is an answer to the browser. The Worker tells it where to go and is done; the browser then connects to the target on its own, wherever that target is hosted. The Worker does not see what happens there, which is why counting a conversion on the target needs the postback described further down.
Redirect. With the status code you choose: 301, 302, 307 or 308. A permanent code
tells browsers to remember the answer, which is wrong for a TDS decision that depends on
the visitor, so the presets use 302. Every redirect the TDS sends also carries
Cache-Control: private, no-cache, so no shared cache stores one visitor’s answer for the
next.
The redirect can be sent in five ways: a plain HTTP redirect, a meta refresh page with an
optional delay, a meta refresh that hides the referrer, a JavaScript location.replace(),
or an iframe that keeps your address in the bar. A separate switch adds
Referrer-Policy: no-referrer to any of them. Hiding the referrer is off by default,
because some affiliate networks attribute by it and would stop counting without telling
anyone.
The target URL can contain macros that the Worker fills from the request: {country},
{device}, {os}, {browser}, {path} and {host}. A rule that sends every country to
https://example.net/{country}/ is one rule, not two hundred.
Block. The Worker answers 403 Blocked and your server never sees the request.
Split. A split rule has several target URLs and picks one for each visitor. With a
fixed split you set weights, and 90 and 10 send nine visitors in ten to the first
page. A weight of zero pauses a page without losing its numbers. The other three modes
learn from conversions as they arrive and shift traffic toward the page that converts
better: Thompson sampling, which is the default, UCB and epsilon greedy. A split can also
keep separate pools of pages per group of countries, so the German traffic is tested on
German pages.
A split that learns needs to know which visitor converted. That is the job of the signed click token the Worker adds to the target URL, and of the postbacks that bring the conversion back. Setting those up with a tracker or an affiliate network is a topic of its own.
How the TDS tells bots apart
Every request goes through a classifier before any rule looks at it. The classifier puts
a bot into one of five groups by its user agent: search engines, AI training crawlers,
uptime monitors, ad reviewers and link preview fetchers. A user agent shorter than ten
characters counts as a bot. A user agent with bot, crawl or spider in it that no
table recognizes is an unverified bot of no group.
A user agent is a string anyone can send. That is why the classifier keeps a second,
separate answer: verified or not. It comes only from Cloudflare. When Cloudflare has
confirmed that a request really comes from the bot it claims to be, it sets
request.cf.verifiedBotCategory
(the field).
The group comes from the user agent table, the confirmation from Cloudflare. A scraper
that sends Googlebot’s user agent lands in the search group and stays unverified.
The third answer is the network. If the autonomous system the request comes from belongs to a hosting or cloud provider (AWS, Google, Azure, Hetzner, OVH, DigitalOcean and similar), the request is marked as coming from a datacenter. People browse from home and mobile networks. Checkers, scrapers and review systems often run in datacenters, whatever browser they claim to be.
A rule can combine these: bot or not, which groups, verified or not, datacenter or not.
The presets
You do not have to write rules from scratch. 301.st ships these templates, each one a single rule with its priority already set.
| Preset | Matches | Does |
|---|---|---|
| AI Guard | AI training crawlers | blocks, or redirects to a page you choose |
| Cloaking Standard | ad reviewers, search engines, link preview fetchers | redirects them to a page you choose (a “white page”) |
| Datacenter Cloak | any request from a datacenter network, bot or not | redirects it to a page you choose |
| Bot Shield | any bot | blocks, or redirects |
| Geo + Mobile | mobile visitors from the countries you list | redirects |
| Mobile Redirect | mobile visitors | redirects |
| Desktop Redirect | desktop visitors | redirects |
| Geo Filter | visitors from the countries you list | redirects |
| Facebook Traffic | utm_source of facebook, fb, fb_ads or meta, or fbclid |
redirects |
| Google Traffic | utm_source of google or google_ads, or gclid |
redirects |
| UTM Split | the utm_source values you list |
redirects |
AI Guard leaves search engines alone on purpose. They have their own group, and blocking them to keep scrapers out would take the site out of search results.
What the two cloaking presets do, and what they cost you
Cloaking Standard and Datacenter Cloak show one page to the systems that review a site and another page to the people who visit it. That is what the word means, and we name the presets after it so nobody switches one on by accident.
Know what you are running into. Google’s spam policies define cloaking as “presenting different content to users and search engines with the intent to manipulate search rankings and mislead users”. Google Ads names it in its Circumventing systems policy: “showing different content on your website to different people or to Google to try to hide things that might break Google Ads’ rules”. The penalty is stated in the same place: accounts “will be suspended upon detection and without prior warning”. Other ad platforms have rules of the same kind.
The same Google Ads page draws the line for the rest of this article. It says “It’s okay to show slightly different content to different people, like showing a landing page or ad destination in different languages, having different special offers, or adjusting for geographical location”. Its forbidden example is a shop that shows Google a page selling clothing while the site sells guns. A geo rule that sends each country to its own landing page for the same offer fits the first description. Which side a particular rule falls on is your decision and your risk.
Two technical facts matter if you use them. First, Cloaking Standard matches on the user
agent unless you also require “verified”. Without that switch, a reviewer that sends a
plain browser user agent goes through to the real page, and anyone who types Googlebot
into their user agent gets the white page. Second, Datacenter Cloak recognizes networks by
the name of the provider that owns them. That catches the large clouds and misses a
provider whose name is not on the list.
What it costs on the Free plan
A route runs the Worker on every request to the hostname: the page, and each image, stylesheet and font it loads. The static bypass described above saves database work, not invocations. The Workers Free plan allows 100,000 requests a day, reset at midnight UTC (limits).
The database has its own daily allowance. D1 on the Free plan includes 5 million rows read and 100,000 rows written a day (D1 pricing). On a domain with rules, every page request reads the domain’s settings and writes at least one counter row. So on page requests the two allowances run out at about the same traffic.
What happens then is different for the two.
When D1 runs out, Cloudflare says queries stop until the reset. The TDS treats a failed rules query as “no rules” and passes every request to your server. Your site keeps working. The routing stops, and so does the counting.
When Workers run out, the route decides. A route either fails open, which “Bypasses
the Worker. Requests behave as if no Worker is configured”, or fails closed, which
“Returns a Cloudflare 1027 error page”. 301.st does not set this mode when it creates
the route today. Open Workers & Pages in your Cloudflare dashboard, find the routes of
301-tds, and check the mode on each. On a TDS in front of a working site, fail open is
almost always what you want: when the quota ends, visitors reach your server unsorted
instead of reaching an error page.
What lifting both costs. The Workers Paid plan is $5 a month per account with 10 million requests included, then $0.30 per additional million (pricing). It has no daily request limit, and it removes the D1 daily limits as well. For a TDS on more than a few small domains, that is the plan to be on.
The article on bot analytics Workers works through the same route arithmetic with real numbers from this site.
Setting it up
- Move the zone to Cloudflare if it is not there, and check that the record for the hostname is proxied. Leave it pointing at your current server.
- Check the SSL mode against your server: Full (Strict) if the server has a valid certificate, Full if it has any certificate, Flexible only if it has none.
- Create an API token with the permissions the 301.st connect screen lists, and connect the account. The platform creates the two Workers and the database.
- Add the domain, and
wwwas a separate one if visitors use it. - Create a rule, from a preset or by hand, and press Apply. The route appears with the first active rule.
- Set the route to fail open in the Cloudflare dashboard, unless you are on Workers Paid.
- Test from outside. A user agent is the easiest condition to try:
curl -sI https://example.com/ | grep -i -E '^(HTTP|location)'
curl -sI -A "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) Mobile" \
https://example.com/ | grep -i -E '^(HTTP|location)'
The first request should come back with your server’s normal answer, the second with the redirect of a mobile rule. Rules by country need a request from that country, through a VPN or a remote browser. Allow a minute after Apply for the rule cache.
When you need a TDS at all
If all you need is one fixed condition, try Cloudflare without a Worker first. A Single
Redirect rule is an expression in Cloudflare’s rules language, which has fields for the
visitor’s country (ip.src.country) and user agent (http.user_agent), and it does not
count against any request quota. The Free plan gives a zone ten of them;
every way to redirect on Cloudflare lays out what
each option can match and what it costs. The Worker earns its place when the decision needs
something a static rule does not have: telling verified bots from claimed ones, splitting
traffic and learning which page converts, counting every outcome per rule and hour, and
keeping those rules the same across a portfolio of domains. That last part is the reason
301.st exists, and the TDS is the piece of it that runs in front of your server.