D
P
0
← All articles Baca dalam Bahasa Indonesia

JavaScript, DOM & Browser Animation

Every Vimeo Host Fails TLS With `certificate has expired`: The Certificate Belongs to `internetpositif.id`

· · 8 min read
Every Vimeo Host Fails TLS With `certificate has expired`: The Certificate Belongs to `internetpositif.id`

Jumping to a chapter in the Vimeo player has been on my list of unverified items since the notes of 21 September, with the cause written right next to it: player.vimeo.com answers with a wrong certificate from this network, because Vimeo is blocked here. The work list rewritten on the afternoon of 21 September planned to try that chapter jump on staging from a network that can reach Vimeo. Two days later, the state notes from the morning of 23 September still said the chapter jump had never been seen working.

The error itself is on record in the notes of 12 October: every Vimeo domain refused connections from my machine with certificate has expired. A day later, on 13 October, while checking the start time parameter for the member item page, every Vimeo host again refused TLS from my work laptop. During that check, I read the subject of the certificate those hosts were presenting.

All five hosts present an internetpositif.id certificate

Five hosts are on record: player.vimeo.com, api.vimeo.com, vimeo.com, help.vimeo.com, and developer.vimeo.com. On my network, all five present a certificate for internetpositif.id, the Indonesian government's Internet Positif filter. The subject was read with openssl s_client piped into openssl x509 -noout -subject. My notes keep that command in a shortened form, so the block below also carries the general form for anyone who wants to repeat the check on their own network:

# as recorded:  openssl s_client ... | openssl x509 -noout -subject
# general form to repeat it, one host per command:
openssl s_client -connect player.vimeo.com:443 -servername player.vimeo.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject

A plain HTTP request to the Vimeo hosts fails the same way, with the message certificate has expired. That message only reports which check failed, without saying whose certificate was being checked. It steers attention toward dates, such as a laptop clock that has drifted or a Vimeo certificate renewed too late, when the rejected certificate was never meant for Vimeo in the first place. My notes on this block don't record the validity dates of that internetpositif.id certificate, so I can't say exactly what made the message come out as an expiry.

There is one point of comparison on the same network: fast.wistia.net and api.wistia.com are reachable. A drifting laptop clock usually breaks many sites at once, not a single provider.

Why the certificate check is not bypassed

If the certificate that arrives belongs to the filter, the connection ends at the filter, not at Vimeo. My notes put it as a conditional: bypassing the certificate check would only fetch the filter's block page. The 12 October notes also say, in the same sentence as the certificate has expired refusal, that the check was not bypassed.

The cost goes beyond one feature. A Vimeo recording, on staging or locally, cannot be played or checked from this network. The Vimeo path of the transcript import script cannot reach Vimeo's API from here either. My notes state that without saying the import was ever run against Vimeo's API, and api.vimeo.com is indeed on the list of five hosts above. Playback can only be checked by someone outside the filter, or over a VPN that gets used only when I decide so.

Vimeo facts from archived copies and GitHub

Because help.vimeo.com and developer.vimeo.com are blocked too, Vimeo's documentation can't be opened directly. On 12 October, I read help.vimeo.com content through search results. Alongside the 13 October finding, a rule went into my notes: facts about Vimeo are read from official sources that are still reachable. The first is web.archive.org copies of help.vimeo.com. The second is Vimeo's own GitHub repositories, namely the vimeo/player.js README, api.yaml in vimeo/openapi, and vimeo/vimeo.js, read through raw.githubusercontent.com or api.github.com.

Those two sources also supply Vimeo's official URL shapes. Vimeo's official start time parameter is #t=1m2s at the end of the player URL, according to the help.vimeo.com article "Start playback at a specific timecode" and the "About Player Parameters" table. I read those help.vimeo.com pages through web.archive.org copies dated 26 September and 10 October 2026. According to the official vimeo/player.js README on GitHub, unlisted Vimeo videos use ?h=<hash> in the player URL.

The API was read the same way. The track list endpoint, GET https://api.vimeo.com/videos/{id}/texttracks, comes from the official vimeo/openapi spec on GitHub for API version 3.4. According to that spec, each track has a link to the text file, a language, a type of captions or subtitles, and active. What couldn't be confirmed is the shape of the response, a plain array or one wrapped in data, because Vimeo's developer site is blocked. So the code accepts both. The idea, as a minimal example that is not the project's code:

// minimal example with neutral names, not the project's code
const tracks = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : []

The sample responses stored for tests were also built from the official schema rather than recorded from real responses, and they are noted to be replaced with real ones once the client's account is available.

Only the URL can be checked

For any change that touches Vimeo, the recorded rule is short: from this network, the change can only be checked as the URL the code builds, and that limit has to be stated in the report.

The project's embed URL function takes a provider, a video ID, and a start time option. It writes #t=4m5s for Vimeo and &time=245 for Wistia. Both point at the same second, since 4 × 60 + 5 = 245. Lined up against the official shapes above:

official Vimeo, start time    ...#t=1m2s      help.vimeo.com, via archived copies
official Vimeo, unlisted      ...?h=<hash>    vimeo/player.js README
from the code, Vimeo          ...#t=4m5s
from the code, Wistia         ...&time=245

The fix for unlisted Vimeo videos, which replaces %2F in the URL with ?h=, was done the same way. It couldn't be tried directly because Vimeo is blocked, and its basis is the official URL shape.

The tool I used to check start times, start-time-shot.cjs, is built around the same limit. The comment at the top of the script says Vimeo playback can't be checked from this network because it is blocked, so the URL is what gets read. Here are three lines from that script:

// collect console error messages
page.on('console', (message) => message.type() === 'error' && errors.push(message.text().slice(0, 160)))
 
// the player URL is the src attribute of the first iframe
player: document.querySelector('iframe')?.getAttribute('src') ?? null,
 
// printed error count, leaving out messages that contain "vimeo"
errors: errors.filter((text) => !/vimeo/i.test(text)).length

That last line matters when reading the results. Messages containing the word vimeo, in any letter case, are not counted, so a zero error count from this tool says nothing about Vimeo.

Wistia as a stand-in, from a fresh browser context

The Wistia side can be checked for real, and those checks run fine in headed Brave. The stand-in is a public Wistia video. To see the player during a local check on a production build, one open library item was temporarily given that video through psql at 19.52. Two minutes later, at 19.54, the item was restored exactly to its original values, including the same updated_at.

The result covers 18 views: three languages (EN, FR, ES) at two widths (390 and 1440) with three URL variants, 3 × 2 × 3 = 18. With ?t=245, the iframe URL ends in &time=245, and the Wistia player stops at second 245 in a paused state. Without t and with t=4:05, the URL has no time. Every view returned status 200, with no sideways scrolling and 0 console errors, keeping in mind that this count already leaves out messages mentioning Vimeo.

One Wistia behavior makes the order of checks part of the result. After the video has been opened at 4:05 once, a visit without t resumes from 4:05, while a fresh browser context starts from 0. My notes call this Wistia's resumable feature for videos of 5 minutes or more, without recording where that 5 minute threshold comes from. That is why start time checks use a fresh browser context. In an old context, a player already sitting at 4:05 could read as proof that the start time parameter works, when the position actually comes from the player's memory.

What is still unproven

The block itself has not gone away. What was put in place is a way of working around it, and the Vimeo side has never been verified through playback. The plan to try a chapter jump from a network that can reach Vimeo has been written down since 21 September, yet the work list rewritten on the evening of 13 October still notes that Vimeo playback cannot be checked from this network. My notes contain no test over a VPN or by someone outside the filter. On the same list, the transcript import on staging only runs once the client's Vimeo or Wistia token exists.

When every host of one provider fails with a certificate error while other providers on the same network are fine, the certificate subject is worth reading before touching the laptop clock or turning verification off. On a filtered network, the name on that certificate tells you more than the error message does. After that, the job comes down to separating what can be proven from your own network from what can't yet, and writing that boundary into the report.