Configuring Projects With vercel.json
The Vercel dashboard can store many project settings, but some configuration belongs in the repository. A vercel.json file lets you define project behavior beside the code it affects. That is useful for review, repeatability, and handoff. When redirects, rewrites, headers, clean URLs, functions, cron jobs, or build settings are important to the app, future maintainers should be able to see those decisions in version control.
Do not add vercel.json just because it exists. Many projects deploy correctly with framework detection and dashboard settings. Add configuration when it makes behavior clearer, makes deployments reproducible, or records decisions that should be reviewed with code.
This lesson introduces the most common categories. It is not a replacement for Vercel's official reference. Project configuration changes over time, and some options are framework-specific or plan-sensitive. Always check the current docs before using a setting in production.
What vercel.json Is
vercel.json is a JSON file placed at the project root. It describes static project configuration for Vercel.
A small file might look like this:
{
"cleanUrls": true,
"trailingSlash": false
}
A larger file might include routing and headers:
{
"redirects": [
{ "source": "/old", "destination": "/new", "permanent": true }
],
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
]
}
Because JSON has strict syntax, commas and quotes matter. A broken vercel.json can break deployment. Keep changes small and validate by deploying a preview before relying on production.
Build, Dev, And Output Settings
Vercel can usually detect common frameworks, but you can also configure commands and output behavior. The relevant concepts are:
build command
creates deployable output
dev command
starts local development behavior for supported workflows
output directory
tells Vercel which generated files to serve for static/front-end builds
install command
installs dependencies before build when needed
For a plain static site, you may not need a build command. For a Vite app, a common build command is npm run build and the output directory is usually dist. For a documentation generator, the output directory depends on that tool. For Next.js, Vercel's framework support handles much of this automatically.
Do not configure commands by guessing. Look at package.json, framework docs, and successful local builds. If the local build creates dist/, then dist is a reasonable output directory. If it creates build/, configure build. A mismatch can produce a successful build with the wrong deployed content.
Redirects
A redirect tells the browser to go somewhere else. Redirects are useful when old URLs should permanently or temporarily point to new URLs.
Example:
{
"redirects": [
{
"source": "/pricing-old",
"destination": "/pricing",
"permanent": true
}
]
}
Use redirects for changed page paths, canonical domain behavior, old campaign URLs, and cleanup after site reorganizations. Permanent redirects can affect browser caches and search engines, so use them only when the move should be durable. Temporary redirects are safer for experiments or short-lived routing.
A redirect changes what the visitor sees in the address bar. That is different from a rewrite.
Rewrites
A rewrite maps one incoming path to another destination without necessarily changing the browser's visible URL. Rewrites are useful for single-page app fallbacks, API proxying, and routing old-looking paths to new internal handlers.
A simple SPA fallback might look conceptually like:
{
"rewrites": [
{ "source": "/app/(.*)", "destination": "/app/index.html" }
]
}
The exact pattern depends on the app. Use rewrites carefully because they can hide where requests actually go. A rewrite to an external API can also create security and caching questions. Do not use a rewrite to expose a private service without authentication. Do not assume proxying makes an unsafe API safe.
If a user should know the URL changed, use a redirect. If the URL should stay the same while the platform serves a different internal resource, consider a rewrite.
Headers
Headers let you attach HTTP response metadata to matching paths. Headers can control caching, security policy, content type behavior, and browser features.
Example:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" }
]
}
]
}
Security headers should be chosen deliberately. A Content Security Policy can improve security but break scripts, fonts, analytics, or inline styles if configured incorrectly. Cache headers can improve performance but serve stale assets if applied to files that change without fingerprinted names.
Do not paste a giant header block without understanding it. Add one decision at a time and test the affected pages.
Function Configuration
Full-stack projects may use Vercel Functions. vercel.json can configure selected function behavior, such as duration or runtime-related settings depending on the current platform support.
A function configuration belongs in code review when it affects cost, timeout behavior, or runtime expectations. If a function needs more time because it sends emails, calls a slow API, or processes a webhook, document why. A longer duration can be useful, but it can also hide inefficient work and affect usage.
Function configuration should not contain secrets. Store credentials in Environment Variables. The config describes behavior; it should not embed private values.
A useful review question is:
Why does this function need special configuration, and what happens if it times out?
If nobody can answer, the setting may be premature.
Cron Jobs
Vercel supports cron jobs for scheduled requests. A cron definition can call an endpoint on a schedule, such as refreshing a sitemap, warming a cache, sending reminders, or cleaning temporary records.
A cron job is production behavior. It runs without a user clicking a page. That means the endpoint needs authorization, idempotency, logging, and failure handling. Do not create a cron job that can perform destructive work repeatedly without safeguards.
A conceptual cron entry looks like:
{
"crons": [
{ "path": "/api/cron/refresh", "schedule": "0 6 * * *" }
]
}
Cron support and limits can depend on plan and current Vercel rules. Check the official docs before promising a schedule to a client. Later lessons cover cron jobs in more detail.
Clean URLs And Trailing Slashes
Clean URLs remove visible .html endings from static pages. For example, /about can serve /about.html. Trailing slash settings control whether paths prefer /about or /about/.
These settings affect user-visible URLs, links, redirects, analytics, and SEO. Choose a style early and keep it consistent. Do not casually change URL style after launch unless you also configure redirects and test important paths.
For a small static site, clean URLs can make addresses nicer:
{
"cleanUrls": true,
"trailingSlash": false
}
But if a framework already manages routing, adding URL settings without understanding the framework can create conflicts. Let the framework handle routing unless you have a specific reason to override it.
Dashboard Settings Vs vercel.json
Some settings can live in the dashboard, vercel.json, or framework configuration. Prefer the place that makes the decision easiest to review and maintain.
Use the dashboard for project ownership, domains, environment variable values, team permissions, and settings that are operational rather than source-defined.
Use vercel.json for routing and deployment behavior that should travel with the repository and be reviewed in pull requests.
Use framework configuration for framework-specific behavior, such as Next.js routing, image settings, or build behavior when the framework expects it there.
Avoid configuring the same behavior in several places. If a redirect exists in framework config and vercel.json, future maintainers may not know which one wins.
Configuration Ownership
Every setting in vercel.json should have an owner in practice, even if the file itself is shared. Routing rules affect frontend users. Function settings affect backend behavior and cost. Cron jobs affect scheduled production work. Headers affect security, caching, and browser behavior. Build settings affect whether the project can deploy at all.
When a pull request changes vercel.json, review it with the same seriousness as application code. Ask who requested the behavior, what paths are affected, how it was tested, and how it can be rolled back. A redirect for one old URL is low risk. A rewrite that catches every path is high risk. A header on one asset folder is low risk. A site-wide cache header can break production quickly.
For client projects, configuration ownership also affects handoff. A future maintainer should not need to reverse-engineer why a cron job exists or why a route is rewritten. Put the reason in the pull request, issue, or nearby documentation. The JSON file says what Vercel should do; the surrounding project record should explain why.
Debugging Broken Configuration
When configuration breaks a deployment, isolate the category. If the build fails before output is created, check build command, install command, package manager, and output directory. If the build passes but a URL goes somewhere unexpected, check redirects and rewrites. If the page loads but assets or APIs behave strangely, check headers and cache behavior. If scheduled work fails, check the cron path, endpoint authorization, runtime logs, and plan limits.
Do not respond to a routing problem by changing five rules at once. Make one small correction, deploy a preview, and test the exact path. Keep a list of URLs that should work before and after the change. Routing bugs are easiest to fix when you can say, "This source path should redirect to this destination with this status," or, "This visible URL should serve this internal resource without changing the address bar."
Configuration is code in the practical sense: it changes behavior, it needs review, and it can fail in production.
Validate Configuration Changes
Every vercel.json change should be tested in Preview before Production. A typo can block deployment. A broad rewrite can shadow routes. A cache header can make old assets stick. A cron job can call an endpoint too often. A redirect can create a loop.
Use a checklist:
JSON syntax is valid.
Preview Deployment builds.
Changed routes behave as expected.
Old routes redirect or rewrite intentionally.
Headers appear only on intended paths.
Functions still run.
Cron endpoints are protected.
Production impact is understood before merge.
For local syntax checks, a JSON-aware editor is enough for basic mistakes. For behavior, deploy a preview and test the actual URLs.
Common Mistakes
The first mistake is adding vercel.json before understanding the framework defaults. Defaults often work well.
The second mistake is using redirects and rewrites interchangeably. They solve different routing problems.
The third mistake is setting cache headers too broadly. HTML and frequently changing JSON should not be cached like fingerprinted assets.
The fourth mistake is putting secrets in config. Use Environment Variables.
The fifth mistake is creating cron jobs without authentication or idempotency. Scheduled work can fail, repeat, or overlap.
The sixth mistake is changing URL style without redirects. Users and search engines may still request old paths.
Review Questions
- When is
vercel.jsonuseful? - What is the difference between a redirect and a rewrite?
- Why should cache headers be scoped carefully?
- What belongs in Environment Variables instead of
vercel.json? - Why should cron endpoints be protected?
- What should you test before merging a configuration change?