Serving

# Subresource integrity

Integrity hashes in the import map and the preload links, so the browser refuses a module that was tampered with.

## Enabling integrity

Opt in globally, then control it per pin.

Integrity calculation is off until you call `enable_integrity!` in `config/importmap.rb`. With it on, `integrity: true` (the default for every pin) computes a hash for each local asset through the asset pipeline; `integrity: "sha384-…"` uses the value you give; `integrity: false` or `nil` turns it off for that pin.

```ruby
# Enable integrity calculation globally
enable_integrity!

# With integrity enabled, these auto-calculate integrity hashes
pin "application"                                               # auto-calculated
pin "admin", to: "admin.js"                                     # auto-calculated
pin_all_from "app/javascript/controllers", under: "controllers" # auto-calculated

# Explicit control
pin "cdn_package", to: "https://cdn.example.com/cdn_package.js", integrity: "sha384-abc123..." # pre-calculated hash
pin "no_integrity_package", integrity: false     # explicitly disabled
pin "nil_integrity_package", integrity: nil      # explicitly disabled
```

This is particularly useful for local JavaScript files managed by the asset pipeline, for bulk `pin_all_from` pins where computing hashes by hand would be tedious, and in development where file contents change often. External CDN packages should provide their own hashes.

## Propshaft

SRI needs Propshaft 1.2+ and an integrity hash algorithm configured in your application; without it integrity is disabled by default under Propshaft. Sprockets has integrity support out of the box.

```ruby
config.assets.integrity_hash_algorithm = "sha256" # or "sha384", "sha512"
```

## What the browser gets

The hashes are included in the import map's `integrity` section and on each module preload link, and the browser validates them as it loads the modules:

```json
{
  "imports": {
    "lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js",
    "application": "/assets/application-abc123.js",
    "controllers/hello_controller": "/assets/controllers/hello_controller-def456.js"
  },
  "integrity": {
    "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js": "sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF",
    "/assets/application-abc123.js": "sha256-xyz789...",
    "/assets/controllers/hello_controller-def456.js": "sha256-uvw012..."
  }
}
```

```html
<link rel="modulepreload" href="https://ga.jspm.io/npm:lodash@4.17.21/lodash.js" integrity="sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF">
<link rel="modulepreload" href="/assets/application-abc123.js" integrity="sha256-xyz789...">
```

## When bin/importmap rewrites a pin

`integrity: true` and `integrity: false` are settings, and they survive a rewrite by `pin`, `update` or `pristine` the way `preload:` does. An explicit hash string belongs to one particular file, so it is dropped when the URL changes — the old hash would no longer match. Pin a fresh hash after an update of a remote pin that carried one.