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.
Clean URLs
Remove .html extensions from URLs. When enabled, /about.html is served at /about. Visiting /about.html redirects to /about.
{
"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 |
{
"trailingSlash": false
}
Redirects
Send visitors from one URL to another. The browser URL changes.
{
"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:
{ "source": "/blog/:slug", "destination": "/posts/:slug" }
Wildcards match everything after:
{ "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.
{
"rewrites": [
{ "source": "/feed", "destination": "/feed.xml" }
]
}
| Property | Type | Required | Description |
|---|---|---|---|
source |
string | Yes | Pattern to match |
destination |
string | Yes | Target path or URL |
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:
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
Headers
Add custom HTTP response headers. Unlike redirects and rewrites, multiple header rules can match the same request.
{
"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:
- Static files — served first if they exist on disk
- Headers — all matching rules apply
- Redirects — checked before rewrites
- 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.