Captive Portal Detection

What are Captive Portals?

A captive portal is what we call a network that requires your action before it allows you to connect to the Internet. This action could be to log in using a username and password, or just to accept the network’s terms and conditions.

There are many different ways in which captive portal network might attempt to direct you to the captive portal page.

  • A DNS resolver that always resolves to the captive portal server IP

  • A gateway that intercepts all HTTP requests and responds with a 302/307 redirect to the captive portal page

  • A gateway that rewrites all/specific HTTP responses

    • Changing their content to be that of the captive portal page

    • Injecting javascript or other content into the page (Some ISPs do this when the user hasn’t paid their internet bill)

  • HTTPS requests are handled differently by captive portals:

    • They might time out.

    • They might present the wrong certificate in order to redirect to the captive portal.

    • They might not be intercepted at all.


The CaptivePortalService controls when the checks are performed. Consumers can check the state on nsICaptivePortalService to determine the state of the captive portal.


    • The checks have not been performed or have timed out.


    • No captive portal interference was detected.


    • A captive portal was previously detected, but has been unlocked by the user. This state might cause the browser to increase the frequency of the captive portal checks.


The Captive portal service uses CaptiveDetect.sys.mjs to perform the checks, which in turn uses XMLHttpRequest. This request needs to be exempted from HTTPS upgrades, DNS over HTTPS, and many new browser features in order to function as expected.


CaptiveDetect.sys.mjs would benefit from being rewritten in rust or C++. This is because the API of XMLHttpRequest makes it difficult to distinguish between different types of network errors such as redirect loops vs certificate errors.

Also, we don’t currently allow any redirects to take place, even if the redirected resource acts as a transparent proxy (doesn’t modify the response). This sometimes causes issues for users on networks which employ such transparent proxies.


pref("network.captive-portal-service.enabled", false); // controls if the checking is performed
pref("network.captive-portal-service.minInterval", 60000); // 60 seconds
pref("network.captive-portal-service.maxInterval", 1500000); // 25 minutes
// Every 10 checks, the delay is increased by a factor of 5
pref("network.captive-portal-service.backoffFactor", "5.0");

// The URL used to perform the captive portal checks
pref("captivedetect.canonicalURL", "");
// The response we expect to receive back for the canonical URL
// It contains valid HTML that when loaded in a browser redirects the user
// to a support page explaining captive portals.
pref("captivedetect.canonicalContent", "<meta http-equiv=\"refresh\" content=\"0;url=\"/>");

// The timeout for each request.
pref("captivedetect.maxWaitingTime", 5000);
// time to retrigger a new request
pref("captivedetect.pollingTime", 3000);
// Number of times to retry the captive portal check if there is an error or timeout.
pref("captivedetect.maxRetryCount", 5);

Connectivity checking

The connectivity checker might use some of the captive portal URLs. Refer this page for more details.