[{"data":1,"prerenderedAt":553},["ShallowReactive",2],{"\u002Fblog\u002Fdeploy-nuxt-to-vercel-with-custom-domains-step-by-step-data":3},{"post":4,"surround":542},{"id":5,"title":6,"alternates":7,"authors":8,"badge":14,"body":16,"date":522,"description":26,"extension":523,"head":524,"hero_image_url":25,"json_ld":525,"meta":526,"navigation":532,"ogImage":524,"outbound_links":533,"path":534,"primary_keyword":528,"related_articles":535,"robots":524,"schemaOrg":524,"search_intent":536,"seo":537,"sitemap":538,"stem":539,"supporting_keywords":540,"__hash__":541},"blog_en\u002Fblog\u002Fdeploy-nuxt-to-vercel-with-custom-domains-step-by-step.md","Deploy Nuxt to Vercel with Custom Domains, Step by Step",[],[9],{"name":10,"to":11,"avatar":12},"Tom Han","https:\u002F\u002Fx.com\u002Ftomhan245",{"src":13},"https:\u002F\u002Fcdn.shipahe.ad\u002Ftomhan.webp",{"label":15},"How-to",{"type":17,"value":18,"toc":490},"minimark",[19,28,32,37,40,45,60,67,71,74,79,82,87,91,94,99,103,134,138,149,153,160,164,169,185,189,197,219,223,237,241,245,276,280,283,302,305,309,324,328,336,340,344,360,364,391,395,398,401,405,409,412,416,466,470],[20,21,22],"figure",{},[23,24],"img",{"src":25,"alt":26,"style":27},"https:\u002F\u002Fshipahe.ad\u002Fimages\u002Fblog\u002Fdeploy-nuxt-to-vercel-with-custom-domains-step-by-step\u002Fpost-375.webp","Ship a Nuxt 3 app on Vercel with correct build scripts, env vars, custom domains, previews, caching, redirects, and safe rollbacks. Clear, tested steps.","max-width:100%;border-radius:12px",[29,30,31],"p",{},"Shipping a Nuxt 3 app on Vercel is quick if you line up build scripts, runtime config, and DNS the right way. This is the exact deployment flow we use for SaaS apps with auth, payments, i18n, and admin. Follow it once, then repeat it for every feature branch and release without surprises.",[33,34,36],"h2",{"id":35},"_1-prepare-your-nuxt-project-for-vercel","1) Prepare your Nuxt project for Vercel",[29,38,39],{},"Lock the basics so your cloud build matches your local build.",[41,42,44],"h3",{"id":43},"match-node-versions","Match Node versions",[46,47,48,52],"ul",{},[49,50,51],"li",{},"Use Node 18 or 20. Pick one and use it everywhere.",[49,53,54,55,59],{},"Add an engines field or an ",[56,57,58],"em",{},".nvmrc"," so teammates and CI use the same version.",[61,62,63],"pre",{},[64,65,66],"code",{},"{\n  \"engines\": { \"node\": \"20.x\" }\n}\n",[41,68,70],{"id":69},"set-build-and-start-scripts","Set build and start scripts",[29,72,73],{},"Vercel auto-detects Nuxt, but explicit scripts avoid edge cases.",[61,75,76],{},[64,77,78],{},"{\n  \"scripts\": {\n    \"dev\": \"nuxt dev\",\n    \"build\": \"nuxt build\",\n    \"start\": \"node .output\u002Fserver\u002Findex.mjs\"\n  }\n}\n",[29,80,81],{},"Before pushing, run a local production build to catch SSR issues early:",[61,83,84],{},[64,85,86],{},"pnpm build && node .output\u002Fserver\u002Findex.mjs\n# or: npm run build && node .output\u002Fserver\u002Findex.mjs\n",[41,88,90],{"id":89},"confirm-ssr-image-domains-and-the-vercel-preset","Confirm SSR, image domains, and the Vercel preset",[29,92,93],{},"Most SaaS apps need SSR for auth, server routes, and webhooks. Make that explicit and keep server-only code out of the client bundle.",[61,95,96],{},[64,97,98],{},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  ssr: true,\n  nitro: {\n    \u002F\u002F Nuxt auto-detects Vercel, this keeps it explicit\n    preset: 'vercel'\n  },\n  image: {\n    \u002F\u002F Only if you use @nuxt\u002Fimage or remote images\n    domains: ['images.example.com', 'cdn.example.com']\n  }\n})\n",[41,100,102],{"id":101},"keep-server-code-server-only","Keep server code server-only",[46,104,105,116,131],{},[49,106,107,108,111,112,115],{},"Put API handlers under ",[56,109,110],{},"server\u002Fapi",". Each file becomes an endpoint. Test them locally with ",[64,113,114],{},"curl"," before you deploy.",[49,117,118,119,122,123,126,127,130],{},"Do not reference ",[64,120,121],{},"window"," or ",[64,124,125],{},"document"," in server code. Guard any client-only usage with ",[64,128,129],{},"process.client"," checks.",[49,132,133],{},"Enforce protected routes in server middleware so SSR does not leak restricted content.",[41,135,137],{"id":136},"clean-artifacts-before-first-deploy","Clean artifacts before first deploy",[29,139,140,141,144,145,148],{},"Delete ",[56,142,143],{},".nuxt"," and ",[56,146,147],{},".output",", then rebuild. You want the same clean state Vercel will use.",[33,150,152],{"id":151},"_2-configure-environment-variables-and-runtime-config","2) Configure environment variables and runtime config",[29,154,155,156,159],{},"Separate what the browser can see from what it cannot. Map everything to ",[64,157,158],{},"runtimeConfig",".",[41,161,163],{"id":162},"define-runtime-config-in-nuxt","Define runtime config in Nuxt",[61,165,166],{},[64,167,168],{},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    \u002F\u002F server-only\n    stripeSecret: '',\n    jwtSecret: '',\n    \u002F\u002F exposed to browser\n    public: {\n      appUrl: '',\n      stripePublicKey: ''\n    }\n  }\n})\n",[46,170,171,178],{},[49,172,173,174,177],{},"Anything in ",[64,175,176],{},"runtimeConfig.public"," can be read in the browser. Only put non-sensitive values there.",[49,179,180,181,184],{},"On Vercel, variables prefixed with ",[64,182,183],{},"NUXT_PUBLIC_"," are exposed to the client. Everything else is server-only.",[41,186,188],{"id":187},"add-env-vars-per-vercel-environment","Add env vars per Vercel environment",[46,190,191,194],{},[49,192,193],{},"In Project Settings, add variables for Production, Preview, and Development. Use different keys for staging databases, payment test keys, and email sandboxes.",[49,195,196],{},"Use the CLI to sync values locally so your app behaves the same on your machine and in the cloud:",[61,198,199,214],{},[64,200,201,202,207,210],{},"# Link your directory to a Vercel project if needed\nvercel link\n",[203,204,206],"h1",{"id":205},"pull-cloud-vars-into-a-local-file","Pull cloud vars into a local file",[29,208,209],{},"vercel env pull .env.local",[203,211,213],{"id":212},"add-a-new-key-interactively-choose-env-when-prompted","Add a new key interactively (choose env when prompted)",[29,215,216],{},[64,217,218],{},"vercel env add NUXT_PUBLIC_APP_URL\n",[41,220,222],{"id":221},"know-what-is-read-at-build-time-vs-request-time","Know what is read at build time vs request time",[46,224,225,231,234],{},[49,226,227,228,230],{},"SSR pages and API routes read ",[64,229,158],{}," on each request.",[49,232,233],{},"Prerendered routes capture values at build time. If a value must update without a rebuild, do not prerender that route.",[49,235,236],{},"Use a Preview deployment to test a config change without touching Production.",[33,238,240],{"id":239},"_3-domains-previews-redirects-and-caching","3) Domains, previews, redirects, and caching",[41,242,244],{"id":243},"add-and-verify-your-custom-domain","Add and verify your custom domain",[246,247,248,255,273],"ol",{},[49,249,250,251,254],{},"In your Vercel project, add both the apex domain and the ",[56,252,253],{},"www"," subdomain.",[49,256,257,258],{},"Point DNS to Vercel:",[46,259,260,267],{},[49,261,262,263,159],{},"Apex: A record to ",[264,265,266],"strong",{},"76.76.21.21",[49,268,269,270,159],{},"www: CNAME to ",[264,271,272],{},"cname.vercel-dns.com",[49,274,275],{},"Wait for propagation, then pick a primary domain and keep one canonical host.",[41,277,279],{"id":278},"set-redirects-and-headers-in-nuxt","Set redirects and headers in Nuxt",[29,281,282],{},"Keep SEO and analytics clean with a single host and predictable cache policy. Use Nuxt route rules so your config travels with the repo.",[61,284,285,297],{},[64,286,287,288],{},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  routeRules: {\n    \u002F\u002F Canonical host: force www\n    '\u002F*': { redirect: { to: 'https:\u002F\u002Fwww.example.com\u002F**', statusCode: 308 } },\n",[61,289,294],{"className":290,"code":292,"language":293},[291],"language-text","\u002F\u002F Cache mostly static pages with ISR\n'\u002F': { isr: 300 },\n'\u002Fblog\u002F**': { isr: 600 },\n\n\u002F\u002F Never cache private pages or APIs\n'\u002Fdashboard\u002F**': { cache: false },\n'\u002Fapi\u002F**': { headers: { 'cache-control': 'no-store' } }\n","text",[64,295,292],{"__ignoreMap":296},"",[29,298,299],{},[64,300,301],{},"}\n})\n",[29,303,304],{},"You can also configure redirects in the Vercel dashboard if you prefer UI control. Keep the rule in one place to avoid conflicts.",[41,306,308],{"id":307},"use-preview-deployments-with-real-staging-data","Use Preview deployments with real staging data",[46,310,311,314,317],{},[49,312,313],{},"Connect your Git repo. Every push creates a unique Preview URL. Treat this as staging for product owners and QA.",[49,315,316],{},"Add Preview env vars so previews use staging databases and payment test keys. Never point Preview at production data.",[49,318,319,320,323],{},"If you need a stable preview hostname, attach a subdomain such as ",[56,321,322],{},"preview.example.com"," to the Preview environment in Project Settings.",[41,325,327],{"id":326},"static-assets","Static assets",[46,329,330,333],{},[49,331,332],{},"Ship hashed filenames so long-lived caching is safe. Nuxt handles this by default for built assets.",[49,334,335],{},"Whitelist any remote image domains to avoid production blocks.",[33,337,339],{"id":338},"_4-ship-verify-and-monitor","4) Ship, verify, and monitor",[41,341,343],{"id":342},"deploy-from-git-or-the-cli","Deploy from Git or the CLI",[46,345,346,349],{},[49,347,348],{},"Git-based flow: merge to your main branch to trigger a Production deployment.",[49,350,351,352,355,356,359],{},"CLI flow: ",[64,353,354],{},"vercel"," for a preview, then ",[64,357,358],{},"vercel --prod"," for production.",[41,361,363],{"id":362},"run-the-critical-path-checks","Run the critical path checks",[46,365,366,369,382,385,388],{},[49,367,368],{},"Open the homepage and a few deep links. Watch server logs in the Vercel dashboard for slow queries and missing assets.",[49,370,371,372,381],{},"Exercise APIs with valid and invalid inputs. Example: ",[64,373,374,375],{},"curl -i ",[376,377,378],"a",{"href":378,"rel":379},"https:\u002F\u002Fyourdomain.com\u002Fapi\u002Fhealth",[380],"nofollow"," and check status codes.",[49,383,384],{},"Auth flows end to end: sign up, email magic link, social login, password reset, gated routes. Confirm logout clears cookies and sessions.",[49,386,387],{},"Payments in test mode: create a plan, run checkout, verify webhooks update user entitlements and invoices. Confirm idempotency so retries do not double-charge.",[49,389,390],{},"Internationalization: switch locales, ensure dynamic routes and metadata render correctly on SSR.",[41,392,394],{"id":393},"turn-on-your-platform-features","Turn on your platform features",[29,396,397],{},"If you built on our Nuxt SaaS starter, you can switch on analytics, SEO meta and sitemap generation, scheduled cron jobs for reports or reminders, and transactional emails for password resets and welcomes immediately after go-live. These ship with the kit, so you do not have to glue them on later.",[29,399,400],{},"If content and backlinks are your next bottleneck, teams often pair their app with RankGoat to write posts, secure dofollow links, and fix indexing so new features get discovered.",[33,402,404],{"id":403},"_5-roll-back-fast-and-avoid-pitfalls","5) Roll back fast and avoid pitfalls",[41,406,408],{"id":407},"instant-rollbacks","Instant rollbacks",[29,410,411],{},"Each Vercel deployment is immutable. If production is off, open the last known good deployment in the dashboard and promote it to Production. You can also point your production alias back to that deployment URL using the CLI. The switch is instant because both builds already exist.",[41,413,415],{"id":414},"common-pitfalls-to-avoid","Common pitfalls to avoid",[46,417,418,424,436,442,448,454,460],{},[49,419,420,423],{},[264,421,422],{},"Missing Preview env vars."," A feature works locally but fails on a branch because the variable only exists in Production. Mirror critical keys with safe staging values.",[49,425,426,429,430,432,433,435],{},[264,427,428],{},"Leaking secrets to the client."," Never pass server-only values via ",[64,431,176],{}," or with a ",[64,434,183],{}," prefix.",[49,437,438,441],{},[264,439,440],{},"Remote images blocked."," Production will fail requests if domains are not whitelisted. Add them before launch.",[49,443,444,447],{},[264,445,446],{},"Wrong SSR vs prerender choice."," If a page must reflect per-request data, do not prerender it. If it is mostly static, give it ISR for speed.",[49,449,450,453],{},[264,451,452],{},"Large serverless bundles."," Keep server routes lean. Import only what you use. Move browser-only packages out of server code.",[49,455,456,459],{},[264,457,458],{},"No canonical redirect."," Redirect non-www to www or the reverse so SEO and analytics are not split.",[49,461,462,465],{},[264,463,464],{},"Node mismatch."," Teams on different Node versions see build-only bugs. Pin Node and use the same version in Vercel.",[33,467,469],{"id":468},"key-takeaways","Key takeaways",[46,471,472,475,481,484,487],{},[49,473,474],{},"Lock Node, scripts, SSR, and image domains before connecting Vercel.",[49,476,477,478,480],{},"Store secrets in Vercel and map them to ",[64,479,158],{},". Keep Production and Preview separate.",[49,482,483],{},"Attach your custom domain, set one canonical host, and configure redirects in code or the dashboard.",[49,485,486],{},"Use route-level caching: ISR for mostly static pages, no-store for auth and APIs.",[49,488,489],{},"Rely on immutable deployments to roll back instantly when needed.",{"title":296,"searchDepth":491,"depth":491,"links":492},2,[493,501,506,512,517,521],{"id":35,"depth":491,"text":36,"children":494},[495,497,498,499,500],{"id":43,"depth":496,"text":44},3,{"id":69,"depth":496,"text":70},{"id":89,"depth":496,"text":90},{"id":101,"depth":496,"text":102},{"id":136,"depth":496,"text":137},{"id":151,"depth":491,"text":152,"children":502},[503,504,505],{"id":162,"depth":496,"text":163},{"id":187,"depth":496,"text":188},{"id":221,"depth":496,"text":222},{"id":239,"depth":491,"text":240,"children":507},[508,509,510,511],{"id":243,"depth":496,"text":244},{"id":278,"depth":496,"text":279},{"id":307,"depth":496,"text":308},{"id":326,"depth":496,"text":327},{"id":338,"depth":491,"text":339,"children":513},[514,515,516],{"id":342,"depth":496,"text":343},{"id":362,"depth":496,"text":363},{"id":393,"depth":496,"text":394},{"id":403,"depth":491,"text":404,"children":518},[519,520],{"id":407,"depth":496,"text":408},{"id":414,"depth":496,"text":415},{"id":468,"depth":491,"text":469},"2026-07-28","md",null,"[object Object]",{"tags":527},[528,354,529,530,531],"nuxt","deployment","saas","vue",true,[],"\u002Fblog\u002Fdeploy-nuxt-to-vercel-with-custom-domains-step-by-step",[],"Informational",{"title":6,"description":26},{"loc":534},"blog\u002Fdeploy-nuxt-to-vercel-with-custom-domains-step-by-step",[528,354,529,530,531],"dpoOJzGwr2IrcyhyGIRwL3rzPD9ZE7ZjLOQRXpJtm_o",[543,548],{"title":544,"path":545,"stem":546,"description":547,"children":-1},"Buy vs build Nuxt starter kits: ROI, timeline, and revenue","\u002Fblog\u002Fbuy-vs-build-nuxt-starter-kits-roi-and-time-to-market","blog\u002Fbuy-vs-build-nuxt-starter-kits-roi-and-time-to-market","Founder case study quantifies buy vs build Nuxt: 300 vs 80 hours, 15-day launch, earlier revenue, and clear ROI from a Nuxt SaaS starter kit.",{"title":549,"path":550,"stem":551,"description":552,"children":-1},"How to Build a SaaS with AI Coding Agents – Step-by-Step","\u002Fblog\u002Fhow-to-build-a-saas-with-ai","blog\u002Fhow-to-build-a-saas-with-ai","Learn how to build a SaaS with AI agents in 2026. Discover the exact workflow to use tools like Cursor and ShipAhead to launch your product in days.",1785225730770]