What a static site pipeline is for

With no compilation and no test suite in the traditional sense, it is tempting to conclude that a static site does not need a pipeline. It does, but for different reasons than an application does. The pipeline exists to answer four questions before anybody sees the result:

  • Is the content valid — does every JSON file parse?
  • Is the markup sound — are there broken internal links or missing images?
  • Has anything regressed — accessibility, page weight, headers?
  • Is what is deployed exactly what was reviewed?

Validate the content layer first

On a configuration-driven site, a single trailing comma in a JSON file breaks navigation across every page, and it does so silently in the browser console rather than loudly at build time. This is the highest-value check in the whole pipeline and takes three lines.

Fail fast on malformed JSON

- name: Validate JSON
  run: |
    set -e
    find config content -name '*.json' -print0 \
      | xargs -0 -n1 python -m json.tool > /dev/null
    echo "All JSON files parsed successfully."

Check links before your visitors do

Internal link rot is the most common defect on a corporate website and the least excusable, because it is entirely detectable. A crawl of the built output that fails the job on any internal 404 removes the class of problem permanently.

Treat external links differently. They fail for reasons outside your control, and a pipeline that goes red because someone else's site is having a bad afternoon quickly trains people to ignore red. Report external failures; do not fail on them.

Note

A check that is allowed to fail spuriously will be disabled within a month. Design every gate so that a failure is always the team's fault and always actionable.

Add the checks that machines are good at

Automated accessibility testing catches roughly a third of issues, which is a poor substitute for manual testing and an excellent substitute for nothing. Run it on a representative set of templates rather than every page — homepage, an article, a listing, a form and a legal page will surface almost everything.

Suggested pipeline gates for a static website
Gate Runs on Blocks merge?
JSON validation Every push Yes
HTML validation Every push Yes
Internal link check Pull requests Yes
External link check Weekly schedule No — report only
Accessibility scan Pull requests Yes, on new violations
Page weight budget Pull requests Yes
Header verification After deployment Alerts on failure

Deploy from a protected branch, with a scoped identity

The deployment job is the only part of the pipeline with production rights, so it should be the only job with credentials, and those credentials should be federated rather than stored.

Scoping the deployment job

deploy:
  needs: [validate, check-links]
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  runs-on: ubuntu-latest
  environment: production
  permissions:
    id-token: write     # federated credential
    contents: read      # nothing else

Watch out

Pin third-party actions to a full commit SHA rather than a tag. Tags are mutable: an action you reviewed last year can be rewritten under the same name, and it runs with whatever permissions you granted the job.

Verify after deployment, not only before

A pipeline that goes green when the upload finishes has proved that the upload finished. Add a short post-deployment check that requests the live homepage and asserts the things you actually care about: a 200 status, the expected security headers, and a real 404 on a deliberately invalid path.

This is where header regressions get caught. Configuration files are edited far more often than anyone expects, and a policy that silently stopped applying three months ago is a common finding.

Keep it short enough to trust

A pipeline that takes eleven minutes stops being a feedback loop and starts being an interruption. For a site of this size, aim for under three minutes to the first useful signal. Everything slower belongs on a schedule rather than in the path of a merge.

The goal throughout is unremarkable: someone edits a paragraph, opens a pull request, sees a preview, gets a review, merges, and the change is live a few minutes later with nobody's heart rate elevated. That is the whole ambition.