# Jenkins HTML Report With No CSS? The CSP Fix — and the Better Option

Canonical: https://commareports.com/ci/jenkins-html-report
Published: 2026-08-19

> Jenkins HTML Publisher strips CSS and JavaScript because of DirectoryBrowserSupport's Content-Security-Policy. Here's the sanctioned fix, why loosening it is risky, and how to publish the report to a stable URL instead.

# The Jenkins HTML report with no CSS

You installed the HTML Publisher plugin, pointed it at
`htmlcov/index.html`, clicked the link on the build page — and got the raw
document. No stylesheet, no charts, no tabs. Just headings and links stacked
down the page like it's 1994.

Nothing is broken. Jenkins serves artifact files through
`DirectoryBrowserSupport`, and that path sets a deliberately strict
`Content-Security-Policy` — the default blocks inline styles, external
stylesheets and all JavaScript. It exists because build artifacts are
attacker-writable in a lot of pipelines, and Jenkins serving scripted HTML
from its own origin is a session-stealing primitive.

## The sanctioned fix, and its actual price

The property is `hudson.model.DirectoryBrowserSupport.CSP`. Runtime version,
from **Manage Jenkins → Script Console**:

```groovy
System.setProperty(
  "hudson.model.DirectoryBrowserSupport.CSP",
  "sandbox allow-scripts; default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
)
```

Persistent version, as a JVM argument in your service definition or container
spec:

```
-Dhudson.model.DirectoryBrowserSupport.CSP="sandbox allow-scripts; default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
```

Two things people learn the hard way:

1. **The Script Console version doesn't survive a restart.** It's an in-memory
   system property. If the fix isn't in the JVM args, the reports go unstyled
   again the next time the controller cycles — usually at the worst moment.
2. **It's controller-wide.** There is no per-job scope. Loosening it for your
   trusted coverage report also loosens it for every other job's artifacts,
   including any pipeline that builds a fork, a dependency bump, or third-party
   code. Plenty of teams set it to `""` (fully disabled) and never revisit it.

If your Jenkins only ever builds first-party code and every contributor is
trusted, the narrow policy above is a reasonable trade. Just make it a
deployment-config change, not a console click, and write down why.

## The option that doesn't touch Jenkins security

Serve the report from somewhere that isn't the Jenkins origin. Then the CSP
that protects your Jenkins session is irrelevant, because your report isn't
running inside it.

Store a [scoped token](/docs/api-tokens) (`reports:write` only) as a Jenkins
credential and publish from the pipeline:

```groovy
pipeline {
  agent any
  stages {
    stage('Test') {
      steps { sh 'pytest --cov --cov-report=html' }
    }
  }
  post {
    always {
      withCredentials([string(credentialsId: 'comma-api-token', variable: 'COMMA_API_TOKEN')]) {
        sh '''
          curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID" \
            -H "Authorization: Bearer $COMMA_API_TOKEN" \
            -H "Content-Type: application/json" \
            -d "$(jq -n --rawfile html htmlcov/index.html \
                  --arg title "Coverage — build ${BUILD_NUMBER}" \
                  '{title: $title, html: $html}')"
        '''
      }
      echo "Report → https://commareports.com/p/${COMMA_REPORT_ID}"
    }
  }
}
```

`post { always { … } }` is the Jenkins equivalent of publishing on red builds
too — which are the ones people actually want to read.

Comma renders the HTML in a sandboxed frame in **its own** origin and applies
its own sanitizing on write, so styling survives and your Jenkins session
never enters the picture.

## What changes besides the CSS

- **The link outlives the build.** Jenkins build retention eventually discards
  old builds and their archived artifacts. `PATCH`ing one report id appends a
  revision per build at a URL that doesn't rotate — and any two revisions can
  be diffed.
- **Readers don't need a Jenkins account.** This is usually the real
  constraint: the people who should read the report (PM, security reviewer,
  customer, auditor) are exactly the people you don't want to give a Jenkins
  login. Comma reports use
  [identity-based visibility](/docs/sharing) — private, team, domain-gated, or
  a link.
- **Feedback lands on the report.** Anchored comment threads on the exact row
  or number being questioned, instead of a Slack screenshot of an unstyled
  table. See [commenting on HTML](/comment-on-html).
- **Builds can announce themselves.** A [webhook](/docs/api) on
  `revision.created` posts the new revision to Slack or Discord.

## Caveats, stated plainly

- **HTML body cap is 5 MB**; images, videos and archives attach as
  [assets](/docs/api) at 25 MB per file.
- **Scripts are stripped on write.** A report that is a JavaScript application
  — Allure, Playwright's HTML reporter — should be published as a static digest
  with the full archive attached. Same trade Jenkins forces on you by default,
  minus the unstyled part.
- **Keep HTML Publisher installed** if you also want the in-Jenkins link. The
  two coexist; one is for the build page, one is for the humans.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history. Add the `post` block above and stop debating the CSP property in
your infra channel.

**[Create your first report →](https://commareports.com/)**

### Related

- [Publish from CI](/docs/ci) — the general pipeline pattern
- [GitHub Actions HTML reports](/ci/github-actions-html-report)
- [GitLab CI HTML reports](/ci/gitlab-ci-html-report)
- [Share a pytest report](/share-pytest-report)
