If you’re running systemd-resolved with DNS over TLS (like I detailed in my split DNS post), you’ve probably run into the same annoying problem I have: captive portals just don’t work properly.
The issue is straightforward but frustrating. Your system is configured to use secure DNS servers like Cloudflare (1.1.1.1) with DNS over TLS, which is great for security and privacy. But when you connect to that hotel Wi-Fi or coffee shop network, the captive portal can’t intercept your DNS queries to redirect you to their login page. Your requests bypass their DNS entirely, so you never see the portal.
The Manual Workaround (That Gets Old Fast)
The typical workaround involves giving the Wi-Fi network DNS priority and disabling DNS over TLS:
sudo resolvectl domain wlp0s20f3 "~." && sudo resolvectl dnsovertls wlp0s20f3 no
# Connect to captive portal
# Log in manually
# Revert changes
sudo resolvectl domain wlp0s20f3 "" && sudo resolvectl dnsovertls wlp0s20f3 yes
This works, but it’s annoying for two reasons: you have to remember to revert the changes, and it affects your entire system’s DNS behavior instead of just the browser you need for the captive portal.
Enter captive-firefox
I got tired of this dance and wrote a simple Bash script that handles captive portals elegantly. The idea is simple: launch Firefox in a sandbox that uses the Wi-Fi network’s DNS server directly, bypassing your system’s DNS configuration entirely.
Here’s what the script does:
- Auto-detects your Wi-Fi interface (or you can specify it)
- Extracts the DHCP-provided DNS server from NetworkManager
- Launches Firefox in a firejail sandbox with that specific DNS
- Uses a completely isolated profile – no access to your regular browsing data
The result? Firefox can see the captive portal while your system maintains its secure DNS configuration.
Zero Dependencies, Maximum Convenience
The script requires only standard Linux tools you probably already have:
- Bash
- firejail (for sandboxing)
- nmcli (NetworkManager CLI)
- iw (wireless tools)
- Firefox
No Go binaries to compile, no complex dependencies. Just copy the script and run it.
Usage
Most of the time, it’s as simple as:
./captive-firefox.sh
The script will auto-detect everything and open Firefox pointing to the standard captive portal detection URL. For edge cases, you can specify the interface or target URL manually.
I’ve also included a .desktop
file so you can launch it from your application menu when needed.
Security Considerations
The sandboxed Firefox instance:
- Can’t access your real Firefox profile or data
- Uses only the captive portal’s DNS (isolated from your secure setup)
- Runs in a firejail container for additional isolation
- Automatically cleans up when closed
Your main system’s DNS configuration remains untouched throughout the process.
Get It
The script is available on my GitHub: captive-firefox
Finally, a civilized way to deal with captive portals without compromising your DNS security setup.