skills/deployment/netlify-deploy/SKILL.md
Use this skill when the user says 'deploy to Netlify', 'Netlify setup', 'netlify-deploy', or needs to deploy a static site or serverless functions to Netlify with build configuration and custom domains. Do NOT use for Railway, Vercel, or VPS deployments.
npx skillsauth add cwinvestments/memstack memstack-deployment-netlify-deployInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Validates build config, redirects, environment variables, and deployment readiness for Netlify static/SPA hosting.
When this skill activates, output:
🌐 Netlify Deploy — Running pre-flight checks...
Then execute the protocol below.
| Context | Status | |---------|--------| | User says "deploy to netlify" or "netlify deploy" | ACTIVE | | User says "deploy frontend" or "deploy static site" | ACTIVE | | Preparing a React/Vue/Svelte/Next.js static export for hosting | ACTIVE | | Deploying a backend service or API server | DORMANT — use railway-deploy | | Discussing Netlify pricing or features generally | DORMANT |
| Trap | Reality Check |
|------|---------------|
| "I'll configure it in the Netlify UI" | netlify.toml is version-controlled and portable. UI settings get lost across teams. |
| "Redirects work fine without _redirects" | SPA routing breaks on refresh without /* /index.html 200. Every SPA needs this. |
| "Environment vars are the same everywhere" | Build-time vars (baked into JS bundle) vs runtime vars (Netlify Functions) are different. NEXT_PUBLIC_ prefix exposes to client. |
| "The API proxy just works" | /api/* redirects must point to your actual backend URL. Forgetting to update after backend redeploy breaks the frontend. |
| "I'll check the deploy after pushing" | netlify deploy --build locally catches build failures before they hit production. Always build locally first. |
Look for Netlify config and verify build settings:
# Check for Netlify configuration
ls netlify.toml _redirects _headers 2>/dev/null
# Check package.json for build script
cat package.json | grep -A2 '"scripts"' | grep '"build"'
If netlify.toml exists, verify it:
# Expected structure
[build]
command = "npm run build" # or "yarn build", "pnpm build"
publish = "dist" # or "build", "out", ".next" (varies by framework)
[build.environment]
NODE_VERSION = "20" # Pin Node version for reproducible builds
| Framework | Build Command | Publish Directory |
|-----------|--------------|-------------------|
| React (CRA) | npm run build | build |
| React (Vite) | npm run build | dist |
| Next.js (static) | next build && next export | out |
| Vue | npm run build | dist |
| Svelte/SvelteKit | npm run build | build |
| Astro | npm run build | dist |
| Plain HTML | — | . or public |
Flag if: netlify.toml missing or publish directory doesn't match framework default.
# Check redirect files
cat netlify.toml 2>/dev/null | grep -A5 '\[\[redirects\]\]'
cat _redirects 2>/dev/null
Check for the API proxy pattern (frontend → backend):
# netlify.toml — API proxy to Railway/external backend
[[redirects]]
from = "/api/*"
to = "https://your-backend.up.railway.app/api/:splat"
status = 200
force = true
Or in _redirects:
/api/* https://your-backend.up.railway.app/api/:splat 200
Verify:
status = 200 (proxy, not redirect — preserves the URL for the client)force = true if the proxy should override static files at the same pathlocalhost:3000 — update to productionFlag if: Code references /api/ paths but no proxy redirect is configured.
Single-page apps need a catch-all redirect so deep links and page refreshes work:
# Check for SPA redirect
grep -r "\/\*.*\/index\.html\|\/\*.*200" netlify.toml _redirects 2>/dev/null
Required for SPAs (React Router, Vue Router, etc.):
# In _redirects (must be LAST rule — order matters)
/* /index.html 200
Or in netlify.toml:
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Flag if: Project uses client-side routing but no catch-all redirect exists. Symptoms: pages work when navigated to via links, but return 404 on direct URL access or refresh.
Note: Next.js static export handles this differently — each page is pre-rendered as its own HTML file. SPA redirect is NOT needed for static Next.js.
# Find env vars used in frontend code
grep -rn "process\.env\.\|import\.meta\.env\.\|VITE_\|NEXT_PUBLIC_\|REACT_APP_" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" . | grep -v node_modules
Build-time vs runtime separation:
| Prefix | Framework | When Available | Exposed to Client? |
|--------|-----------|---------------|-------------------|
| REACT_APP_ | CRA | Build time | ⚠️ YES — baked into JS bundle |
| NEXT_PUBLIC_ | Next.js | Build time | ⚠️ YES — baked into JS bundle |
| VITE_ | Vite | Build time | ⚠️ YES — baked into JS bundle |
| No prefix | Any | Build time only | ❌ No — server-side/build scripts only |
Critical security check:
# Search for secrets that might be exposed to client
grep -rn "NEXT_PUBLIC_.*SECRET\|NEXT_PUBLIC_.*KEY\|VITE_.*SECRET\|REACT_APP_.*SECRET" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.env*" . | grep -v node_modules
Flag if: Any secret (API keys with write access, database URLs, auth secrets) uses a client-exposed prefix. These are baked into the JavaScript bundle and visible to anyone who opens DevTools.
Output: List each variable with where to set it:
netlify.toml [build.environment] (for non-sensitive build config like NODE_VERSION)# Check for domain configuration
cat netlify.toml 2>/dev/null | grep -A5 '\[context\]'
Verify in Netlify dashboard:
*.netlify.app or A record to Netlify load balancer)Flag if: Domain is added but DNS hasn't propagated or SSL shows "Waiting for DNS verification."
# Check for serverless functions
ls netlify/functions/ functions/ 2>/dev/null
cat netlify.toml 2>/dev/null | grep 'functions'
If functions exist, verify:
netlify.toml: [functions] directory = "netlify/functions"export const handler = async (event, context) => { ... }# Check for security headers
cat netlify.toml 2>/dev/null | grep -A10 '\[\[headers\]\]'
cat _headers 2>/dev/null
Recommended security headers:
# netlify.toml
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
Build locally to catch errors before Netlify builds:
# Local build test
npm run build
# Check output directory exists and has content
ls -la dist/ # or build/, out/, etc.
# Check for common issues
grep -rn "http://localhost\|http://127\.0\.0\.1" dist/ 2>/dev/null
| Check | Command | Pass Criteria |
|-------|---------|--------------|
| Build passes | npm run build | Exit code 0, no errors |
| Output directory exists | ls dist/ | Has index.html and assets |
| No localhost in build | grep dist/ for localhost | Zero matches |
| _redirects in output | ls dist/_redirects | Exists if using _redirects approach |
| Env vars documented | Check .env.example | All client vars listed |
| No secrets in client code | grep for exposed secrets | Zero matches |
| Git clean | git status | All changes committed |
Output pre-deploy summary:
🌐 Netlify Deploy — Pre-flight Complete
Project: [name] ([framework])
Build: ✅ passes → [publish directory]
Redirects: ✅ SPA routing + API proxy configured
Env vars: ✅ 8 build-time vars, no secrets exposed
Domain: ✅ [domain] with SSL
Functions: ✅ 2 functions in netlify/functions/
Headers: ✅ security headers configured
Ready to deploy.
Preview: netlify deploy --build
Production: netlify deploy --build --prod
After deployment completes:
/dashboard/settings) — should load, not 404https://[domain] — padlock should appear, no mixed content warningshttp://[domain] — should redirect to https://Rollback plan:
tools
Use when the user says 'save diary', 'log session', 'wrapping up', or at end of a productive session.
tools
Use when the user says 'submit to marketplace', 'publish my skill', 'share this skill', 'list on marketplace', 'submit plugin', 'publish to community', or needs to submit a skill or plugin to a community marketplace via PR. Do NOT use for building skills or writing plugin code.
development
Use when the user says 'write browser tests', 'test this page', 'playwright test', 'e2e test', 'end to end test', 'browser test', 'test the UI', or needs Playwright-based browser testing for a web application. Do NOT use for unit tests, API tests, or non-browser testing.
development
Use when the user says 'teach me', 'explain as you go', 'mentor mode', 'walk me through', 'help me learn', 'explain why', 'learning mode', or wants real-time plain language narration of decisions and tradeoffs while building. Do NOT use for code review or debugging.