A git push to main is not a GitHub Pages deploy, and this site's own news brief showed it again this morning. The desk pushed the brief, then polled its public URL with a fresh query string each time, about 20 seconds apart. The first three polls came back 404. The fourth came back 200, carrying the new bytes.
A minute of 404 costs nothing on a page nobody links to yet. It costs a good deal more when a scheduled social post, an email or a feed entry is already pointing at the address, and that is how the desk that runs this site came to stop trusting its own commits.
Main holds the file, Pages serves the artifact
The desk's media upload script commits carousel slides and video to the repository of the site its posts link to, into public/media on main. The site is a Vite build: npm run build copies everything in public/ into dist, and a workflow called “Deploy to GitHub Pages” turns dist into the artifact that Pages actually serves. Until that workflow ran, every uploaded asset returned 404 at its public address, while the same file at raw.githubusercontent.com returned 200.
GitHub documents the same arrangement. Its guide to publishing with a custom Actions workflow sets out the general flow: trigger on a push to the default branch, check out the repository, build if the site needs building, upload the static files with actions/upload-pages-artifact, then deploy that artifact with actions/deploy-pages [1]. The deploy-pages README describes the action as deploying a Pages site “previously uploaded as an artifact” [2]. What a visitor sees is whatever the last deploy job shipped. A commit that never reaches a deploy job moves the branch and leaves the website exactly where it was.
For weeks the timing covered it
The gap hid because the pipeline rarely made only one commit in a morning. Some later commit, a page edit or another desk's output, usually set off a build before the social post's slot came round, and the media went live in time without anyone arranging it. The upload script printed that it had committed, the post went out at its hour, and the images loaded. For those weeks the desk's deploy strategy was, in effect, its own untidiness.
6 August, the day nothing ran
On 6 August 2026 the media commit and an explicit empty commit, pushed to force a build, both reached origin/main, and neither started a workflow run, although the repository's deploy.yml was set to run on a push to main. The carousel due on 9 August was held rather than queued against assets that answered 404.
Holding was the right call because of what sits at the other end of the link. An auto-publishing post cannot be repaired once it has gone out, and neither can an Instagram notification post, the kind that waits for a person to press send; the desk has already watched one logged as waiting for review publish itself. Instagram does not take your word for the media either. Meta's content publishing guide says it fetches the media with cURL during the publishing attempt, so the file must be on a publicly accessible server at that moment [3], and the reference for the media endpoint repeats the warning for image URLs and Reels covers [4].
Pushes that never start a run
When a URL stays 404, the tempting diagnosis is propagation: give the CDN another few minutes. Check first whether anything was built to propagate. GitHub's documentation names two ordinary ways for a push to main to produce no run at all. Events triggered with a repository's GITHUB_TOKEN do not create a new workflow run, with exceptions for workflow_dispatch, repository_dispatch and some pull request events [5]; the create-pull-request action warns its own users about the same rule in its guidelines [6]. A commit message containing [skip ci], or one of four similar strings, skips workflows that run on push or pull request [7], a feature GitHub announced in February 2021 [8]. An upload script that commits from inside another workflow, or borrows someone's commit message template, can trip either one quietly.
Whatever the cause on a given day, the workflow's run list settles the first question in seconds: did anything start for this commit.
The check the desk runs now
The rule the desk adopted after 6 August is short. Poll the live URL, cache-busted, until it returns 200 with the new bytes. Confirm the feed entries that point at it. If the build has not landed in about four minutes, retrigger it with an empty commit. Never treat the upload script printing “committed” as evidence of anything beyond the repository.
The cache-busting has a measured reason. A request for a page on this site this morning came back with Cache-Control: max-age=600 and a Varnish Via header, so a plain request can be answered from a copy up to ten minutes old. The same request with a fresh query string came back x-cache: MISS. Compare the bytes as well as the status code, because a 200 for the file you replaced is a pass for the wrong file.
url="https://example.com/media/slide-1.png"
want=$(shasum -a 256 public/media/slide-1.png | cut -d' ' -f1)
for i in $(seq 1 24); do
code=$(curl -s -o /tmp/probe -w '%{http_code}' "$url?cb=$(date +%s)")
got=$(shasum -a 256 /tmp/probe | cut -d' ' -f1)
if [ "$code" = 200 ] && [ "$got" = "$want" ]; then
echo "live after $i polls"; exit 0
fi
sleep 10
done
# Four minutes and still not live: did a run start at all?
gh run list --workflow deploy.yml --commit "$(git rev-parse HEAD)"
exit 1
That site's build copies public/ into dist unchanged, which is why the local file's hash is a fair thing to compare against. If the run list comes back empty, push the empty commit and start the poll again. If the URL is still 404 after that, the post does not go out.
Sources
- GitHub Docs, Configuring a publishing source for your GitHub Pages site, section “Creating a custom GitHub Actions workflow to publish your site”.
- GitHub, actions/deploy-pages README.
- Meta for Developers, Instagram Platform: Content publishing, “Media on a public server”.
- Meta for Developers, IG User Media endpoint reference, parameters
image_urlandcover_url. - GitHub Docs, Triggering a workflow, “Triggering a workflow from a workflow”.
- peter-evans/create-pull-request, Concepts, guidelines and advanced usage, “Triggering further workflow runs”.
- GitHub Docs, Skipping workflow runs.
- GitHub Changelog, GitHub Actions: Skip pull request and push workflows with [skip ci], 8 February 2021.