Fix SvelteKit Static Broken Links on Vercel (Trailing Slash Issue)
SvelteKit adapter-static deploys but internal links 404 on Vercel. Fix trailingSlash, prerender, and vercel.json rewrites.
Tested on: SvelteKit 2.16, adapter-static 3.0, Vercel
Symptom: npm run preview works. Production 404 on half the links.
TL;DR
- Match trailingSlash in svelte.config.js with Vercel URL behavior.
- Prerender all routes or add fallback for dynamic paths.
- Use vercel.json rewrites if mixing trailing slash styles.
The failure pattern
/blogworks,/blog/404- Or the reverse
<a href="/about">404 but typing URL manually works- Assets load, navigation breaks
Root cause: three configs disagreeing
svelte.config.js→kit.trailingSlash- Vercel project → Trailing Slash setting
- Your
<a href>paths
All three must match. Pick one style and enforce it everywhere.
My fix — always trailing slash
svelte.config.js:
kit: {
trailingSlash: 'always',
paths: {
relative: false
},
adapter: adapter({ fallback: '404.html' })
}
Vercel dashboard → Project Settings → Trailing Slash: Always.
Every internal link:
<a href="/about/">About</a>
<a href="/blog/{post.slug}/">{post.title}</a>
Error in build log (subtle)
[WARN] Files prefixed with +layout will be ignored in version 3
Unrelated warning — don't chase it. Focus on link audit.
Audit script
scripts/check-links.mjs:
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
const buildDir = 'build';
const htmlFiles = [];
function walk(dir) {
for (const f of readdirSync(dir, { withFileTypes: true })) {
const p = join(dir, f.name);
if (f.isDirectory()) walk(p);
else if (f.name.endsWith('.html')) htmlFiles.push(p);
}
}
walk(buildDir);
const hrefRe = /href="(\/[^"#]+)"/g;
const missing = [];
for (const file of htmlFiles) {
const html = readFileSync(file, 'utf8');
let m;
while ((m = hrefRe.exec(html))) {
const href = m[1];
const target = href.endsWith('/') ? `build${href}index.html` : `build${href}.html`;
try { readFileSync(target); }
catch { missing.push({ from: file, href }); }
}
}
console.log(missing.length ? missing : 'All links OK');
Run after every build:
npm run build && node scripts/check-links.mjs
vercel.json (optional safety net)
{
"trailingSlash": true,
"cleanUrls": false
}
vercel.json mistake
{ "cleanUrls": true }
With cleanUrls: true + trailingSlash: 'always' in SvelteKit = guaranteed 404s. Pick one URL style.
Result
Broken internal links: 23 → 0
Link check script runs in CI (12s)
No more "works on my machine" deploys
FAQ
Why do SvelteKit static links 404 on Vercel?
Trailing slash mismatch — SvelteKit generates /about/ but Vercel serves /about, or vice versa. Align trailingSlash config with host defaults.
What trailingSlash setting works for Vercel?
Often trailingSlash: 'always' with Vercel, or 'ignore' with explicit vercel.json redirects. Test both /path and /path/.
Do I need adapter-static for Vercel static deploy?
Yes, for fully static sites. Set adapter-static with fallback: undefined and prerender all routes in +layout.ts.