|$ curl https://forge-ai.dev/api/markdown?path=docs/deploy/netlify
$cat docs/netlify-—-deployment.md
updated Recently·25 min read·published

Netlify — Deployment

DeploymentNetlifyBeginner to Intermediate
Introduction

Netlify is a cloud platform that combines global CDN, serverless functions, form handling, and split testing into a single deployment pipeline. It excels at deploying JAMstack sites with instant rollback, deploy previews, and branch-based workflows.

Like Vercel, Netlify connects to your Git repository and auto-deploys on every push. Its unique strengths include native form handling, branch-based split testing, and a flexible redirects engine.

Netlify CLI

The Netlify CLI provides local development, deployment, and configuration management from the command line.

netlify-cli.sh
Bash
1# Install Netlify CLI
2npm install -g netlify-cli
3
4# Login to Netlify
5netlify login
6
7# Initialize a new site
8netlify init
9
10# Link an existing site
11netlify link
12
13# Deploy to a draft URL (preview)
14netlify deploy
15
16# Deploy to production
17netlify deploy --prod
18
19# Run local dev server with functions
20netlify dev
21
22# Open site in browser
23netlify open
24
25# List all sites
26netlify sites:list
netlify.toml Configuration

The netlify.toml file defines all deployment configuration: build settings, redirects, headers, functions, and split testing. It belongs at the root of your repository.

netlify.toml
TOML
1[build]
2 command = "npm run build"
3 publish = "dist"
4 functions = "netlify/functions"
5
6[build.environment]
7 NODE_VERSION = "20"
8 NPM_VERSION = "10"
9
10# Redirects
11[[redirects]]
12 from = "/api/*"
13 to = "/.netlify/functions/:splat"
14 status = 200
15
16[[redirects]]
17 from = "/old-path"
18 to = "/new-path"
19 status = 301
20
21# Headers
22[[headers]]
23 for = "/*"
24 [headers.values]
25 X-Frame-Options = "DENY"
26 X-Content-Type-Options = "nosniff"
27 Referrer-Policy = "strict-origin-when-cross-origin"
28
29[[headers]]
30 for = "/assets/*"
31 [headers.values]
32 Cache-Control = "public, max-age=31536000, immutable"
33
34# Split Testing
35[[split_testing]]
36 branches = ["main", "experiment"]
37 percentage = 90
38
39# Edge Functions
40[[edge_functions]]
41 function = "hello"
42 path = "/hello"
43
44# Environment Variables (per context)
45[context.production.environment]
46 API_URL = "https://api.example.com"
47
48[context.deploy-preview.environment]
49 API_URL = "https://staging-api.example.com"
50
51[context.branch-deploy.environment]
52 API_URL = "https://dev-api.example.com"

info

Netlify supports [context]-specific settings. You can have different build commands, environment variables, and even different publish directories for production, deploy preview, and branch deploy contexts — all in a single netlify.toml file.
Deploy Previews

Netlify creates a unique preview URL for every deploy, including automatic PR comments. This enables visual review and testing before merging.

deploy-previews.sh
Bash
1# Deploy preview URL pattern:
2https://[hash]--[site-name].netlify.app
3
4# Examples:
5https://a1b2c3d4--my-app.netlify.app
6https://e5f6g7h8--my-app.netlify.app
7
8# Deploy a branch-specific preview
9netlify deploy --branch feat-new-ui
10
11# Deploy with alias for consistent URL
12netlify deploy --alias staging
13
14# Lock a deploy to prevent auto-overwrite
15netlify deploy --prod --message "v2.0.0 release"
🔥

pro tip

Use deploy previews with Netlify's branch subdomain feature: a branch called staging automatically deploys to staging--my-app.netlify.app. This gives you persistent, predictable URLs for long-running feature branches.
Serverless Functions

Netlify Functions run serverless code in response to HTTP requests. Functions are stored in the directory specified by functions in netlify.toml (default: netlify/functions).

netlify/functions/hello.ts
TypeScript
1// netlify/functions/hello.ts
2import { Handler } from '@netlify/functions';
3
4export const handler: Handler = async (event, context) => {
5 const { name = 'World' } = event.queryStringParameters || {};
6
7 return {
8 statusCode: 200,
9 headers: {
10 'Content-Type': 'application/json',
11 },
12 body: JSON.stringify({
13 message: `Hello, ${name}!`,
14 path: event.path,
15 httpMethod: event.httpMethod,
16 }),
17 };
18};
19
20// Function with environment variables
21// netlify/functions/submit-form.ts
22export const handler: Handler = async (event) => {
23 if (event.httpMethod !== 'POST') {
24 return { statusCode: 405, body: 'Method Not Allowed' };
25 }
26
27 const body = JSON.parse(event.body || '{}');
28 const apiKey = process.env.FORM_API_KEY;
29
30 const response = await fetch('https://api.example.com/submit', {
31 method: 'POST',
32 headers: {
33 Authorization: `Bearer ${apiKey}`,
34 'Content-Type': 'application/json',
35 },
36 body: JSON.stringify(body),
37 });
38
39 return {
40 statusCode: response.status,
41 body: await response.text(),
42 };
43};

Function limits:

PropertyFreePro
Memory1024 MB1024 MB
Execution Time10s60s (extendable to 900s)
Invocations125K/month2M/month
Bundle Size50 MB250 MB
Redirects & Headers

Netlify offers two ways to configure redirects and headers: the _redirects file and netlify.toml. The _redirects file lives in the publish directory and uses a simpler syntax.

_redirects
TEXT
1# _redirects — Simple syntax
2# Format: [from] [to] [status]
3
4# Redirect all /api/* requests to serverless functions
5/api/* /.netlify/functions/:splat 200
6
7# Permanent redirects (301)
8/old-blog /blog 301
9
10# Splat parameters
11/blog/:slug /article/:slug 301
12
13# Country-based redirects
14/* /europe /eu 302 Country=de,fr,it
15
16# Language-based redirects
17/* /spanish /es 302 Accept-Language=es
18
19# Query parameter redirects
20/shop /sale 302 q=clearance
21
22# SPA fallback (serve index.html for all routes)
23/* /index.html 200

Equivalent configuration in netlify.toml:

netlify-toml-redirects.toml
TOML
1# netlify.toml redirects (equivalent to _redirects above)
2
3[[redirects]]
4 from = "/api/*"
5 to = "/.netlify/functions/:splat"
6 status = 200
7
8[[redirects]]
9 from = "/old-blog/*"
10 to = "/blog/:splat"
11 status = 301
12
13# SPA fallback
14[[redirects]]
15 from = "/*"
16 to = "/index.html"
17 status = 200
18
19# Splat / named parameters
20[[redirects]]
21 from = "/blog/:slug"
22 to = "/article/:slug"
23 status = 301
Edge Functions

Netlify Edge Functions run at the CDN edge, globally distributed, with near-zero cold starts. They use Deno runtime and can modify requests and responses in flight.

netlify/edge-functions/hello.ts
TypeScript
1// netlify/edge-functions/hello.ts
2import type { Context } from '@netlify/edge-functions';
3
4export default async (request: Request, context: Context) => {
5 const url = new URL(request.url);
6
7 // Geolocation data available at the edge
8 const country = context.geo.country?.code || 'unknown';
9 const city = context.geo.city || 'unknown';
10
11 // A/B testing based on cookie
12 const experiment = context.cookies.get('experiment');
13 if (!experiment) {
14 context.cookies.set({
15 name: 'experiment',
16 value: Math.random() > 0.5 ? 'A' : 'B',
17 });
18 }
19
20 // Rewrite based on country
21 if (country === 'DE' && !url.pathname.startsWith('/de')) {
22 return new Response(null, {
23 status: 302,
24 headers: { Location: `/de${url.pathname}` },
25 });
26 }
27
28 return context.next();
29};
30
31// netlify/edge-functions/geolocation.ts
32export default async (request: Request, context: Context) => {
33 const response = await context.next();
34 const text = await response.text();
35
36 // Inject geolocation data into the HTML
37 const injected = text.replace(
38 '</body>',
39 `<script>
40 window.GEO = ${JSON.stringify(context.geo)};
41 </script></body>`
42 );
43
44 return new Response(injected, response);
45};
$Blueprint — Engineering Documentation·Section ID: NETLIFY-01·Revision: 1.0