# ship.json

> Optional configuration for how a deployment is served: clean URLs, trailing slashes, redirects, rewrites, headers, their processing order and limits.

Source: https://docs.shipstatic.com/ship-json

An optional `ship.json` file in the root of your project configures how the platform serves your deployment. Most sites work perfectly without one — add it only when you need redirects, rewrites, headers, or URL formatting.

Password protection is set on the deploy request itself, not in `ship.json`. See [Deployments](https://docs.shipstatic.com/deployments#password-protection).

## Clean URLs

Remove `.html` extensions from URLs. When enabled, `/about.html` is served at `/about`. Visiting `/about.html` redirects to `/about`.

```json
{
  "cleanUrls": true
}
```

## Trailing Slash

Control trailing slash behavior for all URLs.

| Value | Behavior |
|-------|----------|
| `false` | `/about/` redirects to `/about` |
| `true` | `/about` redirects to `/about/` |
| not set | Same as `false` — every URL has exactly one canonical form |

```json
{
  "trailingSlash": false
}
```

## Redirects

Send visitors from one URL to another. The browser URL changes.

```json
{
  "redirects": [
    { "source": "/pricing", "destination": "/plans", "permanent": true },
    { "source": "/blog/:slug", "destination": "/posts/:slug", "permanent": true }
  ]
}
```

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `source` | string | Yes | Pattern to match |
| `destination` | string | Yes | Target URL or path |
| `permanent` | boolean | No | `true` = 308, `false` = 307. Default: `false` — permanence is opt-in |

### Pattern syntax

**Named parameters** match a single path segment:

```json
{ "source": "/blog/:slug", "destination": "/posts/:slug" }
```

**Wildcards** match everything after:

```json
{ "source": "/docs/:path*", "destination": "/help/:path*" }
```

## Rewrites

Serve content from a different path without changing the browser URL. Files on disk are checked first — rewrites only apply when no file matches.

```json
{
  "rewrites": [
    { "source": "/feed", "destination": "/feed.xml" }
  ]
}
```

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `source` | string | Yes | Pattern to match |
| `destination` | string | Yes | Target path |

The same pattern syntax as redirects applies (`:param`, `:path*`).

### SPA fallback

The platform automatically detects single-page apps and configures client-side routing fallbacks. Most SPAs work without any configuration. Use a manual rewrite only if auto-detection doesn't cover your setup:

```json
{
  "rewrites": [
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}
```

## Headers

Add custom HTTP response headers. Unlike redirects and rewrites, multiple header rules can match the same request.

```json
{
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ]
}
```

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `source` | string | Yes | Pattern to match |
| `headers` | array | Yes | Key-value pairs to set |

## Processing Order

Rules are evaluated in this order:

1. **Static files** — served first if they exist on disk
2. **Headers** — all matching rules apply
3. **Redirects** — checked before rewrites
4. **Rewrites** — only when no file or redirect matched

Within redirects and rewrites, rules are processed in array order. Put catch-all patterns last.

## Limits

`ship.json` may be up to 1 MB, with up to 1,024 rules each for redirects, rewrites, and headers. Individual patterns are limited to 200 characters. These are generous ceilings — a typical config uses a handful of rules.
