# GitLab Pages Report 404s — The public/ Rule and Everything Else That Bites

Canonical: https://commareports.com/fix/gitlab-pages-report-404
Published: 2026-09-07

> A pages job that succeeds and a URL that 404s. The public/ directory rule, the deploy delay, branch collisions, and why Pages is a poor fit for per-pipeline reports.

# The pages job is green and the URL is a 404

`pages: success` in the pipeline. `https://group.gitlab.io/project/`
returns `404 Not Found`.

Work down this list — the first two account for most of them.

## 1. The report is not in `public/`

GitLab Pages publishes **one** directory: `public/` at the repository
root, declared as the job's artifact. Not `dist/`, not `htmlcov/`, not
`public/` nested inside a subproject.

```yaml
pages:
  stage: deploy
  script:
    - coverage html # writes htmlcov/
    - mv htmlcov public # ← the step people omit
  artifacts:
    paths:
      - public # ← must be exactly this
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```

Miss the `mv` and the job still passes. Miss the `artifacts: paths` and
it also passes. Pages just has nothing to publish.

## 2. It has not deployed yet

The Pages deploy runs after the job, as a separate step. A minute or
three between green pipeline and live site is normal. Check
**Deploy → Pages** for the deployment's own status before assuming
breakage.

## 3. `index.html` is not at the root of `public/`

Pages serves `public/index.html` at `/`. A report that lands as
`public/htmlcov/index.html` needs `/htmlcov/` in the URL. Directory
listings are off by default, so the root is a 404 rather than a helpful
index.

## 4. Another branch overwrote it

One project, one Pages site, published by whichever pipeline ran the
`pages` job last. Two branches both running it means the second wins
silently. The usual workaround is a per-branch subdirectory under
`public/`, which then needs a cleanup job nobody writes — so the site
grows until someone notices.

## 5. Access control

If the project is private, the site is private only when Pages access
control is enabled — a per-instance setting on self-managed GitLab that
is often off, in which case the "internal" report is public. And it is
all-or-nothing: project members or the world. There is no "this one
report, these three people".

## 6. `.gitlab-ci.yml` rules skipped the job

`rules:` or `only:` restricted to the default branch means feature
branches never publish, which is exactly when a preview is wanted.

## The artifact browser is not the fallback

GitLab's artifact browse view can serve a single HTML file, but
multi-file reports — [coverage trees](/fix/coverage-report-links-broken),
[Allure](/fix/allure-report-blank-page),
[Playwright](/fix/playwright-report-blank) — break on its path handling
and policy rules. And artifacts expire: 30 days by default, sooner when
"keep artifacts from most recent successful jobs" deletes superseded
pipelines regardless of age. Every link you paste has
[an expiry date attached](/fix/ci-artifact-expired).

## What Pages is actually good for

One canonical site per project, published from the default branch,
public. Docs sites fit that perfectly.

Per-pipeline reports do not: they are many, they are per-branch, they
are frequently private, and they exist to be argued about. Publishing
each run to a report URL removes the whole list above —

```yaml
publish_report:
  stage: deploy
  when: always # a red pipeline needs it more
  script:
    - >
      curl -sS -X PATCH "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID"
      -H "Authorization: Bearer $COMMA_API_TOKEN"
      -H "Content-Type: application/json"
      --data "$(jq -Rs '{html: .}' < htmlcov/index.html)"
```

— with one id per report, a revision appended per pipeline, per-report
visibility, and no expiry. Full setup in
[GitLab CI HTML reports](/ci/gitlab-ci-html-report) and
[publish from CI](/docs/ci).

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Publish a report from GitLab CI →](https://commareports.com/)**

### Related

- [GitLab CI HTML reports](/ci/gitlab-ci-html-report) · [Publish from CI](/docs/ci)
- [The CI artifact link expired](/fix/ci-artifact-expired)
- [GitHub Pages alternatives](/alternatives/github-pages-alternatives)
- [Coverage report links 404](/fix/coverage-report-links-broken)
