Handling the Captive Network Assistant for a closed network with a redirect using Apache and Bind

A site I worked with had a requirement for a WiFi network that they wanted customers to connect to and when they opened the browser on their device and went to any non SSL page they would be automatically redirected to the splash page for a media system. The WiFi network did not have access to the Internet. Not a problem I thought, a bit of DNS and Web server config and off we go.


I had not taken into account the Captive Network Assistant functionality used in modern mobile devices.

Captive Network Assistant is Apple terminology but the same mechanism is used in Android and Windows devices.

The Captive Network Assistant is designed to detect networks that require sign in through a captive web portal for access and provide a smooth sign in experience for the user. It is a very lightweight web browser with fairly limited functionality.

As soon as a device was connected to the WiFi network , the Captive Network Assistant would launch and display the media system page. Not the desired result.

When the device connects to a network, it sends a HTTP request. If it receives a redirect and the content it receives is not what is expected, it assumes that it is in a captive portal network and launches the Captive Network Assistant to display what it expects will be the network login page. To stop the CNA from triggering, I would have to detect the initial HTTP request and return a response that would satisfy its desire to reach the Internet. The rewrite engine in the Apache web server would allow me to do this, I just needed to find a pattern to match.

As the URL referenced in the HTTP request changes, I decided to build a rule based on the User Agent string which seems to stay consistent (It is still working after 2 years!).

There are a few moving parts to this solution but they are all housed on a Ubuntu 16.04 LTS server. Instructions on how to setup a Ubuntu server can be found here.

The DNS server Bind is used to provide name resolution for the internal media services and also hosts a fake Root DNS zone file that returns a single IP address for ANY requested host name.

The Apache web server redirects any HTTP request to our media service server and also handles the CNA requests by responding with a 403 Forbidden status code.

Configure your server with an appropriate static IP address and then install Bind and Apache with the following commands:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install bind9
sudo apt-get install apache2

Add the following to the file /etc/bind/named.conf.local

zone "." IN {
type master;
file "/etc/bind/db.fakeroot";

Create a new file in /etc/bind called db.fakeroot and insert the following, substituting X.X.X.X with your servers IP address.

@ IN SOA ns.domain.com. hostmaster.domain.com. ( 1 3h 1h 1w 1d )
* IN A X.X.X.X

Enable mod_rewrite in Apache

sudo a2enmod rewrite
sudo systemctl restart apache2

Add the following into the default VirtualHost section of /etc/apache2/sites-available/000-default.conf, substituting the FQDN or IP of where you wish to redirect clients.

NOTE: If you use an FQDN you will need to host a zone file for that domain in Bind on this server as well with an appropriate A record.

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} ^CaptiveNetworkSupport(.*)$ [NC]
RewriteRule .* - [F]
RewriteCond %{HTTP_USER_AGENT} ^WifiHotspot(.*)$ [NC]
RewriteRule .* - [F]
RewriteRule ^(.*)$ http://content.portal.com/ [R=307,L]

2 Comments on "Handling the Captive Network Assistant for a closed network with a redirect using Apache and Bind"

  1. Why don’t you simply whitelist the URLs (on your network device) that the OS usually hits on to check whether the network is running behind a captive portal or not? .. For example, Apple uses (captive.apple.com) and Android uses (connectivitycheck.gstatic.com) ..


    1. Hi Mohammad

      The main reason why we don’t just whitelist the connectivity check URL’s is that there is no Internet access from the network in question.




Leave a Reply

Your email address will not be published. Required fields are marked *