DEV Community

Cover image for Making a Browser That Slips Past WiFi Captive Portals — and Why This Loophole Isn’t Worth It
Daniel Chifamba
Daniel Chifamba

Posted on • Edited on

Making a Browser That Slips Past WiFi Captive Portals — and Why This Loophole Isn’t Worth It

Here's something fun — a loophole in WiFi captive portals that lets you surf the internet using a special web browser even if you haven't logged in yet. But before you pack up your laptop and head for free WiFi...there’s a catch! 😜

You've seen it before - you connect to a coffee shop hotspot or hotel internet, and you're instantly met with the familiar "Please log in or pay" page. Until you do, every site you visit redirects right back to that portal.

Most people assume captive portals completely block internet access until you've paid up or agreed to the terms.

But there's a small, odd exception buried under the hood... This isn't a "free internet" hack, and it won't stream Netflix, but it's a technical side effect of how the internet's plumbing works.

I built a functioning, text-only web browser that works entirely on top of this exception and I'll show you how it works, why captive portals sometimes allow it, and why you probably shouldn't rely on it for anything important.


If you prefer to jump to checking the code and running the browser, you can find the completed code here: https://github.com/nadchif/dnSurfer


What's Really Happening Behind That Wi-Fi Login Page

When you connect to public Wi-Fi, you're not immediately on the open internet. You're placed inside a restricted network, with a captive portal controlling what traffic gets through. Until you log in or accept the terms, it appears that every request you make is intercepted and redirected back to that same login page.

But here's the interesting part — before your browser can load example.com, it first has to ask a DNS (Domain Name System) server for that site's IP address.

And sometimes, the captive portal doesn't block that lookup step.

  • They assume DNS is harmless ("it's just phone numbers for websites"). 🤷‍♂️
  • Blocking DNS can break their own login page for people using a custom DNS setup.

The result: even though your web requests (HTTP/HTTPS) are locked away, plain old DNS queries can still slip out into the open internet. And because DNS can carry little snippets of text (via TXT records), clever folks have figured out you can string those snippets together and turn DNS into a tiny — but working — data tunnel.

So naturally, I had to build one to see if this actually worked in practice.

Turning DNS Into a Web Browser

The DNS browsing setup requires two parts.

When you type a URL in the DNS Browser, it doesn't make a normal HTTP request. Instead, it:

  1. Encodes the URL into Base64 so it can fit inside valid DNS labels.
  2. Splits the encoded data into chunks small enough for a DNS query.
  3. Sends each chunk as part of a DNS TXT query to your custom DNS server.

The DNS server — running somewhere outside the captive portal — decodes those labels, fetches the actual webpage from the real internet, strips it down to plain text or Markdown, and splits it into small pieces. It sends each piece back as a TXT record. The Electron app (Browser) then stitches those chunks together and renders them in the window.

Overview flow of how system works

This works because captive portals that leak DNS will let these TXT queries and responses pass before login — meaning you can sneak tiny bits of webpage content back to your device.

To see the difference, let's look at the actual network traffic using Wireshark. I visited a page from https://news.ycombinator.com.

Packets captured when we loaded the URL from Chrome showed:

Wireshark screenshot of Chrome packets

While packets captured when we loaded the same URL from our DNS browser showed:

Wireshark screenshot of dnSurfer packets

Notice the difference: our DNS browser relied solely on the DNS protocol (over UDP port 53), while Chrome used TCP connections for HTTPS (TLSv1.2).

The trick is simple: only DNS is used, and DNS is still open. No magic here, folks 😉

Why This Loophole Isn't Worth It

Sure, it's fun, but:

  • Zero HTTPS protection. Everything travels in plain text, so anyone between you and the DNS server can read every page you load. It's basically broadcasting your activity in public, leaving you susceptible to MITM attacks.
  • Bare-bones browsing. Forget forms, images, or anything interactive this is text-only, and even that's slow.
  • Not dependable. Some captive portals already block DNS traffic, and those that don't might close the gap at any time. (The network admins read posts like this too, apparently 😅)

Think of it like this: if your neighbor locks the front door but leaves a small window open, it doesn't mean you should climb in. If you can put together a system like this, you can probably also manage the far simpler task of paying for that coffee or grabbing a Wi-Fi pass. 😉

Treat dnSurfer as a curiosity — a safe way to explore how protocols behave under unusual conditions — not a replacement for normal browsing.

The full source code (including chunking logic, multi-page navigation, and rendering) is in the repo:

GitHub logo nadchif / dnSurfer

A DNS-only web browser — browse even when HTTP(S) is blocked.

dnSurfer Suite

A web browser that surfs entirely over DNS

Preview screenshot

dnSurfer is a proof-of-concept browser that operates entirely over DNS, using a client–server connection to turn DNS queries into text-only web content.

  1. DNS Browser (Desktop/Electron) — A text-only browser that fetches pages entirely over DNS TXT records.
  2. Custom DNS Server (Node.js) — Serves stripped-down web pages as DNS responses.

No HTTPS. No TCP. Just DNS queries slipping past wifi captive portals.


Quick Start – How to Run the Browser

Pre-requisites

Steps

  1. Clone this project
git clone https://github.com/nadchif/dnSurfer.git
Enter fullscreen mode Exit fullscreen mode
  1. Change directory to the cloned folder
cd dnSurfer
Enter fullscreen mode Exit fullscreen mode
  1. Launch Browser
npm run browser
Enter fullscreen mode Exit fullscreen mode

How It Works

Best explained in the blog post I published here


Bundling / Compiling Executable

  1. Change directory to the desktop client folder:
cd desktop-client
  1. Install dependencies
npm install
  1. Build app
  • MacOS
npm run dist:mac
  • Windows
npm run dist:win

.

High-level code breakdown

For those who want the technical details:

Server Side (Node.js):

  • We use the dns2 library to create a UDP server listening on port 53
  • When TXT query requests come in, we extract the Base64-encoded URL from the DNS query
  • Decode the Base64 to get the original URL, then fetch that page using standard HTTP
  • Convert the HTML response to clean Markdown using the turndown library
  • Store the converted content in Redis with a 5-minute TTL for caching
  • Split the Markdown response into chunks small enough for DNS TXT records
  • Return each chunk with metadata like <|1/4|> indicating chunk number and total chunks

Client Side (Electron):

  • Create a minimal browser interface with a URL input field
  • When user submits a URL, encode it to Base64 and split into DNS-safe chunks
  • Use Node.js dgram to create UDP connections for DNS queries
  • Send TXT queries using the syntax: ..dns.me
  • Parse responses to extract chunk metadata (<|1/4|>, <|2/4|>, etc.)
  • Continue querying until all chunks are received
  • Reassemble the chunks, convert Markdown back to HTML using the marked package
  • Render the final HTML in the Electron webview

The whole system essentially turns DNS into a slow but functional HTTP proxy, with the server doing the heavy lifting and the client just reassembling text chunks.


If you're responsible for one of these captive portal setups, this is probably something to look into. While most people won't build custom DNS browsers, it's still an unintended hole that's worth plugging.


If you're into this kind of weirdness, check out:

  • Ch.at (chat with an LLM over DNS)
  • Links (browser in terminal)
  • Gophie (browse in gopher, alternative to http protocol)

Top comments (0)