Vercel generates unique URLs for deployments, previews, and production. Understanding these patterns helps with CI/CD, testing, and debugging.
Key Takeaways
- 1Each deployment gets a unique .vercel.app URL
- 2Preview deployments are generated for each push
- 3Production URLs require domain configuration
- 4Edge functions use same domain with /api routes
- 5Rewrites and redirects configured in vercel.json
When you deploy to Vercel, you get multiple URLs for the same codebase. Knowing which URL to use—and when—helps you set up proper CI/CD workflows, share preview links with stakeholders, and configure environment-specific behavior.
“Every push to a branch creates a Preview Deployment with a unique URL. Production Deployments are created when you merge to your production branch.”
URL Types
Vercel generates several types of URLs, each serving a different purpose. Understanding the naming patterns helps you predict URLs for automation scripts and CI/CD pipelines.
| Type | Pattern | Example |
|---|---|---|
| Production | project.vercel.app | myapp.vercel.app |
| Preview | project-{hash}.vercel.app | myapp-abc123.vercel.app |
| Branch | project-git-branch.vercel.app | myapp-git-feature.vercel.app |
| Deployment | project-{deployment}.vercel.app | myapp-abc123def.vercel.app |
| Custom domain | your-domain.com | www.example.com |
Production URLs point to the latest successful deployment from your main branch, while preview URLs are immutable—they always show the exact code from that specific deployment.
Deployment URL Patterns
Each deployment type follows a predictable URL pattern. The examples below show how Vercel constructs URLs from your project name, team slug, and deployment or branch identifiers.
# Vercel deployment URL structure
# Production (latest main/master)
https://{project-name}.vercel.app
# Unique deployment URL (immutable)
https://{project-name}-{deployment-id}-{team-slug}.vercel.app
# Branch preview
https://{project-name}-git-{branch-name}-{team-slug}.vercel.app
# PR preview
https://{project-name}-git-{pr-branch}-{team-slug}.vercel.app
# Examples
https://my-nextjs-app.vercel.app
https://my-nextjs-app-abc123def-myteam.vercel.app
https://my-nextjs-app-git-feature-login-myteam.vercel.app
# With custom domain aliases
https://www.myapp.com -> Production
https://staging.myapp.com -> Staging branch
https://preview.myapp.com -> Latest previewThe unique deployment URL (with the deployment ID) is immutable and will always show that exact version. This is useful for rollback testing or sharing a specific state with QA.
API Route URLs
Vercel serves your API routes from the same domain as your frontend. In Next.js, these are defined in the app/api directory and follow familiar file-based routing conventions.
// Vercel API routes (Next.js App Router)
// Located in app/api/*/route.ts
// app/api/users/route.ts
export async function GET(request) {
const url = new URL(request.url);
const page = url.searchParams.get('page') || '1';
const limit = url.searchParams.get('limit') || '10';
return Response.json({ page, limit, users: [] });
}
// URL: /api/users?page=1&limit=10
// Dynamic segments
// app/api/users/[id]/route.ts
export async function GET(request, { params }) {
const { id } = params;
return Response.json({ id, name: 'John' });
}
// URL: /api/users/123
// Catch-all routes
// app/api/[...path]/route.ts
export async function GET(request, { params }) {
const { path } = params; // ['a', 'b', 'c'] for /api/a/b/c
return Response.json({ path });
}API routes can handle query parameters, dynamic path segments, and catch-all patterns. The URL structure mirrors your file system, making it easy to reason about routing.
Rewrites & Redirects
Rewrites and redirects let you control how URLs map to content. Rewrites happen internally (the user sees the original URL), while redirects change the URL in the browser. Configure these in vercel.json.
{
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://api.example.com/:path*"
},
{
"source": "/blog/:slug",
"destination": "/posts/:slug"
},
{
"source": "/app/:path*",
"destination": "/dashboard/:path*",
"has": [
{ "type": "cookie", "key": "logged_in", "value": "true" }
]
}
],
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
},
{
"source": "/blog/:year(\\d{4})/:slug",
"destination": "/posts/:slug",
"permanent": true
}
],
"headers": [
{
"source": "/api/:path*",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
]
}Rewrites are useful for proxying API requests to avoid CORS issues, while redirects handle URL migrations and vanity URLs. You can add conditions based on headers, cookies, or query parameters.
Edge Middleware URL Handling
Edge middleware runs before your page loads, letting you modify requests, add headers, or redirect users based on URL patterns. This is powerful for A/B testing, authentication checks, and geo-based routing.
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(request) {
const url = request.nextUrl.clone();
// URL-based routing
if (url.pathname.startsWith('/api/')) {
// Add auth header for API routes
const response = NextResponse.next();
response.headers.set('x-api-version', 'v2');
return response;
}
// Redirect based on URL
if (url.pathname === '/old-dashboard') {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
// Rewrite URL (internal routing)
if (url.pathname.startsWith('/products/')) {
url.pathname = '/shop' + url.pathname;
return NextResponse.rewrite(url);
}
// A/B testing with URL preservation
if (url.pathname === '/pricing') {
const bucket = Math.random() < 0.5 ? 'a' : 'b';
url.pathname = `/pricing-${bucket}`;
return NextResponse.rewrite(url);
}
return NextResponse.next();
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico).*)'
]
};The middleware examines the URL path and takes different actions: adding headers for API routes, redirecting legacy paths, rewriting URLs for internal routing, and implementing A/B tests with URL preservation.
Environment-Based URLs
Vercel injects environment variables that tell your code which environment it is running in. Use these to construct URLs dynamically and connect to the right backend services.
// Access deployment URL in code
// Server-side (API routes, getServerSideProps)
const deploymentUrl = process.env.VERCEL_URL;
const environment = process.env.VERCEL_ENV; // production, preview, development
// Build full URL
function getBaseUrl() {
if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`;
}
// Fallback for local development
return 'http://localhost:3000';
}
// Branch-specific configuration
const branchUrl = process.env.VERCEL_GIT_COMMIT_REF;
const commitSha = process.env.VERCEL_GIT_COMMIT_SHA;
// Build URLs for different environments
function getApiUrl() {
switch (process.env.VERCEL_ENV) {
case 'production':
return 'https://api.myapp.com';
case 'preview':
return 'https://api-staging.myapp.com';
default:
return 'http://localhost:4000';
}
}
// Client-side (use NEXT_PUBLIC_ prefix)
const publicUrl = process.env.NEXT_PUBLIC_SITE_URL;The VERCEL_ENV variable tells you whether you are in production, preview, or development. Use this to select the right API endpoints, feature flags, or analytics configurations.
Vercel API URLs
Vercel provides a REST API for managing deployments, projects, and domains programmatically. This is useful for building deployment dashboards, automation scripts, or custom CI/CD integrations.
// Vercel REST API
const baseUrl = 'https://api.vercel.com';
// Common endpoints
const endpoints = {
// Deployments
listDeployments: '/v6/deployments',
getDeployment: '/v13/deployments/{id}',
createDeployment: '/v13/deployments',
// Projects
listProjects: '/v9/projects',
getProject: '/v9/projects/{idOrName}',
// Domains
listDomains: '/v5/domains',
addDomain: '/v10/projects/{projectId}/domains',
// Environment Variables
getEnvVars: '/v10/projects/{projectId}/env'
};
// Example: List deployments
const response = await fetch(`${baseUrl}/v6/deployments?projectId=${projectId}`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
// Get deployment by URL
const deployment = await fetch(`${baseUrl}/v13/deployments/${deploymentUrl}`, {
headers: {
'Authorization': `Bearer ${token}`
}
});All API requests require authentication via a Bearer token. You can create tokens in your Vercel account settings. The API follows REST conventions with versioned endpoints.