Spoofed Stack Detection: How Sites Probe for Unrealistic Proxy Headers


Hannah
July 9, 2025


Spoofed Stack Detection: How Sites Probe for Unrealistic Proxy Headers
Somewhere along the line, “spoofing headers” became a punchline in the anti-bot world. Maybe it’s because of all those old blog posts from the early days—just grab a User-Agent, match your Accept-Language, toss in a Referer, and you were good. At least, you thought you were. For a while, it even worked. There was a time when the internet was simple, when scripts just looked for the wrong browser string or a missing Accept-Encoding, and sent you packing. But if you’re reading this in 2025, you know better. That game is long over.
The reality now is that every header you spoof is a thread in a story. Sites aren’t just reading your headers. They’re building a behavioral model—one that expects scars, quirks, chaos, and entropy. That’s where almost everyone gets burned. You can copy every value, patch every string, but if your stack doesn’t look like it’s lived a life, you’re dead in the water.
Where The Game Gets Lost—Tiny Tells and Unlived Histories
Let’s talk about how this plays out. The first thing you notice is friction, but it’s never obvious. Maybe your session hangs for a couple seconds too long. Maybe the login form flickers, or a page times out for no reason. The errors feel random—sometimes you’re banned outright, sometimes you just get nudged to the slow lane.
What you’re not seeing are the invisible clusters behind the scenes. Your “clean” stack is actually just a beacon—one that detection engines love. You matched the main User-Agent, but missed that on this OS, with this browser version, the Accept header should have been in a different order. Or you sent Accept-Language in lower case, while every real user in your declared locale uses upper case, or throws in a regional fallback.
Headers don’t travel alone. They’re shaped by time—by upgrades, by installed extensions, by weird navigations and aborted loads. There are patterns you don’t even realize you’re missing. Like the fact that mobile Safari on iOS 17 adds a header Chrome doesn’t, or that a real user with an accessibility extension will have an oddball DNT or a Sec-GPC out of nowhere.
Nobody tells you about these details in the blog posts. You only learn after your pool is dead and you’re sifting through packet captures, wondering why everyone got flagged at 3:07am.
Messy Isn’t Just Plausible—It’s Required
Here’s a hard lesson: real user stacks are a mess. They accumulate junk. They pick up a Russian Accept-Language after a trip, then forget to remove it. They hold onto an odd Accept-Encoding after an old browser update. A privacy extension disables something, a VPN adds a weird header, an OS patch flips header order, and nobody ever cleans it up. The mess lingers.
Bots? They clean up too well. They set everything by script, and every session looks identical. Perfect Accept-Encoding. Straight-line Accept-Language. User-Agent matches OS, region, browser, all the way down to the build number. But nothing else about the session changes. No history, no scars, no entropy.
The best bots sometimes try to inject a little noise—shuffle headers, randomize a value here and there. But it’s always too regular. Real life isn’t random. It’s inconsistent. Some days your headers match, some days they don’t. Your session might pick up an extension for one page, then lose it after a restart. A bot can’t fake that. It’s why detectors cluster scripted sessions, even when every individual value looks good.
The Story the Headers Tell—And Why It’s Never Just the Values
You start to understand this when you look at real traffic. Here’s what you see: a user logs in from a French IP, Accept-Language is “fr-FR,fr;q=0.9,en;q=0.8.” Next session, same user, now on hotel WiFi, the Accept-Language flips—“en-US,en;q=0.9,fr;q=0.7,” with an odd Accept-Charset thrown in because they installed a third-party IME for work. Sometimes their User-Agent changes because of a forced update. It’s a trail of accidents, not a design.
But in botland? You’ll see ten thousand sessions all matching “en-US,en;q=0.9,” same order, same weights, no quirks. Even if you try to randomize, you end up with the same shuffled patterns—like a casino dealing from a deck that’s never been played.
Let’s dig deeper into what else gets missed. Some headers are context-dependent: Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-User. They change based on how you navigate—a click, a direct URL, a new tab. Real users vary these without thinking. Bots that hard-code miss the subtleties. Detection engines pick up on these clues because they see the same session logic repeated over and over. The absence of entropy is itself a pattern.
Real browser stacks do weird things when you least expect it. An update from Apple or Google can change a header mid-session. A privacy extension you forgot about might inject an X-Requested-With on one request, not another. The entropy isn’t “randomized”—it’s lived.
How Real-World Mess Protects You
At Proxied.com, we don’t fight this—we lean into it. Our mobile proxies route real devices, real user sessions, with headers that have history. Sometimes Accept-Language shifts because the user travelled. Sometimes the order is flipped, or an old extension left a residue. Sessions can carry a French fallback, or a weird Accept-Charset, just because someone once logged in from an overseas airport.
We don’t “simulate” this. We just let the mess happen. If you want to pass as real, you need to look like you’ve been around. The same header set shouldn’t show up twice. There should be scars—wrong header casing, a dropped Accept-Encoding, a mangled Sec-Fetch-Site after an upgrade. That’s what real detection models expect.
The Places Where Bots Get Burned—Anecdotes and Real Fails
Let’s get specific. I watched a pool of 5,000 bots go down in one night. They all copied a Chrome User-Agent, set Accept-Encoding to “gzip, deflate, br,” kept Accept-Language perfect, and routed through a clean mobile proxy. But they all left out Sec-CH-UA-Arch and Sec-CH-UA-Platform, which Chrome 120 started sending after the December update. Detectors didn’t need to look for the values—just the absence.
Another time, a bot farm set Accept-Language to “en-US,en;q=0.9,” but missed that the OS in question shipped with “en-US;q=0.7,fr;q=0.5” as a fallback. The real userbase had at least two languages. The bots all clustered. Session killed, pool burned, months of IPs flagged.
Or there was the time a team tried to match Referer on every navigation, but their scripts fired direct requests—no navigation context, no Referer at all. It’s not about copying the values, it’s about copying the journey. The best header stack in the world can’t save you if the story it tells makes no sense.
One of the sneakiest leaks I ever saw came from Accept-Encoding again. A proxy tool patched “gzip, deflate, br” into every request. But when the browser hit a .woff font or a video resource, the real Chrome changed the header order, or dropped “br” altogether. The bots? Never changed. Detectors watched, clustered every session, and shut down the operation.
Detectors Don’t Need Proof—Just Suspicion
The scariest part is that you don’t always get banned. Sometimes the site just slows you down. Sometimes you’re served stale data, or harder captchas, or stuck in an endless “review” loop. The risk score climbs. The pool gets marked. And you start losing quietly, one session at a time, without ever knowing where you went wrong.
Real users never get this treatment, because their entropy is real. It’s ugly, unpredictable, full of leftovers. Their stack tells a story that fits the site’s model of a person who’s been online for years—not a bot built five minutes ago.
How To Actually Pass—The Long Game
The only way through is to build real life into your stack. Don’t just randomize—replay lived-in headers from real devices. Let the mess leak in. Carry an odd language, let an extension add a weird header for one session, let a header order change after an update. If your Accept-Charset matches your Accept-Language every time, you’re not trying hard enough.
Log your sessions. Look for repetition. If you see clusters, break them. Don’t be afraid to look a little weird. It’s better to be off by one than to be the same as a thousand other bots.
Sometimes, it’s about going slow. If you rush to patch every new header or spoof every latest value, you’ll always be behind the curve. Let your sessions breathe—let them get stale, let them carry the quirks of real use.
📌 Final Thoughts
The bot game isn’t about being invisible anymore. It’s about looking like you belong. Perfection is a dead giveaway. Mess is the only defense.
At Proxied.com, we’ve learned that chaos isn’t a flaw—it’s the feature. If you want to survive, don’t build a clean stack. Build a messy one. Build a story that only a real user could tell.
Let your headers fight for you—not because they’re perfect, but because they’ve lived.