I’ve been watching the World Cup since 2006. This is my sixth one. I still remember Maxi Rodriguez’s volley against Mexico in extra time, the kind of goal that makes a 10-year-old fall in love with football permanently. I remember the horror of watching Asamoah Gyan hit the crossbar against Uruguay in 2010, and the silence in the room afterwards. The pure joy of watching Germany win it in 2014, Götze’s chest-and-volley in the final. And the 2022 final, which I maintain is the second single greatest football match ever played, behind the Italy vs Germany semi-final in 2006.
I watch every game, or at least I used to watch every single one when my bills were paid by my elders. Including the ones most people skip, like the third group stage matches where both teams are already eliminated, the 0-0 between two defensive sides at 2am or the “small” nations duking it out. Real ones know these are often the most entertaining. There’s something beautiful about football when the pressure is off and players just express themselves.
As a kid, this was simple. Every game was on one cable channel. You turned it on, football was there. But football broadcasting in 2026 is a mess. Rights are split between five different platforms per country, each wanting a separate subscription. Group A is on this service, Group B is on that one, and the knockout rounds are on a third thing you’ve never heard of.
I ran into this problem head-on during the group stage. Uruguay vs Spain, last matchday, 2am local time. I wanted to watch it. In Germany, where I live, MagentaTV had exclusive rights to that particular game. Of course, a subscription is required. I had no interest in paying that, especially considering the other fees I am paying for streaming, so I went into an adventure to find a legal free stream.
After a solid 40 minutes of searching, tab-switching, and trial-and-error with different country servers on my VPN, I found that RTVE (Spanish public television) was broadcasting all Spain games free on their website. Connected my VPN to a Spanish server, opened rtve.es, and there it was. Free, legal, public broadcast.
The game itself was quite boring, except for the fact that the Uruguayans seemed intent on physically destroying every Spanish player they could reach.
But sitting there at 2am, having spent almost as long finding the stream as watching the first half, I thought: this process is ridiculous. The information exists — which countries broadcast which matches for free — but it’s scattered across dozens of broadcaster websites, each in their own language, buried in JavaScript-heavy pages or PDF schedules. Nobody has consolidated it.
I could build this. A simple lookup tool: pick your match, see which countries show it free, VPN in, watch.
So the next morning I started building VPNKickoff.
Starting point
The initial plan was straightforward. Two layers of data: match schedule (easy, APIs exist) and broadcaster-per-match mapping (hard, no API exists, that’s the whole point).
For the schedule, I first tried openfootball, an open-source project that publishes World Cup fixtures as JSON. It was clean and public domain, but when I checked the knockout bracket, half the teams were still placeholders (“3A/B/C/D/F” instead of actual countries). The group stage was still finishing. ESPN’s free scoreboard API turned out to be much more current e.g Germany vs Paraguay was already resolved there while openfootball still showed a placeholder. So I switched. No API key needed, just a public endpoint that returns every match with teams, times, venues, and live scores.
The broadcast mapping was the actual work. I started with the obvious ones like ServusTV and ORF in Austria, ZDF and ARD in Germany, SBS in Australia. ServusTV publishes a full schedule page listing exactly which matches they have live vs which ones ORF covers. That was my template: go to each broadcaster, find their equivalent page, extract the match-by-match data.
Some were easy. SBS Australia has all 104 matches. RTÉ Ireland confirmed all 104 on their press release. HRT Croatia, all 104. These go on the “all matches” pile.
Others required more digging. RAI in Italy has only 35 matches free (the rest are on DAZN). I found the specific list via an Italian sports finance site citing Calcio e Finanza. M6 in France has 54, but their knockout picks won’t be announced until the group stage ends. Same for BBC and ITV in the UK.
And some led to dead ends. RTVE (Spain), the same channel that saved my Uruguay game, turned out to have only 17 matches total. All group stage and Zero knockout games. For knockout football in Spain, you need DAZN. So RTVE got removed from the knockout tool entirely.
What changed along the way
The biggest pivot was realising that many broadcasters guarantee coverage of their national team’s matches regardless of the specific schedule. Telefe in Argentina broadcasts all Argentina games. Azteca 7 in Mexico broadcasts all Mexico games. Instead of hardcoding these into the match data (which would break as the bracket resolves), I added a `follows_team` rule — the app dynamically attaches the channel whenever that team appears in a match. As Argentina advances through the bracket, Telefe automatically shows up on their cards.
The design went through iterations too. I started with a dark theme and simple cards, then commissioned a proper redesign with per-round color coding from the Trionda match ball palette (sky blue for Round of 32, green for quarter-finals, red for semis, gold for the final). The header went dark to match the “VPN tunnel” vibe, with the pitch image visible behind a dark scrim. The broadcast list became collapsible — showing flag previews and “Free in 15 countries” as a summary, expanding to a full channel grid on click.
I also added filters by country and language. If you know you have a VPN server in Ireland, you can filter to just see Irish channels. If you want Portuguese commentary, filter by language and CazéTV pops up for every match.
Where it stands now
I have assembled about 30 channels across 24 countries, covering 16 languages. Every knockout match has at least 13 free streaming options verified. The schedule updates automatically every 5 minutes from ESPN. The broadcast data is a single JSON file that I can edit and redeploy in under a minute.
Countries covered: Australia, Austria, Belgium, Brazil, China, Croatia, Denmark, France, Germany, Indonesia, Ireland, Italy, Mexico, Netherlands, New Zealand, Norway, Romania, Singapore, South Africa, Sweden, Turkey, United Kingdom, Vietnam, Argentina.
What’s still missing: BBC/ITV (UK) and M6 (France) haven’t published their knockout picks yet; both announce today now that the group stage is over. I’ll add those within 24 hours. The Nordics (Sweden, Norway, Denmark) are partially mapped. A few partial broadcasters (SABC South Africa, TVNZ New Zealand) need their specific knockout match lists confirmed.
## Deployment and distribution
The whole thing is a static Vite + React app. No backend, no database. It fetches ESPN client-side, reads the broadcast JSON from the same server. Deployed on Vercel’s free tier. You push to GitHub, auto-deploys in 30 seconds. Live at vpnkickoff.vercel.app.
I tried to share it on Reddit but it turns out every relevant subreddit (r/soccer, r/worldcup, r/cordcutters) has strict rules against self-promotion and VPN-related content. Fair enough. The irony of building a tool to make information more accessible and then not being able to tell anyone about it is not lost on me.
So here it is instead. If you’re watching the World Cup and don’t want to pay for five different streaming subscriptions, have Magenta all up in your videos, dancing, this might save you the 40 minutes I spent that night searching for Uruguay vs Spain.
Check it out: https://vpnkickoff.vercel.app
Source code: https://github.com/EdouardGTME/vpnkickoff
If you spot a wrong channel, a dead stream URL, or know of a broadcaster I’m missing — email me at edouardgermain96@gmail.com or open an issue on GitHub. The knockout stage starts tomorrow. South Africa vs Canada, first ball 21:00 CEST.
We will be there, no matter what.
Leave a comment