Next.js Production: Vercel Deployment ve Optimizasyon Rehberi
Next.js uygulamalarını Vercel'e deploy etmenin tüm adımlarını öğrenin. CI/CD pipeline, environment variables, domain yapılandırması, monitoring ve production optimizasyonları bu kapsamlı rehberde.
Next.js Production: Vercel Deployment ve Optimizasyon Rehberi
Next.js uygulamanızı geliştirdiniz, artık dünyayla paylaşma zamanı. Vercel, Next.js'in yaratıcısı olarak en optimize deployment deneyimini sunuyor. Zero-config deployment, automatic previews, edge network ve built-in analytics ile production'a geçiş hiç bu kadar kolay olmamıştı.
Bu rehberde Vercel'e ilk deployment'tan advanced production stratejilerine kadar tüm süreci ele alacağız. Environment variables yönetimi, custom domain yapılandırması, CI/CD pipeline'lar ve monitoring dahil.
İçindekiler
- Vercel Nedir ve Neden Kullanmalı?
- İlk Deployment
- Environment Variables Yönetimi
- Custom Domain Yapılandırması
- Preview Deployments
- Build Konfigürasyonu
- Edge Functions ve Middleware
- Monitoring ve Analytics
- Performance Optimizasyonu
- Security Best Practices
- CI/CD Pipeline Entegrasyonu
- Alternatif Deployment Seçenekleri
- Sık Sorulan Sorular
Vercel Nedir ve Neden Kullanmalı?
Vercel, frontend framework'leri için optimize edilmiş cloud platform. Next.js'in yaratıcısı olan Vercel, framework'le native entegrasyon sunuyor.
Vercel Avantajları
Zero-Config Deployment: Git push ile otomatik deployment. Build settings, routing, caching otomatik yapılandırılır.
Edge Network: Global CDN ile düşük latency. 100+ edge location.
Preview Deployments: Her PR için unique URL. Team review kolaylaşır.
Serverless Functions: API routes otomatik serverless function olarak deploy edilir.
Built-in Features:
- Web Analytics
- Speed Insights
- Log Drains
- DDoS Protection
- SSL Certificates
Vercel vs Alternatifler
| Platform | Next.js Desteği | Ücretsiz Tier | Edge Support |
|---|---|---|---|
| Vercel | Native | Generous | Global |
| Netlify | Adapter ile | Generous | Limited |
| Cloudflare Pages | Adapter ile | Generous | Global |
| AWS Amplify | Kısmi | Limited | Regional |
| Railway | Docker | Limited | Yok |
İlk Deployment
Git Repository Bağlama
- vercel.com'a giriş yapın
- "Add New Project" tıklayın
- GitHub/GitLab/Bitbucket'ı bağlayın
- Repository'yi seçin
- "Deploy" tıklayın
Vercel otomatik olarak:
- Framework'ü algılar (Next.js)
- Build command'ı belirler (
npm run build) - Output directory'yi ayarlar (
.next)
Vercel CLI ile Deployment
# Vercel CLI kurulumu
npm install -g vercel
# Login
vercel login
# Deployment (interaktif)
vercel
# Production deployment
vercel --prod
# Environment ile deployment
vercel --env DATABASE_URL=xxxİlk Deploy Sonrası
✅ Deployment başarılı!
Production: https://my-app.vercel.app
Preview: https://my-app-git-main-username.vercel.app
Inspect: https://vercel.com/username/my-app/deployments/xxx
Environment Variables Yönetimi
Vercel Dashboard'dan
- Project Settings > Environment Variables
- Key-value pair ekleyin
- Environment seçin (Production, Preview, Development)
vercel.json ile
{
"env": {
"NEXT_PUBLIC_API_URL": "https://api.example.com"
},
"build": {
"env": {
"DATABASE_URL": "@database-url"
}
}
}Secret Reference
# Secret oluştur
vercel secrets add database-url "postgresql://..."
# vercel.json'da referans
{
"env": {
"DATABASE_URL": "@database-url"
}
}Environment Türleri
Production: https://example.com
DATABASE_URL = production-db
Preview: https://my-app-xxx.vercel.app
DATABASE_URL = staging-db
Development: localhost:3000
DATABASE_URL = local-db
Sensitive Variables
// NEXT_PUBLIC_ prefix'i olmayan değişkenler
// sadece server-side'da erişilebilir
// .env.local
DATABASE_URL="postgresql://..." // Server-only
NEXT_PUBLIC_API_URL="https://api.com" // Client + Server
// Kullanım
// lib/db.ts (server-side)
const db = new PrismaClient({
datasources: {
db: { url: process.env.DATABASE_URL }
}
})
// components/api.ts (client-side)
fetch(process.env.NEXT_PUBLIC_API_URL + '/data')Custom Domain Yapılandırması
Domain Ekleme
- Project Settings > Domains
- Domain adını girin (example.com)
- DNS ayarlarını yapın
DNS Yapılandırması
# A Record (root domain)
Type: A
Name: @
Value: 76.76.21.21
# CNAME (www subdomain)
Type: CNAME
Name: www
Value: cname.vercel-dns.com
Subdomain Routing
# Farklı subdomainler farklı projelere
api.example.com → API projesi
app.example.com → Ana uygulama
blog.example.com → Blog projesi
Wildcard Domains
// vercel.json
{
"rewrites": [
{
"source": "/:path*",
"has": [
{
"type": "host",
"value": "(?<subdomain>.*).example.com"
}
],
"destination": "/sites/:subdomain/:path*"
}
]
}SSL Sertifikası
Vercel otomatik olarak:
- Let's Encrypt SSL sertifikası oluşturur
- Otomatik yeniler
- HTTP → HTTPS redirect yapar
Preview Deployments
Her PR için otomatik preview deployment oluşturulur.
Preview URL Formatı
https://my-app-git-feature-branch-username.vercel.app
https://my-app-xxxx.vercel.app (unique ID)
Branch Protection
// vercel.json
{
"git": {
"deploymentEnabled": {
"main": true,
"staging": true,
"feature/*": true
}
}
}Preview Comments
GitHub PR'larında otomatik deployment comment:
🎉 Preview deployed!
**Preview URL:** https://my-app-xxx.vercel.app
**Inspect:** https://vercel.com/...
✅ Build successful (45s)Preview Environment Variables
# Preview-specific variables
NEXT_PUBLIC_ENV=preview
API_URL=https://staging-api.example.com
Build Konfigürasyonu
vercel.json
{
"buildCommand": "npm run build",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "nextjs",
"outputDirectory": ".next",
"regions": ["iad1", "fra1"],
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
],
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
],
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://api.example.com/:path*"
}
]
}Build Optimization
// next.config.js
module.exports = {
// Standalone output (Docker için)
output: 'standalone',
// Image optimization
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com'
}
]
},
// Bundle analyzer
webpack: (config, { isServer }) => {
if (process.env.ANALYZE) {
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
config.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: isServer
? '../analyze/server.html'
: './analyze/client.html'
})
)
}
return config
}
}Build Caching
Vercel otomatik olarak cache'ler:
node_modules/.next/cache/- Turbo cache (monorepo)
// vercel.json - Cache override
{
"build": {
"env": {
"TURBO_REMOTE_CACHE_SIGNATURE_KEY": "@turbo-signature"
}
}
}Edge Functions ve Middleware
Middleware Deployment
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Geo-based routing
const country = request.geo?.country || 'US'
if (country === 'TR') {
return NextResponse.redirect(new URL('/tr', request.url))
}
// A/B testing
const bucket = request.cookies.get('bucket')?.value ||
Math.random() > 0.5 ? 'a' : 'b'
const response = NextResponse.next()
response.cookies.set('bucket', bucket)
return response
}
export const config = {
matcher: ['/((?!api|_next/static|favicon.ico).*)']
}Edge Runtime Route Handlers
// app/api/edge/route.ts
export const runtime = 'edge'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const name = searchParams.get('name')
return new Response(JSON.stringify({ hello: name }), {
headers: { 'Content-Type': 'application/json' }
})
}Edge Config
// Real-time feature flags
import { get } from '@vercel/edge-config'
export async function middleware() {
const maintenance = await get('maintenance')
if (maintenance) {
return NextResponse.rewrite(new URL('/maintenance', request.url))
}
}Monitoring ve Analytics
Vercel Analytics
npm install @vercel/analytics// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}Speed Insights
npm install @vercel/speed-insights// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
)
}Log Drains
// vercel.json
{
"logDrains": [
{
"url": "https://logs.example.com/ingest",
"secret": "@log-drain-secret"
}
]
}Custom Metrics
// lib/monitoring.ts
export function trackEvent(name: string, properties?: Record<string, any>) {
if (typeof window !== 'undefined' && window.va) {
window.va('event', { name, ...properties })
}
}
// Kullanım
trackEvent('purchase', { productId: '123', amount: 99.99 })Performance Optimizasyonu
Image Optimization
import Image from 'next/image'
export function ProductImage({ src, alt }) {
return (
<Image
src={src}
alt={alt}
width={800}
height={600}
priority={false}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
sizes="(max-width: 768px) 100vw, 50vw"
/>
)
}Font Optimization
// app/layout.tsx
import { Inter, Space_Grotesk } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter'
})
const spaceGrotesk = Space_Grotesk({
subsets: ['latin'],
display: 'swap',
variable: '--font-space'
})
export default function RootLayout({ children }) {
return (
<html className={`${inter.variable} ${spaceGrotesk.variable}`}>
<body>{children}</body>
</html>
)
}Bundle Size Optimization
// Dynamic imports
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
ssr: false
})
// Conditional loading
const Chart = dynamic(
() => import('chart.js').then(mod => mod.Chart),
{ ssr: false }
)Headers Configuration
// vercel.json
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
}
]
},
{
"source": "/fonts/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}Security Best Practices
Environment Security
# Sensitive değişkenleri Vercel'de encrypt et
# NEVER commit .env.local to git
# .gitignore
.env
.env.local
.env.*.localRate Limiting
// middleware.ts
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s')
})
export async function middleware(request: NextRequest) {
const ip = request.ip ?? '127.0.0.1'
const { success, pending, limit, reset, remaining } = await ratelimit.limit(ip)
if (!success) {
return new NextResponse('Too Many Requests', {
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': reset.toString()
}
})
}
return NextResponse.next()
}Security Headers
// next.config.js
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()'
}
]
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders
}
]
}
}DDoS Protection
Vercel built-in DDoS protection sağlar:
- Automatic traffic filtering
- Rate limiting at edge
- Bot protection
// vercel.json - Additional protection
{
"protection": {
"enabled": true,
"attackModeEnabled": false
}
}CI/CD Pipeline Entegrasyonu
GitHub Actions
# .github/workflows/preview.yml
name: Vercel Preview Deployment
on:
pull_request:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linting
run: npm run lint
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}Production Deployment
# .github/workflows/production.yml
name: Vercel Production Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'Database Migrations
# .github/workflows/production.yml
jobs:
deploy:
steps:
# ... previous steps
- name: Run database migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Deploy to Vercel
# ...Alternatif Deployment Seçenekleri
Docker Deployment
# Dockerfile
FROM node:20-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY /app/public ./public
COPY /app/.next/standalone ./
COPY /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]Self-Hosted
# Build
npm run build
# Start
npm start
# PM2 ile
pm2 start npm --name "next-app" -- startNetlify
# netlify.toml
[build]
command = "npm run build"
publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"Cloudflare Pages
# @cloudflare/next-on-pages
npm install -D @cloudflare/next-on-pages
# wrangler.toml
name = "my-next-app"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]Sık Sorulan Sorular
Vercel ücretsiz tier limitleri nelerdir?
Hobby plan: 100GB bandwidth/ay, 100 saat serverless execution, sınırsız deployment. Ticari kullanım için Pro plan gerekir.
Preview deployment'lar production'ı etkiler mi?
Hayır, preview'lar ayrı environment'ta çalışır. Ayrı database ve environment variables kullanabilirsiniz.
Vercel'de database hosting var mı?
Vercel Postgres, Vercel KV (Redis), Vercel Blob storage sunuyor. Alternatif olarak Neon, PlanetScale, Supabase kullanabilirsiniz.
Cold start problemi nasıl çözülür?
Edge Runtime kullanın veya Fluid Compute (Pro plan) aktifleştirin. Sık kullanılan fonksiyonlar warm tutulur.
Monorepo deployment nasıl yapılır?
Vercel Turborepo desteği sunuyor. Root configuration ile her package ayrı proje olarak deploy edilebilir.
Rollback nasıl yapılır?
Vercel Dashboard'dan herhangi bir önceki deployment'a instant rollback yapabilirsiniz.
Sonuç
Vercel, Next.js uygulamaları için en optimize deployment platformu. Zero-config approach ile hızlıca production'a geçebilir, advanced features ile scale edebilirsiniz.
Bu rehberle öğrendikleriniz:
- Git-based deployment workflow
- Environment variables yönetimi
- Custom domain ve SSL
- Edge Functions ve Middleware
- Performance monitoring
- Security best practices
- CI/CD entegrasyonu
Production Checklist:
- ✅ Environment variables production için ayarlandı
- ✅ Custom domain yapılandırıldı
- ✅ Analytics ve monitoring aktif
- ✅ Security headers eklendi
- ✅ Database migrations otomatize edildi
- ✅ CI/CD pipeline kuruldu
- ✅ Error tracking entegre edildi
- ✅ Backup stratejisi belirlendi
Next.js Full-Stack serisini tamamladınız! App Router'dan database entegrasyonuna, authentication'dan production deployment'a kadar modern full-stack development'ın tüm temellerini öğrendiniz.
Başarılı projeler dileriz!
Projenizi Hayata Geçirelim
Web sitesi, mobil uygulama veya yapay zeka çözümü mü arıyorsunuz? Fikirlerinizi birlikte değerlendirelim.
Ücretsiz Danışmanlık Alın