Russia reachability test rig

One IP (135.125.239.97), one document root, four arms. Mobile data only — Wi-Fi off, no VPN. Tap each link and note whether it completes, hangs, or errors instantly.

Arm A — sslip.io hostname (Let’s Encrypt)

135-125-239-97.sslip.io

Arm B — OVH hostname (Let’s Encrypt)

vps-85c8a526.vps.ovh.net

Arm C — plain HTTP to the IP (no TLS, no SNI)

http://135.125.239.97

Arm D — HTTPS to the IP (TLS, no meaningful SNI)

https://135.125.239.97 — self-signed. The browser will warn; tap through it once, then the rest load normally.

Arm G — real domain, straight to the OVH box (no CDN)

direct.bravoapp.cc — the control arm. Same server and same files as arms A and B, but a real registered domain. If this works where arm A stalls, the hostname was the problem.

Arm F — same box, behind the Cloudflare edge

cf.bravoapp.cc — identical origin and files, proxied by Cloudflare. If arm G stalls and this works, a CDN in front is mandatory.

A second tap on the same file may be served from Cloudflare’s cache rather than the origin. Use the buttons below for a guaranteed cold fetch.

Arm H — Cloudflare R2 object storage

r2.bravoapp.cc — decides whether map tiles and user photos can live in R2.

Arm E — WebSocket, held open (tap and wait)

A file download finishing tells you nothing about whether a long-lived connection survives. Supabase Realtime holds one of these. Tap a button and leave the screen on — it runs for 120 s and counts keepalive frames.

Upload — 256 KB POST

The app’s critical path is an upload: a GPS track is 252 KB at p95, photos are bigger. Uploads cross DPI differently from downloads.

Reference

What to watch for. A block on Russian mobile looks like a stall, not an error: the download starts, reaches some byte count, and then sits there forever. If that happens, note roughly how far the progress bar got — that number is the whole point of the exercise. An instant “can’t reach” error means something different from a freeze.