The Browser Is the Bottleneck: Why dApps Can’t Escape HTTP
The chain doesn't care what transport you use. The browser does.
- The Hidden Centralization in Web3: DNS as a Single Point of Failure
- The Browser Is the Bottleneck: Why dApps Can’t Escape HTTP — you are here
- Deterministic IPFS as a Browser-Compatible Discovery Layer
In the last post I made the case that DNS is Web3's hidden single point of failure — that a domain seizure, a CDN outage, or a government-level block can take down a "decentralized" app even when the underlying chain is running fine. A few people came back with the same response:
"Why don't dApps just use libp2p and skip DNS entirely?"
It's a reasonable question. libp2p is the networking layer behind IPFS and several blockchain nodes. It handles peer discovery, connection multiplexing, and transport negotiation. In theory it's exactly the kind of thing that should let you route around DNS.
In practice, you can't run it from a browser tab. At least not in the way people assume.
What the browser will and won't let you do
A browser tab is a sandbox. This is intentional — the browser is trusted with your banking session, your passwords, your local network. If a webpage could open raw TCP or UDP sockets to arbitrary hosts, any site you visit could:
- Port-scan your home network.
- Connect to services on your router's admin interface.
- Relay traffic in ways that look like they're coming from your machine.
So the browser spec deliberately limits what a webpage can put on the wire. From a browser tab, you can make:
- HTTP and HTTPS requests — your standard
fetch()calls. - WebSocket connections (
ws://andwss://) — persistent bidirectional connections that start as an HTTP upgrade. - WebRTC — peer-to-peer data channels, but with a catch (more on this below).
That's it. No raw TCP. No raw UDP. No custom transport protocols.
Every connection a browser-based dApp makes to the outside world goes through one of those three mechanisms. All three ultimately require a server that speaks HTTP or has a publicly reachable address. That server has a domain name. That domain name is in DNS.
You haven't escaped anything. You've just moved one level down.
Why libp2p hits the same wall
libp2p supports a range of transports: TCP, QUIC, WebSocket, WebRTC, and a few others depending on the implementation. When you run a libp2p node on a server or desktop client, you get the full menu.
In a browser, you get WebSocket and WebRTC. The rest don't exist.
WebSocket still requires a remote endpoint with a domain or IP address. You're back to DNS.
WebRTC is more interesting — it can establish direct peer-to-peer connections without a persistent server in the middle. But WebRTC connections don't start peer-to-peer. They start with a signaling phase: both peers have to exchange connection metadata (ICE candidates) through some shared channel before they can connect directly. That channel is almost always an HTTP server. A server with a domain. In DNS.
You also need a STUN server to figure out your external IP and port, and often a TURN server to relay traffic when NAT traversal fails. Both are centralized infrastructure.
So "use libp2p" in a browser means: use WebSocket (still DNS) or use WebRTC (still needs a signaling server and STUN, also DNS). The topology changes, but the DNS dependency doesn't go away.
What most dApps do instead
Because the browser's network constraints are well understood, the ecosystem landed on a pragmatic solution: hardcoded RPC endpoints.
Your wallet or dApp has a list that looks something like this:
wss://mainnet.infura.io/ws/v3/YOUR_API_KEY
https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY
https://cloudflare-eth.com
These are WebSocket or HTTPS endpoints run by third parties — Infura (Consensys), Alchemy, Quicknode, Cloudflare — that speak the Ethereum JSON-RPC protocol. The dApp connects to one of these and uses it to submit transactions, read state, and subscribe to events.
This works. Until it doesn't.
Infura, November 2020. A configuration error caused a multi-hour outage that broke MetaMask for most users. Because MetaMask defaulted to Infura as its RPC provider, and because most users had never changed that default, millions of people effectively lost access to their wallets. The chain was fine. Infura wasn't.
API key rate limiting. If your dApp gets popular, you hit rate limits. You can pay for a higher tier, but now you're paying for access to infrastructure that can cut you off. That's not decentralization — that's a SaaS dependency with extra steps.
Geographic blocking. RPC providers are US-based companies subject to US sanctions law. The same OFAC pressure that took down Tornado Cash's frontend can compel an RPC provider to block connections from sanctioned countries. Your transaction doesn't get submitted. You don't get an error message. You just get a timeout.
Multiple fallbacks aren't enough
The common response to this is: add more endpoints. If Infura is down, fall back to Alchemy. If Alchemy is slow, try Quicknode.
This is better than nothing, but it doesn't solve the underlying problem.
Every fallback endpoint is another domain. Another DNS lookup. Another company that can have an outage, revoke your API key, or respond to a court order. You've gone from one single point of failure to three or four, but they're all the same kind of failure. A coordinated enforcement action or a widespread BGP route leak doesn't care that you have backup endpoints — it takes them all out at once.
More fundamentally: the list of fallback endpoints is hardcoded. Hardcoded means someone has to update it when endpoints change. Hardcoded means a determined censor can enumerate the list and block each one individually. Hardcoded means you're playing whack-a-mole against infrastructure pressure.
The endpoints themselves are the problem, not the number of them.
ENS doesn't escape this either
ENS is often pointed to as the decentralized alternative to traditional domains. And it is genuinely more censorship-resistant at the naming layer. You own the .eth name on-chain. No registrar can revoke it.
But a user typing app.uniswap.eth into Chrome gets a 404. Browsers don't resolve .eth natively. To make ENS names work in a browser, you need a gateway: eth.limo, eth.link, or a local resolver extension.
eth.limo is a domain. It resolves through DNS. It's hosted on CDN infrastructure. If eth.limo goes down, your ENS name doesn't resolve for most users. Decentralization at the naming layer is undone at the resolution layer.
This is the pattern. Every time the Web3 ecosystem adds decentralization at one layer, the browser forces a centralized dependency back in at the transport layer.
What a real fix looks like
The browser constraint isn't going away. It exists for good reasons and no browser vendor is going to open up raw TCP from webpage contexts — the security implications are too severe.
So the question isn't "how do we bypass the browser?" It's "how do we build on top of what the browser allows — HTTP and WebSocket — in a way that doesn't create new central points of failure?"
The core insight: the problem isn't that endpoints speak HTTP. The problem is that the endpoints are static. Hardcoded addresses that can be enumerated, rate-limited, blocked, or seized.
What if the endpoint addresses were dynamic? Computed fresh each time, not hardcoded? What if a frontend could figure out where to connect without asking DNS — by deriving the answer from something that lives on the content-addressed web?
That's what I'll cover in the next post. VoiceBan's backend isn't at a fixed WSS address. The frontend computes where it should be, deterministically, from first principles — and it does it using only HTTP calls to IPFS gateways. Multiple gateways, in parallel, any one of which can fail without breaking anything.
The browser's constraint becomes the design. HTTP in, but no DNS dependency, no hardcoded endpoints, no single point the censor can push on.
References
- Infura outage, November 11, 2020 — configuration error caused a consensus split and left MetaMask and most browser-based Ethereum clients unable to sync: Infura's incident report · The Block coverage of wallet impact
- libp2p browser transports — the js-libp2p implementation supports WebSocket and WebRTC in browser contexts; TCP and QUIC are not available from a webpage: libp2p browser connectivity documentation
- WebRTC signaling requirement — peers must exchange SDP offer/answer and ICE candidates through an out-of-band channel before a direct connection can be established: WebRTC API — MDN Web Docs
- ENS gateway dependency —
eth.limoandeth.linkare conventional domains hosted on CDN infrastructure; browsers do not natively resolve.ethnames: eth.limo · ENS documentation - OFAC sanctions and RPC providers — the same enforcement framework that targeted Tornado Cash applies to US-incorporated infrastructure operators including RPC providers: U.S. Treasury press release JY0916