<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sumeet Shroff Developer</title>
    <description>The latest articles on Forem by Sumeet Shroff Developer (@sumeet_shrofffreelancer_).</description>
    <link>https://forem.com/sumeet_shrofffreelancer_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3688027%2Fd12e1a0b-56b7-49e1-8cc1-4e01c1e936ed.png</url>
      <title>Forem: Sumeet Shroff Developer</title>
      <link>https://forem.com/sumeet_shrofffreelancer_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sumeet_shrofffreelancer_"/>
    <language>en</language>
    <item>
      <title>Deploy Next.js on cPanel/WHM with Phusion Passenger</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Sun, 08 Feb 2026 02:11:49 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/deploy-nextjs-on-cpanelwhm-with-phusion-passenger-de4</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/deploy-nextjs-on-cpanelwhm-with-phusion-passenger-de4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ucbxp4w6axmugz14pkp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ucbxp4w6axmugz14pkp.jpg" alt="Deploy Next.js on cPanel/WHM with Phusion Passenger" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step-by-step practical guide to host production Next.js apps on cPanel/WHM with Phusion Passenger — covers architecture, prerequisites, build/start scripts, deployment, SSL, and troubleshooting.&lt;/p&gt;

&lt;p&gt;! &lt;a href="https://prateeksha.com/images/blog/deploy-nextjs-cpanel-phusion-passenger.jpg" rel="noopener noreferrer"&gt;Deploy Next.js on cPanel/WHM with Phusion Passenger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Passenger to run Next.js SSR or a custom server on cPanel/WHM without a separate Node host.&lt;/li&gt;
&lt;li&gt;Choose SSR (next start / custom server) or static export (next export) depending on features and hosting limits.&lt;/li&gt;
&lt;li&gt;Validate Node versions, prepare build/start scripts, set process.env.PORT, and use cPanel’s Node.js manager when available.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this approach
&lt;/h2&gt;

&lt;p&gt;This guide shows practical, repeatable steps to run production Next.js on cPanel/WHM using Phusion Passenger. It targets developers with cPanel accounts and host admins running WHM. The goal: get SSR or a production Node server working reliably on shared or managed hosting when Passenger is available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js: can run as SSR (Node server) or as a static export. SSR requires a Node runtime.&lt;/li&gt;
&lt;li&gt;Node.js: executes the Next.js server code.&lt;/li&gt;
&lt;li&gt;Phusion Passenger: spawns and manages Node processes behind Apache or nginx, forwarding HTTP requests.&lt;/li&gt;
&lt;li&gt;cPanel/WHM: controls domains, SSL, and (sometimes) a Node.js app manager that hides Passenger details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Request flow (typical Apache + Passenger)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser requests &lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Apache routes the request to Passenger, which manages the Node process.&lt;/li&gt;
&lt;li&gt;Passenger starts your startup file; your app listens on process.env.PORT.&lt;/li&gt;
&lt;li&gt;Next.js handles rendering and responds via Passenger.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Decide SSR vs static export&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSR: required for getServerSideProps, server-side APIs, or dynamic per-request rendering.&lt;/li&gt;
&lt;li&gt;Static export: best for fully static sites (no Passenger needed, simplest on shared hosting).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preparation checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Confirm Passenger is available via cPanel or WHM (ask support if unsure).&lt;/li&gt;
&lt;li&gt;Confirm available Node versions match your Next.js requirements (Node 16/18/20 are common).&lt;/li&gt;
&lt;li&gt;Prefer SSH access for builds and installs; cPanel UI can help but SSH simplifies reproducible deploys.&lt;/li&gt;
&lt;li&gt;If you manage the server, plan to enable Passenger through WHM/EasyApache for a supported install.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prepare your Next.js app for production
&lt;/h2&gt;

&lt;p&gt;Repository layout (recommended):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;next-app/

&lt;ul&gt;
&lt;li&gt;package.json&lt;/li&gt;
&lt;li&gt;server.js        &amp;lt;-- startup file for Passenger (SSR)&lt;/li&gt;
&lt;li&gt;next.config.js&lt;/li&gt;
&lt;li&gt;pages/ or app/&lt;/li&gt;
&lt;li&gt;public/&lt;/li&gt;
&lt;li&gt;.env.production&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Add clear scripts in package.json so Passenger runs the correct production command. Example package.json snippets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV=production node server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build &amp;amp;&amp;amp; next export"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A minimal server.js must honor process.env.PORT so Passenger can route requests to the correct socket/port. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRequestHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;gt; Next.js server started on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Optional: Graceful shutdown hooks&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use NODE_ENV=production and build with next build before starting.&lt;/li&gt;
&lt;li&gt;For fully static sites, run next export and upload the out/ folder into public_html; no Passenger needed.&lt;/li&gt;
&lt;li&gt;Build locally or on the server. If native modules exist, building on the server avoids binary mismatches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enabling Phusion Passenger in WHM
&lt;/h2&gt;

&lt;p&gt;Preferred: use WHM → EasyApache 4 to enable the Passenger Apache module. Steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log into WHM, open EasyApache 4, Customize, add Passenger (if provided), and provision.&lt;/li&gt;
&lt;li&gt;Restart Apache: sudo systemctl restart httpd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If EasyApache doesn’t provide the module, follow Phusion's official installer (advanced). Avoid mixing manual gem installs with EasyApache-managed modules.&lt;/p&gt;

&lt;p&gt;Verify installation (root/WHM):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check Apache modules and passenger utilities with passenger-status and passenger-memory-stats.&lt;/li&gt;
&lt;li&gt;Tail Apache logs: see where errors land and confirm Passenger loaded successfully.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy: upload, install deps, build, and run
&lt;/h2&gt;

&lt;p&gt;Common flow (two options: cPanel Node manager or manual Passenger vhost):&lt;/p&gt;

&lt;p&gt;1) Prepare start scripts&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Either use next start -p $PORT or a server.js wrapper that listens on process.env.PORT.&lt;/li&gt;
&lt;li&gt;Example start script in package.json: "start": "node server.js" or "next start -p $PORT".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Upload your project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Git (recommended), SFTP, or File Manager. Keep the app outside public_html when possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) Install and build on the server (SSH):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use package manager commands to install production deps and build the app:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;cd ~/next-app&lt;br&gt;
  npm ci --only=production --no-audit --progress=false&lt;br&gt;
  npm run build&lt;/p&gt;

&lt;p&gt;4) Configure the cPanel Node.js App manager (if available)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an app, select Node version, set app root and startup file (server.js or package.json start), add env vars, then Start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) Or configure Passenger via vhost include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add PassengerAppRoot, PassengerNodejs (optional), and PassengerAppEnv to a vhost include and restart Apache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) Start and verify&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cPanel: click Start or Restart.&lt;/li&gt;
&lt;li&gt;Passenger spawns the app on first request (or on start depending on settings). Check logs for errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful server commands (root or account as allowed):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tail webserver logs and Passenger status:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tail -f /usr/local/apache/logs/error_log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;passenger-status
passenger-memory-stats
passenger-config restart-app /home/username/path/to/app --ignore-app-not-running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And to follow application logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/path/to/app
tail -f ./npm-debug.log ./next.log ./logs/*.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Domains, env vars, and SSL
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Map your domain/subdomain to the app root or symlink into the domain DocumentRoot.&lt;/li&gt;
&lt;li&gt;Passenger provides process.env.PORT; your app must respect it.&lt;/li&gt;
&lt;li&gt;Set environment variables via cPanel’s Node.js manager, SetEnv in vhost, or a secure server-side secret store. Avoid committing .env files.&lt;/li&gt;
&lt;li&gt;Use cPanel/WHM AutoSSL to install certificates; Passenger works behind Apache’s SSL termination. Redirect 80 → 443 and add HSTS when appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;p&gt;Common symptoms and fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;502/503: app failed to start — ensure npm install &amp;amp;&amp;amp; npm run build ran and that the startup file listens on process.env.PORT.&lt;/li&gt;
&lt;li&gt;Missing modules: reinstall node_modules on the server (npm ci) and match Node versions.&lt;/li&gt;
&lt;li&gt;Dev server running: ensure you’re not running next dev; use next start or node server.js in production.&lt;/li&gt;
&lt;li&gt;Permissions: set correct owner (chown -R username:username) and file modes (644/755).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example package.json start scripts for clarity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "build": "next build",
  "start": "next start -p $PORT"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you cannot access required logs on a shared host, include log excerpts and steps taken when contacting support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case study: shared cPanel deploy (short)
&lt;/h2&gt;

&lt;p&gt;Steps taken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone or upload a minimal Next.js app to /home/username/nodeapps/my-next-app:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/vercel/next.js.git
cd next.js/examples/basic-css
npm install
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Build on the server:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm ci
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Configure cPanel Node manager to use server.js or next start -p $PORT and start the app.&lt;/li&gt;
&lt;li&gt;Example custom server (Express + Next) can be used if needed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server.js
const express = require('express')
const next = require('next')

const dev = false
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() =&amp;gt; {
  const server = express()
  server.get('*', (req, res) =&amp;gt; handle(req, res))

  const port = parseInt(process.env.PORT, 10) || 3000
  server.listen(port, err =&amp;gt; {
    if (err) throw err
    console.log(`&amp;gt; Ready on http://localhost:${port}`)
  })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common fixes discovered: avoid dev server, rebuild native modules on the server, fix file ownership and permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Running Next.js on cPanel/WHM with Phusion Passenger is a practical option for smaller SSR apps when Passenger is available. Validate Node versions, prepare a production build, use a startup script that honors process.env.PORT, and prefer building on the server when native modules exist. For larger scale, consider migrating to a platform designed for Node.js scaling.&lt;/p&gt;

&lt;p&gt;Call to action&lt;br&gt;
Try the steps above on a staging subdomain first. If you need help adapting a project or automating deployment, reach out for assistance.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;https://prateeksha.com&lt;/a&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;
Canonical: &lt;a href="https://prateeksha.com/blog/deploy-nextjs-cpanel-phusion-passenger" rel="noopener noreferrer"&gt;https://prateeksha.com/blog/deploy-nextjs-cpanel-phusion-passenger&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>nextjs</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Next.js Advanced FAQs: Fonts, Images, i18n, Security &amp; Headless Shopify</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Sun, 08 Feb 2026 02:10:16 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/nextjs-advanced-faqs-fonts-images-i18n-security-headless-shopify-3ca6</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/nextjs-advanced-faqs-fonts-images-i18n-security-headless-shopify-3ca6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furtok5w4awbc34g8xbap.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furtok5w4awbc34g8xbap.jpg" alt="Next.js Advanced FAQs: Fonts, Images, i18n, Security &amp;amp; Headless Shopify" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reduce CLS by using Next.js font optimization and proper font-display.&lt;/li&gt;
&lt;li&gt;Configure next/image for remote CDNs and optimize product images for ecommerce.&lt;/li&gt;
&lt;li&gt;Use i18n routing, hreflang, and translated meta for multilingual SEO.&lt;/li&gt;
&lt;li&gt;Keep secrets out of the client, use HTTP-only cookies for auth, and prefer edge-safe Middleware.&lt;/li&gt;
&lt;li&gt;Choose Server Actions or Route Handlers based on complexity; prefer direct-to-storage uploads for files.&lt;/li&gt;
&lt;li&gt;For headless Shopify, combine SSG/SSR, canonical tags, structured data, and image optimizations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This post collects practical solutions to advanced Next.js questions you’ll face as projects scale. Topics include performance (fonts/images), SEO for ecommerce, internationalization, security (env vars and auth), file uploads, Middleware guidance, and approaches for headless Shopify builds. Each section gives focused guidance you can apply quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fonts: reduce CLS and improve load
&lt;/h2&gt;

&lt;p&gt;Why CLS happens: late-loading custom fonts cause layout shifts when the fallback text is replaced.&lt;/p&gt;

&lt;p&gt;How to address it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Next.js built-in font utilities (next/font) to load fonts locally or from Google Fonts.&lt;/li&gt;
&lt;li&gt;Preload critical fonts so the browser fetches them early.&lt;/li&gt;
&lt;li&gt;Use font-display: swap to show fallbacks until the custom font is ready.&lt;/li&gt;
&lt;li&gt;Prefer subsets (latin, latin-ext) for lower payloads.&lt;/li&gt;
&lt;li&gt;Consider variable fonts when appropriate for fewer files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: fewer layout shifts and faster perceived rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote images with next/image
&lt;/h2&gt;

&lt;p&gt;To serve images from Shopify, Cloudinary, or other CDNs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whitelist domains in next.config.js via images.domains or use images.remotePatterns for complex patterns.&lt;/li&gt;
&lt;li&gt;Example entries include your Shopify CDN host and any third-party image services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;next/image will handle automatic resizing, format negotiation (WebP/AVIF when available), and optimization.&lt;/li&gt;
&lt;li&gt;Keep CDN and cache headers configured for long-lived caching where appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ecommerce image optimization
&lt;/h2&gt;

&lt;p&gt;Product pages are image-heavy; optimize with these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always set width and height (or use layout options) to avoid layout shifts.&lt;/li&gt;
&lt;li&gt;Mark above-the-fold images with priority.&lt;/li&gt;
&lt;li&gt;Use responsive sizes and srcset via next/image.&lt;/li&gt;
&lt;li&gt;Serve modern formats (WebP/AVIF) while falling back to JPEG/PNG for older browsers.&lt;/li&gt;
&lt;li&gt;Compress at source when possible (Cloudinary/Shopify transformations).&lt;/li&gt;
&lt;li&gt;Lazy-load non-critical images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps reduce page weight, speed render time, and help conversions.&lt;/p&gt;

&lt;h2&gt;
  
  
  i18n routing in the App Router
&lt;/h2&gt;

&lt;p&gt;Next.js supports locale-aware routing through next.config.js i18n settings. With the App Router:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define locales and defaultLocale.&lt;/li&gt;
&lt;li&gt;Next.js generates locale-prefixed routes (e.g., /en/, /hi/).&lt;/li&gt;
&lt;li&gt;Implement language switchers that update routes, not just UI strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SEO tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add hreflang tags to pages to signal language/region versions to search engines.&lt;/li&gt;
&lt;li&gt;Ensure your sitemap lists all localized URLs.&lt;/li&gt;
&lt;li&gt;Translate meta tags and structured data for each locale.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Securing environment variables and secrets
&lt;/h2&gt;

&lt;p&gt;Keep secrets out of your repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use .env.local for local development and never commit it.&lt;/li&gt;
&lt;li&gt;Only prefix variables with NEXT_PUBLIC_ when they must be exposed to the browser.&lt;/li&gt;
&lt;li&gt;Store API keys, DB credentials, and other secrets without NEXT_PUBLIC_ so they remain server-only.&lt;/li&gt;
&lt;li&gt;Use your host’s environment variable UI (Vercel, Netlify, etc.) for production values.&lt;/li&gt;
&lt;li&gt;Rotate keys periodically and limit scope/permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Authentication: cookies vs tokens
&lt;/h2&gt;

&lt;p&gt;Best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer HTTP-only cookies to store session tokens to mitigate XSS exposure.&lt;/li&gt;
&lt;li&gt;If using JWTs, store them in HTTP-only cookies rather than localStorage.&lt;/li&gt;
&lt;li&gt;Use secure, SameSite, and proper expiration settings on cookies.&lt;/li&gt;
&lt;li&gt;Consider NextAuth.js or a similar library for standard flows (OAuth, email, credentials).&lt;/li&gt;
&lt;li&gt;Use refresh tokens and short-lived access tokens for better control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Balance security and UX: choose approaches that fit your threat model and scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use Middleware
&lt;/h2&gt;

&lt;p&gt;Good use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight auth checks and redirects.&lt;/li&gt;
&lt;li&gt;Locale detection and routing.&lt;/li&gt;
&lt;li&gt;A/B tests or header-based experiments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What to avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heavy CPU tasks, long-running database calls, or complex business logic. Middleware runs at the edge and needs to be fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pattern: keep Middleware thin and push heavy operations to server functions or external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Actions vs Route Handlers
&lt;/h2&gt;

&lt;p&gt;Pick based on needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server Actions: convenient for simple forms and mutations tightly coupled to UI. They let you keep logic closer to components.&lt;/li&gt;
&lt;li&gt;Route Handlers (API routes): preferred for complex APIs, heavy integrations, or when a clear backend contract is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guideline: use Server Actions for simple CRUD tied directly to UI; use Route Handlers for third-party APIs, complex validation, or when multiple clients will consume the endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling file uploads
&lt;/h2&gt;

&lt;p&gt;Recommended approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct-to-storage: have the client upload directly to S3, R2, or Cloudinary using signed URLs or SDKs. This reduces server load and improves reliability.&lt;/li&gt;
&lt;li&gt;Server-mediated: accept files via a Route Handler if you need server-side validation or processing before storage.&lt;/li&gt;
&lt;li&gt;Always validate file type, size, and use virus-scanning where necessary.&lt;/li&gt;
&lt;li&gt;Provide progress feedback on the client for better UX.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building a headless Shopify store without SEO regressions
&lt;/h2&gt;

&lt;p&gt;Key practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use SSG or SSR for product and category pages so content is crawlable and fast.&lt;/li&gt;
&lt;li&gt;Emit correct canonical URLs and meta tags for all pages.&lt;/li&gt;
&lt;li&gt;Attach structured product data (JSON-LD) for rich search results.&lt;/li&gt;
&lt;li&gt;Optimize product images and feeds.&lt;/li&gt;
&lt;li&gt;Generate an XML sitemap covering all storefront URLs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining these keeps your headless store fast and search-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This FAQ covers practical, high-impact steps for advanced Next.js projects: performance optimizations, secure patterns, i18n and SEO, file uploads, and headless ecommerce. Apply these patterns incrementally — start with fonts and images, then secure secrets and authentication, and finally refine routing and uploads.&lt;/p&gt;

&lt;p&gt;Want help implementing any of these items or building a headless Shopify site with Next.js? Reach out and mention which area you want to prioritize.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;Home: https://prateeksha.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;Blog: https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Canonical: &lt;a href="https://prateeksha.com/blog/nextjs-faqs-beginner-to-advanced-part-5" rel="noopener noreferrer"&gt;Canonical: https://prateeksha.com/blog/nextjs-faqs-beginner-to-advanced-part-5&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>performance</category>
      <category>react</category>
      <category>security</category>
    </item>
    <item>
      <title>Turbopack + Next.js: Setup, Troubleshooting, and Practical Fixes</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 06:33:01 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/turbopack-nextjs-setup-troubleshooting-and-practical-fixes-3khl</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/turbopack-nextjs-setup-troubleshooting-and-practical-fixes-3khl</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta6mfd3ai9fawarwuypl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta6mfd3ai9fawarwuypl.jpg" alt="Turbopack + Next.js: Setup, Troubleshooting, and Practical Fixes" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setup and troubleshoot Turbopack for Next.js with step-by-step fixes for common errors, config pitfalls, and performance tips — focused on practical workflows and reproducible debugging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta6mfd3ai9fawarwuypl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta6mfd3ai9fawarwuypl.jpg" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick summary

&lt;ul&gt;
&lt;li&gt;How to enable Turbopack in Next.js and when it helps.&lt;/li&gt;
&lt;li&gt;Common failure modes and concrete fixes for module resolution, CSS/PostCSS, and HMR.&lt;/li&gt;
&lt;li&gt;Practical performance tweaks, checklists, and debugging commands for repeatable results.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Turbopack aims to make development builds much faster by using a Rust-based bundler and smarter incremental rebuilds. The speed gains are real, but Turbopack's resolver and plugin compatibility differ from webpack. That creates predictable failure modes in projects that were tuned for webpack. This guide focuses on practical, reproducible fixes so you can adopt Turbopack without guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick setup: enabling Turbopack in Next.js
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Confirm your Next.js version supports Turbopack in dev mode.&lt;/li&gt;
&lt;li&gt;Start the dev server with Turbopack enabled, for example:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next dev --turbo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or enable the appropriate flag in your toolchain if your Next.js release uses a different option.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Start with a minimal app (one route, no custom webpack) to verify basic routing, Fast Refresh, and CSS behavior before adding customizations.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Tip: Re-introduce plugins and aliases one at a time so you can quickly identify what breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use Turbopack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use Turbopack when dev iteration time is your primary bottleneck (large codebases, many components).&lt;/li&gt;
&lt;li&gt;Prefer it for client-heavy apps where webpack plugins aren’t essential to day-to-day iteration.&lt;/li&gt;
&lt;li&gt;Keep webpack in CI/production builds or when you depend on a mature plugin ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Turbopack vs Webpack — practical trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cold start: Turbopack is usually much faster.&lt;/li&gt;
&lt;li&gt;Incremental rebuilds: Turbopack is optimized for small changes.&lt;/li&gt;
&lt;li&gt;Plugin ecosystem: webpack has far more plugins and loaders.&lt;/li&gt;
&lt;li&gt;Source maps &amp;amp; debugger: Turbopack's mapping is improving but can behave differently.&lt;/li&gt;
&lt;li&gt;Config compatibility: Expect to change some project config to be Turbopack-compatible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common errors and real fixes
&lt;/h2&gt;

&lt;p&gt;This section covers three frequent failure modes and concrete steps to fix them.&lt;/p&gt;

&lt;h3&gt;
  
  
  App fails to boot with "module not found" for internal packages
&lt;/h3&gt;

&lt;p&gt;Symptoms: Local packages (monorepo workspaces or aliased imports) resolve under webpack but fail with Turbopack.&lt;/p&gt;

&lt;p&gt;Fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure each package.json in the monorepo has correct "exports", "main", and "module" fields and that source files are reachable.&lt;/li&gt;
&lt;li&gt;Prefer relative/absolute imports or runtime-compatible aliases. Webpack-only aliases don’t always translate to Turbopack.&lt;/li&gt;
&lt;li&gt;For workspace symlinks, make sure your package manager settings (nodeLinker, pnpm settings) and Next.js resolver can follow them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CSS or PostCSS plugins are ignored in dev
&lt;/h3&gt;

&lt;p&gt;Symptoms: Styles are different or PostCSS transforms aren’t applied when using Turbopack.&lt;/p&gt;

&lt;p&gt;Fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Next.js' built-in PostCSS support; avoid webpack loaders that depend on custom pipelines.&lt;/li&gt;
&lt;li&gt;Place postcss.config.js at the repository root where Next.js/Turbopack will find it.&lt;/li&gt;
&lt;li&gt;If a plugin is webpack-only, consider a hybrid workflow: Turbopack for day-to-day dev and webpack for builds that require that PostCSS plugin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  HMR not updating a component (Fast Refresh issues)
&lt;/h3&gt;

&lt;p&gt;Symptoms: Code changes save but browser doesn’t reflect updates without a full reload.&lt;/p&gt;

&lt;p&gt;Fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid module-level singletons or stateful side effects that break Fast Refresh semantics. Move state into React components or context providers that survive refresh.&lt;/li&gt;
&lt;li&gt;Verify Fast Refresh is enabled for your Next.js/React version. Some older releases require specific flags or updates.&lt;/li&gt;
&lt;li&gt;If you rely on library code that loses identity across HMR, consider hot-export-friendly patterns or small wrapper modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warning: Don’t assume dev behavior equals production behavior. Always verify production builds separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Config pitfalls and how to avoid them
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Heavy custom next.config.js/webpack hooks can break Turbopack. Minimize webpack-specific plugins.&lt;/li&gt;
&lt;li&gt;TypeScript path aliases need runtime-resolvable alternatives; keep tsconfig paths mirrored in runtime imports or use a resolver plugin that Turbopack supports.&lt;/li&gt;
&lt;li&gt;Experimental flags change between releases. Lock Next.js versions and test upgrades in a branch before adopting them widely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance tips (practical)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Turn off expensive dev tasks (linting, type-checking) while iterating; run them in CI or pre-commit hooks.&lt;/li&gt;
&lt;li&gt;Break up large pages with dynamic imports and code splitting.&lt;/li&gt;
&lt;li&gt;Prefer ESM-first dependencies for faster parsing.&lt;/li&gt;
&lt;li&gt;Keep route trees shallow while actively developing to reduce rebuild surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world scenarios
&lt;/h2&gt;

&lt;p&gt;Scenario 1 — Monorepo module resolution failure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: "module not found" for internal UI packages.&lt;/li&gt;
&lt;li&gt;Root cause: missing "exports"/module config and a stale webpack alias.&lt;/li&gt;
&lt;li&gt;Fix: publish correct fields in package.json and remove the alias; Turbopack resolved modules afterward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario 2 — CSS changes not reflected with HMR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Global CSS variables updated but not hot-applied.&lt;/li&gt;
&lt;li&gt;Root cause: a production-only PostCSS plugin not present in dev.&lt;/li&gt;
&lt;li&gt;Fix: move postcss.config.js to repo root and simplify loader assumptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario 3 — Intermittent source maps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: breakpoints don’t match source during debugging.&lt;/li&gt;
&lt;li&gt;Root cause: pre-transforms (TS transforms) applied before Turbopack’s pipeline.&lt;/li&gt;
&lt;li&gt;Fix: emit source maps in a Turbopack-friendly way in dev; restrict inline maps for CI/production builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;p&gt;Before enabling Turbopack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm Next.js supports Turbopack in your version.&lt;/li&gt;
&lt;li&gt;Test a baseline app with default config.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you hit an error:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reproduce with a minimal app.&lt;/li&gt;
&lt;li&gt;Check package.json "exports", "main", and "module".&lt;/li&gt;
&lt;li&gt;Simplify aliases and tsconfig mappings.&lt;/li&gt;
&lt;li&gt;Inspect dev server logs and run verbose mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Debugging commands and tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Start with verbose output: &lt;code&gt;next dev --turbo --verbose&lt;/code&gt; (or the equivalent for your Next.js release).&lt;/li&gt;
&lt;li&gt;Isolate config issues by temporarily removing custom next.config.js rules.&lt;/li&gt;
&lt;li&gt;Binary-search by reverting recent package upgrades to find regressions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Integration patterns: when to fall back
&lt;/h2&gt;

&lt;p&gt;If a critical workflow requires a webpack plugin without a Turbopack alternative, use a hybrid setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turbopack for daily development.&lt;/li&gt;
&lt;li&gt;webpack-based builds in CI or for feature branches that require the plugin.
Automate the switch via NPM scripts for developer convenience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  External resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs — JavaScript &amp;amp; devtools knowledge.&lt;/li&gt;
&lt;li&gt;Google Lighthouse — performance auditing.&lt;/li&gt;
&lt;li&gt;W3C WAI — accessibility guidance.&lt;/li&gt;
&lt;li&gt;Cloudflare Learning Center — networking fundamentals.&lt;/li&gt;
&lt;li&gt;OWASP — security best practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Latest trends
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More ESM-first packages and explicit "exports" fields in packages.&lt;/li&gt;
&lt;li&gt;Tooling shifting to faster incremental builds and better sourcemap fidelity.&lt;/li&gt;
&lt;li&gt;Hybrid dev workflows are common while plugin compatibility improves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;Home: https://prateeksha.com&lt;/a&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;Blog: https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog/turbopack-nextjs-faster-dev-builds-common-errors-fixes" rel="noopener noreferrer"&gt;Canonical: https://prateeksha.com/blog/turbopack-nextjs-faster-dev-builds-common-errors-fixes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Turbopack can noticeably speed dev iteration but requires verification and small config changes.&lt;/li&gt;
&lt;li&gt;Fixes usually involve making package exports explicit, avoiding webpack-only plugins, and simplifying aliasing.&lt;/li&gt;
&lt;li&gt;Keep a minimal reproducible repo for any issue — it speeds community and vendor help.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Turbopack offers meaningful developer experience improvements, but it changes the failure modes you’ll encounter. Use the checklists and scenarios above to reproduce issues quickly and apply targeted fixes. Try Turbopack on a small branch, keep webpack in CI, and keep a tiny repro to get help fast.&lt;/p&gt;

&lt;p&gt;Want help adopting this workflow or debugging a Turbopack problem? Contact us to set up a reproducible test or an audit of your Next.js config.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;https://prateeksha.com&lt;/a&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;https://prateeksha.com/blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;About Prateeksha Web Design&lt;/p&gt;

&lt;p&gt;Prateeksha Web Design focuses on performance-first Next.js engineering and tooling support. We help teams adopt Turbopack, resolve build issues, and build hybrid workflows that balance fast iteration with production reliability.&lt;/p&gt;

&lt;p&gt;Chat with us now &lt;a href="https://prateeksha.com/contact-us" rel="noopener noreferrer"&gt;Contact us today&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>performance</category>
      <category>nextjs</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Reliable Shopify Webhooks: Idempotency, Retries, and Signature Verification</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 06:31:09 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/reliable-shopify-webhooks-idempotency-retries-and-signature-verification-30b3</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/reliable-shopify-webhooks-idempotency-retries-and-signature-verification-30b3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs5czs8eoazbr6t656fv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs5czs8eoazbr6t656fv.jpg" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify Shopify webhook HMACs before doing any work.&lt;/li&gt;
&lt;li&gt;Acknowledge requests quickly and process asynchronously via queues.&lt;/li&gt;
&lt;li&gt;Use idempotency keys, retry/backoff strategies, and a dead-letter queue (DLQ) to avoid duplicates and data loss.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why webhook reliability matters
&lt;/h2&gt;

&lt;p&gt;Webhooks drive critical flows: inventory updates, order imports, payment confirmations, and fulfillment. If your receiver drops, delays, or double-processes events you can get missed shipments, duplicate charges, or inconsistent data across systems.&lt;/p&gt;

&lt;p&gt;Reliable handling is a combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticating the source (HMAC verification).&lt;/li&gt;
&lt;li&gt;Fast acknowledgement (reduce retries from Shopify).&lt;/li&gt;
&lt;li&gt;Safe asynchronous processing with idempotency and retries.&lt;/li&gt;
&lt;li&gt;Observability and operational controls for failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: Keep the HTTP response fast (ideally &amp;lt;500ms) and move heavy work to a worker queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signature verification (authenticate the source)
&lt;/h2&gt;

&lt;p&gt;Shopify signs webhooks using HMAC-SHA256. Always validate the signature before enqueuing or processing the payload. Use the raw request body and a timing-safe comparison to prevent replay and spoofing attacks.&lt;/p&gt;

&lt;p&gt;Example (Node.js / Express):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// verify-signature.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyShopifyWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shopifySecret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmacHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Shopify-Hmac-Sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shopifySecret&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hmacHeader&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifyShopifyWebhook&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute HMAC with your app secret against the raw body.&lt;/li&gt;
&lt;li&gt;Use timing-safe comparisons (e.g., crypto.timingSafeEqual) to avoid leaking verification timing.&lt;/li&gt;
&lt;li&gt;Reject or log invalid signatures; don’t enqueue them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic webhook receiver pattern
&lt;/h2&gt;

&lt;p&gt;A robust receiver follows a consistent pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture raw body and relevant headers.&lt;/li&gt;
&lt;li&gt;Verify HMAC signature.&lt;/li&gt;
&lt;li&gt;Enqueue the event and return 200 OK quickly.&lt;/li&gt;
&lt;li&gt;Process the job asynchronously with idempotency checks and controlled retries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example Express route using a Redis-backed queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifyShopifyWebhook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./verify-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bull&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webhookQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shopify-webhooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redis://127.0.0.1:6379&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhooks/shopify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOPIFY_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;verifyShopifyWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Enqueue quickly&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;webhookQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates the HTTP surface (which must be reliable and fast) from the processing surface (which can scale and retry independently).&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency keys and deduplication
&lt;/h2&gt;

&lt;p&gt;Shopify will retry deliveries on transient failures, and networks can duplicate requests. Deduplicate by computing a stable idempotency key for each webhook.&lt;/p&gt;

&lt;p&gt;Common key sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify event ID if present.&lt;/li&gt;
&lt;li&gt;Resource ID combined with a timestamp or version.&lt;/li&gt;
&lt;li&gt;A deterministic hash of relevant headers + body.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute an idempotency key like shopify:{shopId}:webhook:{event_id} or hash(payload).&lt;/li&gt;
&lt;li&gt;Attempt to claim that key in a fast store (Redis) before processing.&lt;/li&gt;
&lt;li&gt;If the claim succeeds, proceed and set a TTL (24–72 hours).&lt;/li&gt;
&lt;li&gt;If the key exists, skip processing and acknowledge success.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Node.js sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// idempotency.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;claimIdempotency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttlSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;claimed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttlSeconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;claimed&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true if key was set (not present before)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;markDone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;claimIdempotency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;markDone&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worker-side check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`shopify:webhook:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// choose stable identifier&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;claimed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;claimIdempotency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;claimed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// already processed&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do work&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;markDone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// let queue retry or move to DLQ&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents double-processing when retries arrive while the original job is pending or being retried.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retries, backoff, and dead-letter patterns
&lt;/h2&gt;

&lt;p&gt;Don’t rely solely on Shopify’s retry schedule for long-running business work. Use your queue’s retry/backoff and DLQ features.&lt;/p&gt;

&lt;p&gt;Choose a retry strategy based on failure type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immediate retries: for tiny transient errors.&lt;/li&gt;
&lt;li&gt;Exponential backoff: for rate-limited third-party APIs.&lt;/li&gt;
&lt;li&gt;Rate-limited workers: when you must guarantee throughput to downstream systems.&lt;/li&gt;
&lt;li&gt;Dead-letter queue: when repeated attempts fail and manual intervention is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure attempts and backoff per job type.&lt;/li&gt;
&lt;li&gt;After N attempts, move jobs to a DLQ with error metadata.&lt;/li&gt;
&lt;li&gt;Record last error, attempt count, and origin so operators can triage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warning: Never delete failing webhook events silently — push them to DLQ and alert operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability and monitoring
&lt;/h2&gt;

&lt;p&gt;Track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Webhook endpoint 4xx/5xx rates.&lt;/li&gt;
&lt;li&gt;Acknowledgement latency.&lt;/li&gt;
&lt;li&gt;Worker processing latency and success rate.&lt;/li&gt;
&lt;li&gt;DLQ size and age.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Log structured events (shop ID, webhook ID, attempts) and add traces for workflows spanning services. Alerts should trigger on spikes in 4xx/5xx, DLQ growth, or increased processing lag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world scenarios
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Payment duplicates: Teams used a request-hash idempotency key in Redis to avoid a double-capture after a transient 500. DLQ captured the failed item for replay.&lt;/li&gt;
&lt;li&gt;Order spike: During flash sales, enqueueing plus autoscaled workers and exponential backoff prevented downstream API bans.&lt;/li&gt;
&lt;li&gt;Key rotation mishap: A staging secret deployed to production caused verification failures. Alerts surfaced the issue quickly; rollback and a DLQ replay fixed missed orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Verify HMAC signatures with timing-safe comparisons.&lt;/li&gt;
&lt;li&gt;Respond 200 quickly and enqueue heavy work.&lt;/li&gt;
&lt;li&gt;Claim idempotency keys in a fast store before processing.&lt;/li&gt;
&lt;li&gt;Configure queue retries, backoff, and DLQ handling.&lt;/li&gt;
&lt;li&gt;Log structured events and instrument tracing.&lt;/li&gt;
&lt;li&gt;Add alerts for endpoint error rates and DLQ growth.&lt;/li&gt;
&lt;li&gt;Build a safe replay tool for DLQ items.&lt;/li&gt;
&lt;li&gt;Rotate secrets securely and support multi-key verification during transitions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Storage comparison for idempotency state
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Redis: Low latency, TTL support, ideal for short-lived claims.&lt;/li&gt;
&lt;li&gt;Postgres: Durable and auditable, better for long-term records and complex queries.&lt;/li&gt;
&lt;li&gt;S3/Blob: High latency, used for mapping large payloads or archival references.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Redis for quick dedup checks and a DB for audit trails if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and operational scripts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simulate retries, network errors, and signature failures in staging.&lt;/li&gt;
&lt;li&gt;Test multi-key verification for secret rotations.&lt;/li&gt;
&lt;li&gt;Provide a replay CLI/UI that re-validates signatures, rate-limits replays, and requires manual approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Short conclusion and CTA
&lt;/h2&gt;

&lt;p&gt;Reliable Shopify webhook handling combines source verification, fast acknowledgement, idempotency, controlled retries, and clear operational tooling. Apply these patterns to reduce duplicates, avoid data loss, and make webhook failures visible and actionable.&lt;/p&gt;

&lt;p&gt;Want help implementing a resilient webhook pipeline or reviewing your current setup? Contact us to audit or build a production-ready receiver.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;https://prateeksha.com&lt;/a&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;
Canonical: &lt;a href="https://prateeksha.com/blog/shopify-webhooks-idempotency-signature-verification" rel="noopener noreferrer"&gt;https://prateeksha.com/blog/shopify-webhooks-idempotency-signature-verification&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Practical Next.js Caching: Routes, Data, Revalidation, and Tags</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 06:26:33 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/practical-nextjs-caching-routes-data-revalidation-and-tags-24d1</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/practical-nextjs-caching-routes-data-revalidation-and-tags-24d1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj28bq95kns3wsbyem3ah.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj28bq95kns3wsbyem3ah.jpg" alt="Practical Next.js Caching: Routes, Data, Revalidation, and Tags" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A concise, practical guide to Next.js caching: how route and data caches differ, when to revalidate, and how tag-based invalidation reduces rebuild cost. Includes patterns, anti-patterns, and debugging tips.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj28bq95kns3wsbyem3ah.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj28bq95kns3wsbyem3ah.jpg" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand route (HTML) cache vs data (API) cache and when to prefer each.&lt;/li&gt;
&lt;li&gt;Learn time-based, event-driven, and tag-based revalidation patterns.&lt;/li&gt;
&lt;li&gt;See common anti-patterns and a checklist for production readiness.&lt;/li&gt;
&lt;li&gt;Find debugging tips and operational commands to verify cache behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why caching matters in Next.js
&lt;/h2&gt;

&lt;p&gt;Next.js apps mix server rendering, edge runtimes, and client fetching. Caching ties these pieces together by improving perceived speed, reducing origin load, and lowering costs. The right mix of route caching, data caching, and revalidation strategies helps you deliver fresh content efficiently.&lt;/p&gt;

&lt;p&gt;Fact: Server-side caching plus correct revalidation reduces TTFB and origin cost when implemented safely and intentionally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview: cache layers in Next.js
&lt;/h2&gt;

&lt;p&gt;Common cache layers you’ll encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route cache: HTML responses cached at the edge or CDN (SSG / ISR).&lt;/li&gt;
&lt;li&gt;Data cache: fetch() results or API responses cached by edge runtimes or CDNs.&lt;/li&gt;
&lt;li&gt;Browser cache: Cache-Control headers that influence client-side caching.&lt;/li&gt;
&lt;li&gt;Tag-based invalidation: group-based revalidation to refresh only affected pages or data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick conceptual mapping&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route cache = fast full-page delivery (SSG, ISR).&lt;/li&gt;
&lt;li&gt;Data cache = shared JSON or API responses used across routes/components.&lt;/li&gt;
&lt;li&gt;Tags = fine-grained invalidation for entities referenced by many pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Core concepts and examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Route cache (HTML)
&lt;/h3&gt;

&lt;p&gt;Route caching applies when pages are served as static HTML or regenerated via ISR. Use getStaticProps + revalidate for periodic updates, or call on-demand revalidation for targeted invalidation.&lt;/p&gt;

&lt;p&gt;Example pattern (getStaticProps + revalidate):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/products/[id].js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A revalidate: 60 value instructs Next.js to serve the cached HTML and refresh the page in the background every 60 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Data cache (fetch + cache mode)
&lt;/h3&gt;

&lt;p&gt;When multiple pages share the same API responses, cache results at the data layer. Next.js’s fetch supports cache modes that control how responses are stored and reused.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;force-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cache: 'no-store' for always-fresh responses (higher origin cost).&lt;/li&gt;
&lt;li&gt;cache: 'force-cache' or default for long-lived results.&lt;/li&gt;
&lt;li&gt;Tune TTLs to balance freshness and cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3) Revalidation strategies
&lt;/h3&gt;

&lt;p&gt;Common approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time-based (ISR revalidate): predictable periodic refresh.&lt;/li&gt;
&lt;li&gt;Event-driven (on-demand revalidation): trigger refresh when content changes.&lt;/li&gt;
&lt;li&gt;Tag-driven: attach keys (tags) to pages/data and revalidate only those that reference an entity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On-demand revalidation endpoint example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/revalidate.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MY_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;revalidated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use secure tokens for endpoints that trigger revalidation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Tag strategies (grouped invalidation)
&lt;/h3&gt;

&lt;p&gt;Tags help avoid brute-force site-wide rebuilds. Attach tags during render (e.g., product:123, author:alice). When an entity changes, revalidate the corresponding tag(s) to update all affected pages.&lt;/p&gt;

&lt;p&gt;Pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add tags describing data dependencies at render time.&lt;/li&gt;
&lt;li&gt;On data change, trigger revalidation for specific tags (and optionally related tags like category:shoes).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: Tags are ideal when many pages reference the same entity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route cache vs Data cache vs Tag invalidation
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it caches&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Control mechanisms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Route cache&lt;/td&gt;
&lt;td&gt;HTML (pages)&lt;/td&gt;
&lt;td&gt;Full-page speed, low compute&lt;/td&gt;
&lt;td&gt;getStaticProps revalidate, ISR, on-demand revalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data cache&lt;/td&gt;
&lt;td&gt;JSON/API responses&lt;/td&gt;
&lt;td&gt;Shared data across pages&lt;/td&gt;
&lt;td&gt;fetch cache modes, edge cache headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tag invalidation&lt;/td&gt;
&lt;td&gt;Groups of pages/data&lt;/td&gt;
&lt;td&gt;Selective invalidation after updates&lt;/td&gt;
&lt;td&gt;tags, tag-driven revalidate endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Practical patterns
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Read-mostly marketing pages&lt;/li&gt;
&lt;li&gt;SSG + revalidate (300s–3600s depending on volatility).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use tags for shared entities (authors, categories).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frequently-updated product catalog&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data cache with short TTLs (30–60s) for APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trigger on-demand revalidation for affected product pages via tags.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Personal dashboards&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid long server/CDN caching. Use fetch with cache: 'no-store' and client-side updates for near-real-time data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Anti-patterns to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Long TTLs without any invalidation path — leads to stale data for real updates.&lt;/li&gt;
&lt;li&gt;Global revalidation for small changes — wastes compute and slows deployments.&lt;/li&gt;
&lt;li&gt;Conflicting TTLs across systems without documentation — causes unpredictable freshness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warning: Avoid very high revalidate values for moderately changing content unless you have reliable invalidation triggers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reproduce locally with caching disabled to verify logic.&lt;/li&gt;
&lt;li&gt;Inspect response headers (Cache-Control, Age, X-Nextjs-*) using curl -I.&lt;/li&gt;
&lt;li&gt;Check CDN and origin logs for hit/miss patterns.&lt;/li&gt;
&lt;li&gt;If on-demand revalidation fails, verify secret tokens and exact route paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Practical command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;curl -I &lt;a href="https://your-site.com/page" rel="noopener noreferrer"&gt;https://your-site.com/page&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world scenarios
&lt;/h2&gt;

&lt;p&gt;Scenario 1: E-commerce product update&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Global revalidation after each inventory change caused rebuild spikes.&lt;/li&gt;
&lt;li&gt;Solution: Tag product pages by ID and revalidate only affected tags, reducing rebuilds and improving shopper performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario 2: Editorial publication&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Long ISR intervals left headlines stale.&lt;/li&gt;
&lt;li&gt;Solution: Shorten revalidate window for front page and call on-demand revalidation on publish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario 3: Internal dashboard&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Aggressive caching caused stale analytics.&lt;/li&gt;
&lt;li&gt;Solution: Use no-store for live widgets while keeping non-critical cards cached.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Latest trends
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Edge runtimes and granular edge cache controls are maturing.&lt;/li&gt;
&lt;li&gt;Declarative tags and partial revalidation are becoming more common in frameworks.&lt;/li&gt;
&lt;li&gt;Observability for cache hit/miss metrics is gaining adoption.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Decide SSG vs SSR vs client-only per page.&lt;/li&gt;
&lt;li&gt;[ ] Define revalidation policy for each content type (TTL, tags, triggers).&lt;/li&gt;
&lt;li&gt;[ ] Add tag-based invalidation for shared entities.&lt;/li&gt;
&lt;li&gt;[ ] Implement a secure on-demand revalidation endpoint.&lt;/li&gt;
&lt;li&gt;[ ] Instrument headers, CDN logs, and alerts for cache behavior.&lt;/li&gt;
&lt;li&gt;[ ] Document caching policies for the team.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key operational debugging commands
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;curl -I to inspect headers&lt;/li&gt;
&lt;li&gt;Review CDN logs for origin requests&lt;/li&gt;
&lt;li&gt;Check server logs for on-demand revalidation failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key takeaways&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use route cache (SSG/ISR) for full-page performance and data cache for shared API responses.&lt;/li&gt;
&lt;li&gt;Prefer tags for selective invalidation over global busting on large sites.&lt;/li&gt;
&lt;li&gt;Combine time-based and event-driven revalidation for predictable freshness.&lt;/li&gt;
&lt;li&gt;Avoid anti-patterns like long TTLs without invalidation or global revalidation for small edits.&lt;/li&gt;
&lt;li&gt;Instrument headers and logs to detect cache miss hotspots and unexpected behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A sustainable Next.js caching strategy balances speed, freshness, and cost. Mix route caching, data caching, and tag-based invalidation according to content volatility and scale. Monitor cache behavior, document decisions, and automate revalidation where possible.&lt;/p&gt;

&lt;p&gt;Interested in a caching review or audit? Reach out to discuss patterns and implementation details.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;https://prateeksha.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Canonical: &lt;a href="https://prateeksha.com/blog/nextjs-caching-explained-route-cache-data-cache-revalidation-tags" rel="noopener noreferrer"&gt;https://prateeksha.com/blog/nextjs-caching-explained-route-cache-data-cache-revalidation-tags&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>performance</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>Next.js 15 Upgrade Playbook: App Router, Caching Pitfalls, and Safe Migration Steps</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 06:25:04 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/nextjs-15-upgrade-playbook-app-router-caching-pitfalls-and-safe-migration-steps-1opa</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/nextjs-15-upgrade-playbook-app-router-caching-pitfalls-and-safe-migration-steps-1opa</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvzoelznn5tommu758j3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvzoelznn5tommu758j3.jpg" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What changed: App Router behavior, caching defaults, middleware/header propagation, and image handling.&lt;/li&gt;
&lt;li&gt;Risks: stale content, broken auth, and hydration errors from server/client boundary changes.&lt;/li&gt;
&lt;li&gt;Goals: audit, migrate incrementally, verify caching and headers, and canary deploy with observability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why upgrade now?
&lt;/h2&gt;

&lt;p&gt;Next.js 15 brings tightened App Router semantics, updated caching defaults, and runtime adjustments that can improve performance and security. Those same changes can also alter behavior in subtle ways — which is why the upgrade should be planned and tested, not rushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in Next.js 15 (at a glance)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;App Router boundaries are stricter: server vs client responsibilities are enforced earlier.&lt;/li&gt;
&lt;li&gt;Caching model refined: more aggressive edge caching in some configs and finer fetch-level controls.&lt;/li&gt;
&lt;li&gt;Middleware and headers: propagation and rewrite behavior changed in ways that can affect auth and proxies.&lt;/li&gt;
&lt;li&gt;Assets and images: loader integrations and cache headers got updates that affect delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Core technical differences (quick reference)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Server Components: calling browser-only APIs in server components now breaks sooner.&lt;/li&gt;
&lt;li&gt;getServerSideProps: migration toward App Router patterns continues; consider server components + fetch.&lt;/li&gt;
&lt;li&gt;Edge runtime: environment differences (no process/fs) require verification for edge-deployed code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pages Router vs App Router — quick comparison
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Data fetching

&lt;ul&gt;
&lt;li&gt;Pages: getStaticProps / getServerSideProps&lt;/li&gt;
&lt;li&gt;App: server components, fetch with cache control&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;File layout

&lt;ul&gt;
&lt;li&gt;Pages: pages/&lt;/li&gt;
&lt;li&gt;App: app/ with nested layouts and templates&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Streaming

&lt;ul&gt;
&lt;li&gt;Pages: limited&lt;/li&gt;
&lt;li&gt;App: built-in streaming and partial rendering&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Middleware

&lt;ul&gt;
&lt;li&gt;Pages: older APIs&lt;/li&gt;
&lt;li&gt;App: updated header and rewrite behavior&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Caching

&lt;ul&gt;
&lt;li&gt;Pages: page-level declarations&lt;/li&gt;
&lt;li&gt;App: fetch-level cache controls and edge-oriented defaults&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common breaking issues and symptoms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Missing or stale content: default cache modes or edge rewrites not forwarding headers.&lt;/li&gt;
&lt;li&gt;Hydration errors: browser APIs used in server components or mismatched component types.&lt;/li&gt;
&lt;li&gt;Auth failures: cookies or authorization headers dropped by middleware or rewrites.&lt;/li&gt;
&lt;li&gt;Over-caching: immutable or aggressive caching preventing content updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended upgrade flow (safe and staged)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Inventory and audit

&lt;ul&gt;
&lt;li&gt;List routes using getServerSideProps, getInitialProps, direct cookie reads, or dynamic headers.&lt;/li&gt;
&lt;li&gt;Identify custom middleware, rewrites, and any proxy logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Read migration docs and test locally

&lt;ul&gt;
&lt;li&gt;Follow official changelogs and migration notes for Next.js 15.&lt;/li&gt;
&lt;li&gt;Build a branch environment and fix compile-time errors first.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Convert incrementally

&lt;ul&gt;
&lt;li&gt;Migrate a few non-critical routes to app/ rather than converting the entire site.&lt;/li&gt;
&lt;li&gt;Replace server-side props patterns with server components + fetch where practical.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Verify caching and runtime behavior

&lt;ul&gt;
&lt;li&gt;Exercise fetch cache modes (no-store, revalidate, default) and inspect Cache-Control headers.&lt;/li&gt;
&lt;li&gt;Test middleware header propagation and rewrites in staging.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Canary deploy and monitor

&lt;ul&gt;
&lt;li&gt;Deploy to a subset of traffic, run E2E and smoke tests, and watch metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prepare rollback and runbooks

&lt;ul&gt;
&lt;li&gt;Keep a rollback path and dashboards for errors, SSR latencies, and cache hit/miss rates.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tip: Use feature flags or route-level toggles to limit the blast radius while migrating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and verification checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unit &amp;amp; integration tests focused on cookie/header flows and API interactions.&lt;/li&gt;
&lt;li&gt;End-to-end tests for login, personalization, and session refresh flows.&lt;/li&gt;
&lt;li&gt;Load tests for SSR and edge functions to measure CPU and latency.&lt;/li&gt;
&lt;li&gt;Cache verification: ensure Cache-Control values match expectations for static and dynamic routes.&lt;/li&gt;
&lt;li&gt;Observability: track error rates, SSR time, and cache metrics for the first 48 hours post-rollout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Helpful tools and references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Search Central for SEO and crawlability.&lt;/li&gt;
&lt;li&gt;Lighthouse for performance and accessibility checks.&lt;/li&gt;
&lt;li&gt;MDN for web API behavior.&lt;/li&gt;
&lt;li&gt;OWASP for header and cookie security guidance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world scenarios and fixes
&lt;/h2&gt;

&lt;p&gt;Scenario: stale landing page after enabling edge caching&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cause: immutable cache policy served promotional content for hours.&lt;/li&gt;
&lt;li&gt;Fix: add revalidate keys or switch to revalidate-based caching for marketing routes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario: login breaks after middleware migration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cause: rewrites stopped forwarding set-cookie/authorization headers.&lt;/li&gt;
&lt;li&gt;Fix: explicitly forward required headers in middleware and validate token refresh flow in staging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario: hydration mismatch from client-only code used in server component&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cause: library importing window.localStorage inside what became a server component.&lt;/li&gt;
&lt;li&gt;Fix: move the code to a client component and add unit tests to prevent regressions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting quick tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hydration errors: scan stack traces for "Hydration" and check for window/document usage.&lt;/li&gt;
&lt;li&gt;Missing cookies: log request headers at middleware entry to confirm forwarding.&lt;/li&gt;
&lt;li&gt;Stale responses: inspect Cache-Control and CDN/edge caching behavior with curl or a network tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: Add CI assertions that validate Cache-Control headers on representative endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Prateeksha Web Design approaches production upgrades
&lt;/h2&gt;

&lt;p&gt;Prateeksha uses a phased approach: audit, convert low-risk routes first, add CI checks for headers and cache, canary deploy, and monitor closely. Runbooks and rollback plans are prepared before any production rollout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key verification commands and snippets
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Local build and checks: npm run build &amp;amp;&amp;amp; npx next build&lt;/li&gt;
&lt;li&gt;Inspect headers: curl -I &lt;a href="https://staging.example.com/path" rel="noopener noreferrer"&gt;https://staging.example.com/path&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Simulate edge behavior: run a staging environment that mirrors CDN/edge settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warning: local dev server behavior can differ from edge runtimes and CDNs — always validate in staging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Audit routes and middleware before upgrading — App Router and caching defaults are stricter.&lt;/li&gt;
&lt;li&gt;Test header and cookie propagation; middleware rewrites can break auth flows.&lt;/li&gt;
&lt;li&gt;Migrate incrementally and canary deploy to reduce risk.&lt;/li&gt;
&lt;li&gt;Verify Cache-Control in staging and monitor cache hit/miss after rollout.&lt;/li&gt;
&lt;li&gt;Prepare a rollback plan and observe the first 48 hours closely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Upgrading to Next.js 15 offers performance and security benefits but introduces behavioral changes that can affect caching, headers, and component boundaries. Follow a staged plan: inventory, incremental migration, thorough testing, canary deployment, and observability. If you need hands-on help, document a rollback plan and monitor key metrics immediately after rollout.&lt;/p&gt;

&lt;p&gt;Ready to plan an upgrade or run an audit? Contact Prateeksha for migration guidance and runbook help.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;Home: https://prateeksha.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;Blog: https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog/nextjs-15-upgrade-guide-app-router-caching-migration" rel="noopener noreferrer"&gt;Canonical: https://prateeksha.com/blog/nextjs-15-upgrade-guide-app-router-caching-migration&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>performance</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>Shopify Scripts Functions: A Practical Migration Playbook</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 06:20:47 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/shopify-scripts-functions-a-practical-migration-playbook-2pih</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/shopify-scripts-functions-a-practical-migration-playbook-2pih</guid>
      <description>&lt;p&gt;A concise, developer-focused playbook to move Script Editor logic into Shopify Functions: audit, prototype, test, and stage rollouts with QA, monitoring, and rollback steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhc2vdca1mzzepgi5hfy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhc2vdca1mzzepgi5hfy.jpg" alt="Shopify Scripts → Functions: A Practical Migration Playbook" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit active scripts, map business intent, and prioritize by impact.&lt;/li&gt;
&lt;li&gt;Prototype a single high-value Function, validate parity, then iterate.&lt;/li&gt;
&lt;li&gt;Use staged rollouts, robust QA, and observability to reduce launch risk.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Shopify is standardizing customization around Shopify Functions. If your store still relies on the Script Editor, planning a migration reduces the chance of customer-facing surprises. This guide presents a practical, developer-oriented workflow: audit, map use-cases, prototype, test, and roll out safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why migrate now
&lt;/h2&gt;

&lt;p&gt;Shopify Functions run as sandboxed WebAssembly modules and plug into Shopify’s native evaluation points (discounts, shipping, payments). The migration brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better performance and scalability&lt;/li&gt;
&lt;li&gt;Clearer extension boundaries and versioning&lt;/li&gt;
&lt;li&gt;Reduced maintenance debt for long-term compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conceptual changes: Scripts vs Functions
&lt;/h2&gt;

&lt;p&gt;Key differences to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execution: Scripts ran in the Script Editor runtime; Functions are invoked by Shopify’s server-side evaluation and must be deterministic.&lt;/li&gt;
&lt;li&gt;Language &amp;amp; tooling: Scripts were Ruby-based; Functions use languages that compile to WASM (Rust is the most common path).&lt;/li&gt;
&lt;li&gt;Scope: Functions are tied to extension points (discounts, shipping, payments), not arbitrary checkout mutations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Design with determinism and idempotence in mind: Functions should produce the same output for the same cart input every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deployment: Script Editor (per-shop) vs app extension + Shopify CLI (versionable).&lt;/li&gt;
&lt;li&gt;Surface: General cart scripting vs specific extension points (discounts/shipping/payments).&lt;/li&gt;
&lt;li&gt;Maintainability: Monolithic shop scripts vs modular, testable functions.&lt;/li&gt;
&lt;li&gt;Performance: Legacy runtime vs sandboxed WASM with lower latency potential.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration overview — step-by-step
&lt;/h2&gt;

&lt;p&gt;Follow this task-oriented flow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Inventory &amp;amp; Audit&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export every active script and note where it runs and which data it uses (line items, customer tags, shipping address).&lt;/li&gt;
&lt;li&gt;Capture business intent: discounts, bundling, conditional shipping, or payment routing.&lt;/li&gt;
&lt;li&gt;Flag scripts that rely on private or deprecated APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prioritize &amp;amp; Scope&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tag scripts as High (revenue-critical), Medium (UX/ops), Low (infrequent).&lt;/li&gt;
&lt;li&gt;Choose one high-impact script for your pilot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Map to Functions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Match business intent to the right extension point (Discounts, Shipping Rates, Payment).&lt;/li&gt;
&lt;li&gt;Re-express logic as pure calculations that return updated pricing or rate objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prototype&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a minimal Function that covers core behavior.&lt;/li&gt;
&lt;li&gt;Run locally with Shopify CLI and a dev store to confirm baseline parity.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Implement &amp;amp; Integrate&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expand logic, introduce shared utilities, and add unit and integration tests.&lt;/li&gt;
&lt;li&gt;Add structured logging and feature flags to control rollout.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test &amp;amp; QA&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit test every rule branch and edge case.&lt;/li&gt;
&lt;li&gt;Integration tests with product fixtures and end-to-end checkout flows.&lt;/li&gt;
&lt;li&gt;Validate rounding, tax interactions, and unusual cart compositions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Staged Rollout&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Canary by percentage, storefront, or market.&lt;/li&gt;
&lt;li&gt;Monitor key metrics and hold a rollback window.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Post-launch&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate telemetry, monitor errors, and iterate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mapping common use-cases
&lt;/h2&gt;

&lt;p&gt;How common Script patterns translate to Functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SKU or collection discounts → Discount Function with deterministic matching rules.&lt;/li&gt;
&lt;li&gt;Buy X get Y / bundle promotions → Discount Function that groups line items and calculates line-level adjustments.&lt;/li&gt;
&lt;li&gt;Conditional shipping (by tag, weight, destination) → Shipping Function that returns authorized shipping rates.&lt;/li&gt;
&lt;li&gt;Payment routing or gating → Payments extension with server-side checks if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always replace side-effects with returned, repeatable data structures — Functions should not rely on mutable external state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world scenarios (short)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Holiday bundle: Prototype caught subtle rounding differences; staged rollout enabled quick fixes.&lt;/li&gt;
&lt;li&gt;Tag-based free shipping: Added tests for international destinations that previous Scripts overlooked.&lt;/li&gt;
&lt;li&gt;Legacy tiered pricing: Split logic between a Discount Function and app-side membership checks to simplify rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  QA and testing checklist
&lt;/h2&gt;

&lt;p&gt;Pre-migration&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List active scripts and business intents&lt;/li&gt;
&lt;li&gt;Capture analytics and revenue attribution per script&lt;/li&gt;
&lt;li&gt;Document any API limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Development&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Function prototype with unit tests&lt;/li&gt;
&lt;li&gt;Add structured logs and feature flags&lt;/li&gt;
&lt;li&gt;Build integration tests against a dev store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Staging &amp;amp; Rollout&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smoke test on limited storefront&lt;/li&gt;
&lt;li&gt;Monitor AOV, conversion rate, discount application rate&lt;/li&gt;
&lt;li&gt;Prepare clear rollback steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Post-launch&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor errors, latency, and revenue math&lt;/li&gt;
&lt;li&gt;Run periodic parity checks between legacy and Function outputs before retiring scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: Keep a canonical test suite that runs both the old Script outputs (when possible) and the new Function outputs against the same cart fixtures to spot drift early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollout strategy
&lt;/h2&gt;

&lt;p&gt;Options to reduce blast radius:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Canary by session percentage or feature flag&lt;/li&gt;
&lt;li&gt;Deploy to one storefront or market first&lt;/li&gt;
&lt;li&gt;Schedule deployments during lower-traffic windows&lt;/li&gt;
&lt;li&gt;Keep stakeholders (marketing, support, ops) informed and ready to respond&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring &amp;amp; observability
&lt;/h2&gt;

&lt;p&gt;Key signals to track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discount application rate vs historical baseline&lt;/li&gt;
&lt;li&gt;Checkout conversion and abandonment&lt;/li&gt;
&lt;li&gt;Average order value (AOV)&lt;/li&gt;
&lt;li&gt;Function latency and error rate&lt;/li&gt;
&lt;li&gt;Structured logs showing unmatched cases or unexpected inputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Integrate with your centralized logging and alerting stack and define thresholds for automated alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common pitfalls and how to avoid them
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expecting a straight 1:1 code translation — instead, map business intent first.&lt;/li&gt;
&lt;li&gt;Incomplete test coverage — build deterministic fixtures and automated checks.&lt;/li&gt;
&lt;li&gt;Preserving clever one-off rules — refactor reusable parts into shared helpers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Post-migration optimization
&lt;/h2&gt;

&lt;p&gt;After parity is confirmed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consolidate duplicated rules into shared libraries&lt;/li&gt;
&lt;li&gt;Tune telemetry to find performance bottlenecks&lt;/li&gt;
&lt;li&gt;Consider warm-up strategies if cold starts are visible in latency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Migrating from Shopify Scripts to Shopify Functions reduces technical risk and aligns stores with Shopify’s supported extensibility. Start with an audit, prototype a high-value use-case, validate with rigorous tests, and roll out in stages with clear monitoring and rollback plans. Want help planning or executing a migration? Reach out and we can map a pilot together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;Home: https://prateeksha.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;Blog: https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://prateeksha.com/blog/migrate-shopify-scripts-to-shopify-functions-playbook" rel="noopener noreferrer"&gt;Canonical: https://prateeksha.com/blog/migrate-shopify-scripts-to-shopify-functions-playbook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Checkout UI Extensions 101 — Build Post-Purchase &amp; Thank-You Page Customizations</title>
      <dc:creator>Sumeet Shroff Developer</dc:creator>
      <pubDate>Thu, 01 Jan 2026 05:23:34 +0000</pubDate>
      <link>https://forem.com/sumeet_shrofffreelancer_/checkout-ui-extensions-101-build-post-purchase-thank-you-page-customizations-np9</link>
      <guid>https://forem.com/sumeet_shrofffreelancer_/checkout-ui-extensions-101-build-post-purchase-thank-you-page-customizations-np9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjnena3m8bookg3owyu6r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjnena3m8bookg3owyu6r.jpg" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick summary&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Checkout UI Extensions are and where they run (checkout vs. post-purchase).&lt;/li&gt;
&lt;li&gt;How to structure an extension: manifest, UI, and backend interactions.&lt;/li&gt;
&lt;li&gt;Practical tips: limits, debugging steps, and a production checklist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Checkout UI Extensions let you inject small, purpose-built UIs into Shopify’s checkout and thank-you page surfaces. They’re intended for non-sensitive UI customizations — upsells, surveys, tracking snippets, or fulfillment instructions — while keeping payment rails untouched.&lt;/p&gt;

&lt;p&gt;This article outlines the architecture, constraints, debugging techniques, a short tutorial for a post-purchase upsell, and a checklist for production readiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Checkout UI Extensions?
&lt;/h2&gt;

&lt;p&gt;Checkout UI Extensions are sandboxed UI components that run inside Shopify’s checkout and post-purchase pages. They use a component runtime (React-like) and operate in a constrained environment to preserve performance and security.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sandbox execution with strict resource and time constraints.&lt;/li&gt;
&lt;li&gt;Predefined UI components and styling primitives.&lt;/li&gt;
&lt;li&gt;No access to raw payment details or card data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where they run
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Checkout (payment flow) — highly restricted surface; use only for light, non-blocking UI.&lt;/li&gt;
&lt;li&gt;Post-purchase / Thank-you page — more flexible; best for upsells, surveys, and order follow-ups.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important: do not attempt to perform sensitive payment logic inside the extension. Treat extensions as UI triggers that call secure backend services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture overview
&lt;/h2&gt;

&lt;p&gt;Typical components of a Checkout UI Extension solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extension package: UI code that Shopify injects at the extension point.&lt;/li&gt;
&lt;li&gt;Manifest: metadata describing extension points, permissions, and runtime info.&lt;/li&gt;
&lt;li&gt;Backend services (optional): APIs for personalization, order creation, or analytics.&lt;/li&gt;
&lt;li&gt;Shopify runtime: injects the extension into the page and isolates it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Merchant installs the extension through an app.&lt;/li&gt;
&lt;li&gt;Shopify validates and injects the extension at declared extension points.&lt;/li&gt;
&lt;li&gt;The extension runs in a sandboxed environment and renders UI.&lt;/li&gt;
&lt;li&gt;For side-effects (create draft orders, save data), the extension calls your backend, which in turn calls Shopify Admin APIs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Communication patterns
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use fetch from the extension to call your backend API, but keep calls short and non-blocking.&lt;/li&gt;
&lt;li&gt;Implement server-side persistence for critical operations (orders, subscriptions).&lt;/li&gt;
&lt;li&gt;Ensure CORS is configured correctly for allowed origins.&lt;/li&gt;
&lt;li&gt;Prefer idempotent backend endpoints so repeated calls don’t cause duplicate orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limits and considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Performance: extensions should render quickly; avoid heavy computation.&lt;/li&gt;
&lt;li&gt;Styling and DOM: you work with provided components; direct DOM access is limited.&lt;/li&gt;
&lt;li&gt;Security: no direct access to payment details or card numbers.&lt;/li&gt;
&lt;li&gt;Network timeouts: treat network calls as unreliable — use retries and fallback UI.&lt;/li&gt;
&lt;li&gt;Accessibility: extensions must be keyboard friendly and screen-reader accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison: when to use Extensions vs Apps vs Script Editor
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Checkout UI Extensions: best for lightweight UI inside checkout/thank-you pages.&lt;/li&gt;
&lt;li&gt;Embedded Shopify Apps: suitable for complex backend workflows, dashboards, and settings.&lt;/li&gt;
&lt;li&gt;Script Editor / Shopify Functions: server-side logic for pricing and promotions (not for UI).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tutorial: Build a Simple Post-Purchase Upsell Extension
&lt;/h2&gt;

&lt;p&gt;This is a concise walkthrough showing the core steps to scaffold and wire a post-purchase upsell.&lt;/p&gt;

&lt;p&gt;1) Scaffold the extension&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Shopify CLI to create a Checkout UI Extension scaffold and run the dev preview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Extension manifest (example snippet)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"post_purchase_upsell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"checkout_ui_extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extension_points"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PostPurchase"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) UI component (pseudo-code)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shopify/checkout-ui-extensions-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PostPurchaseApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Special offer: add a warranty&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;addUpsell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add warranty&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout::PostPurchase::Default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PostPurchaseApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Handling the upsell action&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let the extension trigger a backend call with the order ID and selected option.&lt;/li&gt;
&lt;li&gt;The backend creates a draft order or subscription via the Admin API, and returns a customer-facing link or sends a follow-up email.&lt;/li&gt;
&lt;li&gt;Keep the extension UI responsive: show optimistic states and surface errors clearly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tip: prepare your server-side flow first (draft order creation + checkout links). The extension should only trigger and report status to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use Shopify CLI extension dev tools to preview in a staging checkout.&lt;/li&gt;
&lt;li&gt;Inspect runtime logs via remote debugging in the injected iframe.&lt;/li&gt;
&lt;li&gt;Add structured logging in your backend and map errors to concise messages in the UI.&lt;/li&gt;
&lt;li&gt;Simulate slow networks to catch timeout issues and confirm graceful degradation.&lt;/li&gt;
&lt;li&gt;Validate CORS headers and content-security policies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick checklist when debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the extension injected at the expected extension point?&lt;/li&gt;
&lt;li&gt;Any console errors inside the injected frame?&lt;/li&gt;
&lt;li&gt;Are API responses blocked or delayed by CORS or network issues?&lt;/li&gt;
&lt;li&gt;Does the extension follow runtime method restrictions?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep UI minimal and non-blocking; prioritize performance.&lt;/li&gt;
&lt;li&gt;Move heavy lifting and sensitive operations to secure backend services.&lt;/li&gt;
&lt;li&gt;Build idempotent server endpoints to avoid duplicates.&lt;/li&gt;
&lt;li&gt;Verify accessibility: focus management, labels, and screen-reader support.&lt;/li&gt;
&lt;li&gt;Use feature flags and canary rollouts to limit exposure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful references: MDN for JS/CSS best practices, W3C WAI for accessibility, OWASP for security guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world scenarios
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Subscription upsell: show subscription options on thank-you page and create a draft subscription server-side.&lt;/li&gt;
&lt;li&gt;Warranty offer: accept fee in UI, call backend to add fulfillment notes and update order metadata.&lt;/li&gt;
&lt;li&gt;Fulfillment survey: capture delivery preferences and post them to merchant analytics.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test in a staging store using Shopify CLI preview.&lt;/li&gt;
&lt;li&gt;Confirm all API endpoints and CORS headers are correct.&lt;/li&gt;
&lt;li&gt;Run performance tests under slow network conditions.&lt;/li&gt;
&lt;li&gt;Conduct keyboard and screen-reader accessibility tests.&lt;/li&gt;
&lt;li&gt;Create rollback process and merchant support steps.&lt;/li&gt;
&lt;li&gt;Monitor extension render time, backend latency, and error rates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring &amp;amp; rollout
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use feature flags for gradual rollouts.&lt;/li&gt;
&lt;li&gt;Track key metrics: conversion lift, error rate, and average render time.&lt;/li&gt;
&lt;li&gt;Prepare merchant-facing toggle to disable the extension quickly if issues occur.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Checkout UI Extensions are intended for UI-focused, non-sensitive enhancements in checkout and post-purchase surfaces.&lt;/li&gt;
&lt;li&gt;Offload sensitive and heavy operations to backend services and ensure idempotency.&lt;/li&gt;
&lt;li&gt;Test performance, accessibility, CORS, and error handling before production.&lt;/li&gt;
&lt;li&gt;Use staged rollouts and monitoring to mitigate production risks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Checkout UI Extensions provide a practical way to extend the post-purchase experience without touching payment processing. Follow a server-first approach for side-effects, keep the UI fast and accessible, and use staging, monitoring, and gradual rollouts to ship safely. Ready to build an extension? Start with the Shopify CLI preview and the production checklist above.&lt;/p&gt;

&lt;p&gt;Home: &lt;a href="https://prateeksha.com" rel="noopener noreferrer"&gt;https://prateeksha.com&lt;/a&gt;&lt;br&gt;
Blog: &lt;a href="https://prateeksha.com/blog" rel="noopener noreferrer"&gt;https://prateeksha.com/blog&lt;/a&gt;&lt;br&gt;
Canonical: &lt;a href="https://prateeksha.com/blog/checkout-ui-extensions-101-post-purchase-thank-you-customizations" rel="noopener noreferrer"&gt;https://prateeksha.com/blog/checkout-ui-extensions-101-post-purchase-thank-you-customizations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want help building or auditing an extension, contact Prateeksha Web Design to discuss implementation and production readiness.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>ui</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
