Aleksa Insajder
a one-page brand site with a streams page and an admin panel
A sports creator with an audience on YouTube and TikTok (the site also lists an Instagram account with 94,000 followers, marked as closed) needed one page that explains the brand fully, and another one he fills himself from his phone. Underneath sit an admin panel on a separate subdomain, in-house statistics without cookies and a cache that takes care of itself, so nobody ever has to do a hard refresh. All in custom PHP code, with no platform and no monthly subscription.
- One-page website
- Serbia
- August 2026
- 14 min read
- PHP 8.5
- SQLite
- Service Worker / PWA
- nginx
- Customer
- Aleksa Insajder (Aleksa Dinić)
- Industry
- Sports content and video production
- Area
- Serbia, online
- Brand since
- July 4, 2021
- Type
- One-page brand site with a streams page and an admin panel
- Technologies
- PHP 8.5 · SQLite · Service Worker · nginx
- Website
- aleksainsajder.rs
- Status
- Live site, actively maintained

01 / 11
Who Aleksa Insajder is
Aleksa Insajder is the brand of Aleksa Dinić, a sports content creator from Serbia. He publishes video stories from the world of sports on TikTok and YouTube, and there is also a Discord community around the brand. The site is neither a store nor a service catalog — it is the place where the brand stands as a whole, with its story and principles, and the place social media sends the audience to when they need more than a single video.
The brand has existed since July 4, 2021, and that date is written out on the site, in the section about the brand. The line that carries it is “Who did? God did.”, and above the heading stand three words: truth, character, discipline. This isn't filler text someone added — it is what Aleksa wrote about his own work, and the site is built around it.
What he publishes is described in his own words: authentic stories from the world of sports, good deeds by athletes, faith and character, and content meant to push young people not to give up. That comes with a promise of verified information and quality over quantity. These are his claims about his own work, and that is how they are presented here.

02 / 11
What needed solving
The brief wasn't “make a digital business card”. There were four hard requirements that had to work together:
- One page that reads in a single scroll — the audience comes from phones, through the link in the profile bio, and stays exactly as long as it takes to understand who he is and what he does.
- A streams page the owner fills himself — without us, from his phone, during a break, while the match is on.
- Visitor measurement without third parties — the owner wants to know what people actually watch, without setting cookies on visitors to find out.
- A site that survives a bad connection — traffic comes from mobile networks and arrives in waves, as soon as something is posted.
Along with that came the legal pages, a cookie banner and one rule that shaped the rest of the architecture: everything that gets published must be editable from the panel, never from the code. A site whose owner has to call a developer to change one card is a site that stops changing after a month.
There was also a part you don't see, which later took up the most time: the panel has to work exactly when it's needed, and that is usually Saturday night. Anything that can quietly fail at that moment — the session, the cache, an external source — will fail right then.

03 / 11
One page and its running order
The home page is a single page, but not a single block of text. It goes in this order: a hero with the brand name and the line that carries it, three social icons below it (YouTube, TikTok, Instagram), then two buttons — the Live streams button and Learn more button.
Then comes My story, which says why the brand exists and since when. Then My approach, three cards with principles: originality, quality, discipline. Then a Viktor Frankl quote as a breather, and finally the contact section.
The navigation consists of anchors to those sections, plus two links that lead off the home page, to the streams and to Discord. Next to “Streams” in the navigation there is a pulsing red dot, the same one used for the live badge. It is the only element on the site that is always moving, and it is the only one on purpose.
The contact section is a four-field form, plus four channels and an email address. A successful submission counts as a goal in the statistics — so the panel shows not only how many people visited the site, but also how many of them wrote a message. The cookie banner is shared across our whole fleet of websites, but here it was matched to the brand red, because it was the only thing on the page outside the palette.
04 / 11
The streams page — what it is and what it isn't
The second part of the site is the Streams page. It holds cards with the content Aleksa is featuring at that moment, at most ten at a time, in a layout that adapts itself to the number of cards: one card spans the full width, two go side by side, four form a square, and more than that go into three columns. When there's nothing, an empty state appears with a message to check back later — which, realistically, is the state the page is in for most of the week.
What matters here is what the site does not do, because that was a deliberate decision made at the start. The content isn't on our server. The site embeds publicly available third-party sources, and the page itself carries a clear notice about this, along with a statement that copyright belongs to the content owners and an invitation for anyone who believes something was posted without authorization to get in touch, with a commitment to remove it.
The site's data holds only the address, the title and the time it was added. The server doesn't download, store or rebroadcast anything. The idea of building a proxy that would fetch and rebroadcast the content through the server was considered once and rejected — such a server becomes a distributor of someone else's material, with everything that comes with it, and on a machine that hosts several dozen other client websites. The risk was nowhere near worth the feature.
That decision also has a technical consequence: since the server never requests a pasted address itself, there is no class of attack in which an input field is used to make the server open something on its own network. The link type is recognized by reading the text of the address, without a single request.
05 / 11
Click-to-play: zero requests before the click
The first version of the page loaded the embedded content as soon as it opened. It worked, but it had two problems that only show up with live traffic: the page waited for third-party scripts before it could render, and the visitor got whatever those pages bring along — in two minutes of measurement, twelve empty tabs opened, pop-unders launched by the source itself.
That's why we introduced click-to-play. Until the click, the card shows our black poster: a live badge, the title and a red button. Verified by measuring network requests — before the click, not a single request goes to third parties. The player loads only when the visitor clicks. As a result, the first page opens quickly and, more importantly, nobody gets third-party scripts until they ask for them.
Where the source tolerates it, the embedded frame gets restrictions that block pop-ups. This can't be applied globally, because some sources refuse to display in a restricted frame, so there is a list of domains for which the restriction is switched on. After that change, twenty seconds on the page didn't open a single new tab.
The title sits over the poster, below the badge, and on phones it caused the most trouble: it was tied to a fixed font size, so a long title on a narrow card wrapped onto three lines and covered the whole video. Now the size is calculated from the card's width, not the screen width, the text is cut off at two lines, and long titles are shortened as soon as they're added. The card also has a full-screen button, and a strip with backup sources when there are several links for the same event.
06 / 11
A panel on a separate subdomain
The panel doesn't live at the main site's address but on a separate subdomain, with its own server configuration. It shares the same directory with the site, so whatever the panel writes, the site reads immediately, but only one script runs on that subdomain — everything else returns a 404. The page carries a no-index and a no-cache directive, because a cached panel can show a list from an hour ago and convince the owner that his change didn't go through.
Login uses a password that isn't stored in readable form, every write carries a token proving the request comes from the panel, and the origin of each request is checked as well. On top of all that sits a counter of attempts per IP address.
That counter was also where a real hole was found. It worked on a read — modify — write pattern, with no locking. Five parallel attempts all read zero before any of them wrote its entry, so the limit of five attempts in fifteen minutes didn't exist in practice. It was fixed on two levels: the counter now holds an exclusive lock on its file for the whole cycle, and the attempt is recorded before the password check and deleted only when the login succeeds. Below that there is also a server-level limit that counts only login attempts, not ordinary visits to the panel. Verified: fifteen parallel attempts, nine rejected at the server, and of the remaining six, exactly five logged as a wrong password and one as an exceeded limit.
A log of failed logins with the reason was added too — wrong password, exceeded limit, expired session — so the next time an “I can't get in” message arrives, the answer is looked up in the log instead of guessed. The retention period is ninety days, and that is described in the privacy policy, because the log contains the IP address and the browser identifier.
07 / 11
Three faults that looked like one
In mid-August a report came in that sounded like a single problem: sometimes the panel wouldn't let you in at all, and the site was showing streams from several days earlier. There were three independent causes.
First. Sessions were kept in a shared system directory that the operating system cleans every thirty minutes, deleting everything older than about twenty minutes. The application believed its sessions lasted two hours — that setting meant nothing. The result: constant logouts in the middle of work, and on the next login a message about an expired request, which looks as if the password doesn't work. Sessions were moved to their own directory outside the reach of that cleanup, with the application's own garbage collector switched on — because the default installation keeps it off, counting on a system job to do the work.
Second. Opening the panel fires off three parallel requests, and one of them synchronously checked whether the external sources were available. PHP holds an exclusive lock on the session file until the request ends, so when a source was slow, all three requests queued behind that lock and the panel looked dead — only to fix itself once the cache warmed up. The fix: the session is closed for writing as soon as it's confirmed that the user is logged in, the timeouts were shortened, and a source failure is remembered for five minutes.
Third. A card never removed itself. A finished match stayed on the site until the owner deleted it by hand — that was literally the “streams from several days ago”. First an expiry was introduced, then the owner chose the opposite: no expiry, but a clear age label. The panel now shows how much time has passed since a card was added, and after twelve hours the label turns yellow with a note to check whether it's still current.
On top of all that, the cache in the browser kept the old list even though the server explicitly asks for that page not to be cached — that header isn't honored in this cache. The page now always goes to the network.
08 / 11
A site without CSS and a cache that heals itself
A separate report, which kept coming back now and then for months: the site sometimes loaded bare, without styles. It looked like a server fault. The server was innocent — its log showed twelve hundred successful responses for the stylesheet and not a single error.
The root cause was the service worker in the browser. It kept a copy of the stylesheet under the address /css/style.css, while the page requested /css/style.css?v= with a version number. URL matching is exact by default, so there was never a match — which meant the stylesheet depended entirely on the network. One hiccup in the mobile connection and the page was left without styles until the next refresh. It happened most often right after changes, because that's when the version number changes, so the browser has no copy either.
The fix works in three steps: a cache miss is first retried once after half a second; if that fails too, it takes the previous version of the same file from the cache instead of an error; only then does it give up. Old versions clean themselves up, but the copy without a version number is deliberately kept as a last fallback for a first visit without a network. Verified with the network switched off: the page still has its full styling and its own font.
Server-side caching was cleaned up as well. Pages are no longer kept in the browser for an hour but are always revalidated, while the server-side cache of rendered pages keeps working underneath. Static files are cached for thirty days as immutable, because their addresses carry a version number — and they now send a single caching header instead of two overlapping ones. The goal was simple: no visitor should ever be told to do a hard refresh.
09 / 11
Statistics without cookies
The owner wanted to see what people actually watch, but without setting cookies on visitors. That's why the site has its own anonymous statistics, built into the same panel as the streams.
The data is kept in an SQLite database on the server. A visitor is recognized by a shortened fingerprint calculated from a monthly salt, the IP address and the browser identifier — without cookies, and the salt rotates monthly, so a visitor can't be tracked over time. The collector accepts only POST, always returns the same empty response, is limited in size and number of requests, and accepts only known paths. A stream label is counted only if that stream really exists in the site's data, so made-up names can't be injected from the browser.
The panel shows who is online right now and on which page, then six key numbers — page views, visitors, visits, watch time, peak concurrent viewers and the number of form messages. Below that come an hourly or daily chart and tables: pages, traffic sources, countries, devices, browsers. A separate table measures viewership per stream.
The details that keep this part standing: the country is read from a local database on the server, without a single external service; watch time is written conditionally, so two open tabs don't count the same time twice; and a snapshot of the current peak is taken at most every ten seconds, because during a big match writing on every heartbeat would become a bottleneck.
Google Analytics also runs on the site, but with consent handled under Consent Mode v2 with denial as the default — no cookies until consent is given. Along the way, the privacy policy was corrected, because for a while it claimed that no third-party analytics was used while it was already on the site. Text that doesn't match what the code does is worse than no text at all.
10 / 11
SEO, legal and security
The home page title was cut down to just the brand name, because searching by name is the only way people get here — a five-word addition was only wasting space in the search result. The site carries structured data for Person, Organization and WebSite, with the brand's founding date and links to all its channels, so search engines see the site and the profiles as one entity, not as four unrelated things.
In robots.txt there are also rules for language model crawlers, and the site also has a security.txt file with an address for reporting security findings. CAA records are set for the certificate, the site is on the browsers' HTTPS preload list, and in a test in May 2026 it received the top grade for its connection setup.
The same pass turned up a finding you can't see in the code: the domain had an IPv6 address, but the server was listening only on IPv4. Visitors coming over IPv6 got someone else's default certificate and a warning that the connection wasn't secure — to them, the site looked hacked. It was fixed by adding an IPv6 listener, so both addresses now respond with the same certificate.
Sensitive parts are locked down at the server level and verified with an actual request, not an assumption: the data directory, the credentials file, the internal code and file backups — all of them return a refusal.
The legal part consists of two pages written for this site, not copied: a privacy policy, which covers both the internal statistics and the security log, and the Terms of Use, which state what the site is, what is allowed, and the limitation of liability for content from external sources.
11 / 11
What the client ends up with
A site that is his. The code and data sit on the server and can be copied and moved; there's no monthly platform subscription, no plugins that fall apart at the next update, and no third-party service to pay just to see the traffic.
In practice, a day looks like this. When he wants to feature something playing that evening, Aleksa opens the panel on his phone — it's installed as an app on the home screen — pastes a link or picks one from the list, and the card is on the site. When the match is over, he deletes it, or leaves it until the panel turns it yellow with a reminder to check whether it's still current. When he wants to see how it went, he opens the same panel, one tab over.
The site is still actively maintained. It has been through several passes, which show up in this text as fixed faults: sessions, cache, login, speed, source restrictions. The external source addresses are deliberately kept in the settings rather than in the code, because such things shut down and change without notice — when that happens, one line changes and nobody has to build a new site.
All changes go straight to the live site, with a backup before each one, and with the cache cleared and the PHP processes reloaded right after, because otherwise the owner would be served the version he had just replaced for up to an hour. That's a lesson from this project that is now applied to every site on that server.
On a phone
The same site, in your hand.
Most visitors come from phones, so the layout, buttons and images are arranged for the small screen first.

Questions
What clients ask.
Are the streams stored on your server?
No. The cards on the streams page embed publicly available third-party sources, and the site's data holds only the address, the title and the time it was added. The server does not download, store or rebroadcast any video. The page itself carries a notice that copyright belongs to the content owners, inviting anyone who believes something was posted without authorization to get in touch.
Why doesn't the server fetch the content on the visitor's behalf?
Because it was a deliberate decision not to work that way. A server that fetches and rebroadcasts someone else's material becomes a distributor, with everything that comes with it, and the same machine hosts several dozen other client websites. The idea was considered once and rejected.
Can the owner add and delete cards himself?
Yes, from the admin panel, on his phone. The panel is on a separate subdomain and is installed as an app on the home screen, so it opens in two taps. None of this requires changing the code.
Why in-house statistics and not just an off-the-shelf tool?
For two reasons. First, it works without cookies and the data stays on the site's server. Second, it measures exactly what matters here — how many people are watching which stream at the same moment, and for how long — which an off-the-shelf tool doesn't provide without extra setup. Google Analytics runs alongside it, but only with the visitor's consent.
Why one page and not a site with ten subpages?
Because the content lives on social media, and the site has a different job: to say in one place who is behind the brand, what principles it works by and how to get in touch. Ten subpages would mean nine empty ones. The streams page is separate because it does a completely different job and has its own rules.
What happens when an external source stops working?
The source addresses live in the settings, not in the code, so changing one takes a single line. The panel also shows whether a source returns anything at all before the owner tries to add a card — an earlier version of that check counted what the source promised rather than what it actually delivered, so it showed green even when there was nothing.
Related projects
From the same field.

DJ Njace
Klasičan Gejming