I have a Raspberry Pi that is hosting a simple website using nginx. The RPi is acting as a Wireless Access Point - users can connect to its wireless network, the RPi gives them an IP (it runs a DHCP server), and they can access the site.
Because the RPi doesn't actually provide users with internet (only this one site), I have made it easier for users to find the site. Instead of knowing the exact URL for the site, I have told my dns server (dnsmasq) which the DHCP server tells clients to use, to resolve all queries to the LAN IP of my RPi (192.168.30.1).
At this point, my nginx has an entry in its config that says:
- if the host field of the user's request is not MyRPiServer.com, send a 302 redirect to MyRPiServer.com
- if the host is MyRPiServer.com, serve the local website
This works awesome.
I wanted to take it one step further. When Android connects to a wireless network, it tries to connect to http://connectivitycheck.gstatic.com/generate_204 (or one of the other similar google pages) specifically to check if the request is being redirected. If it gets a 204 code, it assumes everything is fine. If it does not, it assumes it is behind a captive portal, and pops up a browser window that opens the captive portal login.
For some reason, when I tell nginx to respond to requests for the generate_204 page with either a 302 redirect, or even a 200 (with some text), Android doesn't popup the browser.
I have a mikrotik router with a built-in hotspot feature, which does indeed have android popup the browser with the captive portal login (on the same test phone). When I look at the traffic it sends my client, it is a simple HTTP 200, with some text, just like mine.
The one thing that does seem to work is if I disable my DNS server from resolving everything to 192.168.30.1, and use iptables to redirect port 80 to localhost on my RPi.
Does anyone know why redirecting port 80 works when it comes to Android's Captive Portal detection, but configuring the DNS server to resolve everything to the RPi local IP doesn't?
Looking at the code found here https://stackoverflow.com/a/14030276/4258196, it appears that the only thing Android cares about is if it can connect to the host, and if it gets an HTTP 204 back. In my case, it's definitely connecting, and it's definitely not getting a 204 back (nginx logs show it sending HTTP 302 and HTTP 200).
My phone is running Android 8, so the code linked might be different now I supposed.
https://gmail.com
, and they get your valid certificate intstead, the browser will still show them a cert warning since that's not the cert it expected. My test phone does have Chrome installed (came pre-installed), but the Captive Portal popup android uses doesn't use Chrome - not sure if that matters. Either way - do you know exactly what Android is looking for when doing paywall detection (with Chrome installed)? – Tal Mar 20 '18 at 17:43