<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Captain Code]]></title><description><![CDATA[Captain Code]]></description><link>https://capcodetech.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 22 Oct 2026 22:39:25 GMT</lastBuildDate><atom:link href="https://capcodetech.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Fixing Laravel Vite Manifest Not Found in Production]]></title><description><![CDATA[Where Laravel looks for public/build/manifest.json, and three real ways to build assets: on the VPS, in GitHub Actions, or locally for shared hosting.]]></description><link>https://capcodetech.hashnode.dev/laravel-vite-manifest-not-found-production</link><guid isPermaLink="true">https://capcodetech.hashnode.dev/laravel-vite-manifest-not-found-production</guid><category><![CDATA[GitHub Actions]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[vite]]></category><category><![CDATA[PHP]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Captain Code]]></dc:creator><pubDate>Thu, 22 Oct 2026 07:37:48 GMT</pubDate><content:encoded><![CDATA[<p>You push your code, run <code>git pull</code> and <code>composer install</code> on the server, and open the site. Instead of your homepage you get a 500, and the log (or Laravel's error page) says:</p>
<pre><code class="language-text">Vite manifest not found at: /var/www/your-app/public/build/manifest.json
</code></pre>
<p>Locally everything is fine, because there you either have <code>npm run dev</code> running or you ran <code>npm run build</code> at some point. On the server, the file Laravel is looking for simply isn't there. <strong>Laravel's "Vite manifest not found" error</strong> is not a framework bug. It means the frontend build output (the <code>public/build</code> folder) was never created, or was created somewhere Laravel isn't looking. This guide first shows exactly where the error comes from, then walks through three real ways to build your assets and get them onto the server: building on the VPS itself, building in GitHub Actions, and building on your own machine for shared hosting without Node.</p>
<h2>Where exactly does this error come from?</h2>
<p>In Blade you load your assets with the <code>@vite</code> directive:</p>
<pre><code class="language-blade">&lt;head&gt;
    @vite(['resources/css/app.css', 'resources/js/app.js'])
&lt;/head&gt;
</code></pre>
<p>According to the <a href="https://laravel.com/framework/docs/13.x/vite">official Laravel Vite documentation</a>:</p>
<blockquote>
<p>The <code>@vite</code> directive will automatically detect the Vite development server and inject the Vite client to enable Hot Module Replacement. In build mode, the directive will load your compiled and versioned assets, including any imported CSS.</p>
</blockquote>
<p>So <code>@vite</code> has two modes:</p>
<ol>
<li><strong>Dev mode:</strong> if a <code>public/hot</code> file exists (<code>npm run dev</code> creates it), Laravel doesn't touch the manifest at all. It points the tags at the dev server URL written inside that file (for example <code>http://localhost:5173</code>).</li>
<li><strong>Build mode:</strong> if there's no <code>public/hot</code>, Laravel reads <code>public/build/manifest.json</code> to find out which hashed file (for example <code>assets/app-xxxx.js</code>) <code>resources/js/app.js</code> was built into.</li>
</ol>
<p>The exception itself is thrown by the <code>Illuminate\Foundation\Vite</code> class (the class behind <code>@vite</code> and the <code>Vite</code> facade), in its <code>manifest()</code> method. A trimmed version of the framework code:</p>
<pre><code class="language-php">protected function manifest($buildDirectory)
{
    $path = $this-&gt;manifestPath($buildDirectory);

    if (! isset(static::$manifests[$path])) {
        if (! is_file($path)) {
            throw new ViteManifestNotFoundException("Vite manifest not found at: $path");
        }

        static::$manifests[$path] = json_decode(file_get_contents($path), true);
    }

    return static::$manifests[$path];
}
</code></pre>
<p>A few things follow from those lines:</p>
<ul>
<li>The path is built from <code>public_path()</code> + the build directory (default <code>build</code>) + the manifest filename (default <code>manifest.json</code>). So <strong>the path in the error message is exactly where Laravel looked</strong>. Your first job is to compare it with where your build actually lives.</li>
<li>Since Laravel 11, <code>ViteManifestNotFoundException</code> extends <code>ViteException</code> (and is marked deprecated in the source), so catching <code>ViteException</code> also catches this one.</li>
<li>If you changed the build directory (for example with <code>Vite::useBuildDirectory('bundle')</code> or the second argument of <code>@vite</code>), Laravel looks for <code>public/bundle/manifest.json</code>. <code>useBuildDirectory()</code> doesn't throw anything itself; it only changes the path. If <code>vite.config.js</code> (the Laravel plugin's <code>buildDirectory</code> option) doesn't use the same value, the manifest ends up somewhere else and you get this error.</li>
</ul>
<p>Two similar errors that often get mixed up with this one:</p>
<table>
<thead>
<tr>
<th>Message</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td><code>Vite manifest not found at: ...</code></td>
<td>No manifest file at that path (this article)</td>
</tr>
<tr>
<td><code>Unable to locate file in Vite manifest: resources/js/app.js.</code></td>
<td>The manifest exists but doesn't contain that entry: either the manifest is stale or the file isn't in the Laravel plugin's <code>input</code> in <code>vite.config.js</code></td>
</tr>
<tr>
<td><code>Unable to locate file from Vite manifest: ...</code></td>
<td>Only when <code>Vite::content()</code> (inlining an asset) is called and the built file isn't on disk</td>
</tr>
</tbody></table>
<h2>Why does it work locally but not on the server?</h2>
<p>The usual causes:</p>
<ol>
<li><strong><code>public/build</code> never reached the server.</strong> Laravel's default <code>.gitignore</code> contains both <code>/public/build</code> and <code>/public/hot</code>. If your deploy is just <code>git pull</code>, the build never travels with the code and has to be produced somewhere else.</li>
<li><strong>The build exists, but in the wrong place.</strong> Most common on shared hosting: you copied the contents of <code>public</code> into <code>public_html</code>, but Laravel's <code>public_path()</code> still points to <code>.../your-app/public</code>.</li>
<li><strong>The build ran on the server but failed.</strong> For example Node is too old, or dependencies were installed without devDependencies. If <code>vite build</code> exits with an error, no manifest is written.</li>
</ol>
<p>There's also one case that does <strong>not</strong> throw this error but is often confused with it: a leftover <code>public/hot</code> file on the server. Then Laravel skips the manifest and points the tags at the dev server (for example <code>localhost:5173</code>). You get a page with no CSS or JS, not a manifest error. It usually happens when someone ran <code>npm run dev</code> on the server or uploaded the <code>hot</code> file by hand.</p>
<h2>Diagnose first: where is the manifest?</h2>
<p>Before touching CI or config, run these from the project root on the server:</p>
<pre><code class="language-bash"># 1. Does the build folder exist?
ls -la public/build/

# 2. Is manifest.json there, and does it have content?
head -c 500 public/build/manifest.json

# 3. Is a hot file (dev server) left behind?
ls -la public/hot 2&gt;&amp;1

# 4. Which path exactly is Laravel looking at?
php artisan tinker --execute="echo public_path('build/manifest.json');"
</code></pre>
<p>How to read the results:</p>
<ul>
<li><strong>No <code>public/build</code>:</strong> the build never ran or never reached the server. Pick one of the three fixes below.</li>
<li><strong>The folder exists but the manifest is missing or empty:</strong> the build failed partway. Read the full <code>npm run build</code> output (see "Common errors").</li>
<li><strong>The path from command 4 differs from where you put the build:</strong> it's a public path problem (see the shared hosting section).</li>
<li><strong><code>public/hot</code> exists:</strong> delete it. It has no purpose in production:</li>
</ul>
<pre><code class="language-bash">rm -f public/hot
</code></pre>
<p>Another quick check: open <code>https://your-domain.com/build/manifest.json</code> in a browser. The file is public, so if it's in place you'll see JSON; a 404 means it isn't in the public folder your web server serves.</p>
<h2>Which fix fits your setup?</h2>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Criterion</th>
<th>Recommended fix</th>
</tr>
</thead>
<tbody><tr>
<td>Linux VPS with SSH access</td>
<td>You can install Node</td>
<td><strong>Build on the VPS</strong> (simplest)</td>
</tr>
<tr>
<td>Any platform, especially with a team</td>
<td>You want reproducible, automated builds</td>
<td><strong>Build in GitHub Actions</strong></td>
</tr>
<tr>
<td>Shared hosting (cPanel, DirectAdmin) without Node</td>
<td>Limited access, no SSH or Node</td>
<td><strong>Build locally + upload <code>public/build</code></strong></td>
</tr>
<tr>
<td>Laravel Forge or Ploi</td>
<td>Managed server</td>
<td>Make sure the panel's deploy script runs <code>npm ci</code> and <code>npm run build</code></td>
</tr>
<tr>
<td>Docker</td>
<td>App runs in a container</td>
<td>Build in a separate Dockerfile stage (see the FAQ)</td>
</tr>
</tbody></table>
<p>Rule of thumb: if Node is on the server, build there. If it isn't, or you want every deploy to be reproducible, use CI. If neither is available, build on your own machine.</p>
<h2>Fix 1: Build on the VPS</h2>
<h3>Install the right Node version</h3>
<p>First check whether Node is installed at all. Ubuntu and Debian don't ship it by default, and the version in the default repositories may be too old for Vite:</p>
<pre><code class="language-bash">node --version
npm --version
</code></pre>
<p>According to the <a href="https://vite.dev/guide/">Vite docs</a>, Vite requires Node.js 20.19+ or 22.12+ (Laravel 12's default <code>package.json</code> uses Vite 7, Laravel 13's uses Vite 8). Node 20 also reached end of life in April 2026 according to the <a href="https://nodejs.org/en/about/previous-releases">Node.js release table</a>, so use an active LTS such as 22 or 24 on the server. With <a href="https://github.com/nvm-sh/nvm">nvm</a>:</p>
<pre><code class="language-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash
source ~/.bashrc
nvm install 24
</code></pre>
<p>(Check nvm's README for the latest script version before running it.)</p>
<h3>Run the build</h3>
<pre><code class="language-bash"># On the server, inside the project root
cd /var/www/your-app

# Install exact dependencies from package-lock.json
npm ci --no-audit --no-fund

# Production build
npm run build
</code></pre>
<p>⚠️ Don't add <code>--omit=dev</code> or <code>NODE_ENV=production</code> here. In Laravel's default <code>package.json</code>, <code>vite</code>, <code>laravel-vite-plugin</code> and <code>tailwindcss</code> are all <code>devDependencies</code>; if they aren't installed, the build dies with <code>Cannot find module</code>.</p>
<p>A successful run looks something like this (version, hashes and sizes will differ in your project):</p>
<pre><code class="language-text">vite v7.x.x building for production...
✓ NN modules transformed.
public/build/manifest.json             x.xx kB │ gzip: x.xx kB
public/build/assets/app-[hash].css    xx.xx kB │ gzip: x.xx kB
public/build/assets/app-[hash].js     xx.xx kB │ gzip: xx.xx kB
✓ built in x.xxs
</code></pre>
<p>If there's no <code>public/build/manifest.json</code> line, or the build ended with an error, no manifest was written.</p>
<h3>What needs to be on the server?</h3>
<p>After the build, only these matter:</p>
<pre><code class="language-text">public/build/manifest.json
public/build/assets/   (hashed CSS and JS files)
</code></pre>
<p><code>node_modules</code> is only needed to produce the build; Laravel doesn't use it at runtime.</p>
<h3>Checklist before you call the deploy done</h3>
<pre><code class="language-bash"># ✓ Is the manifest there?
test -f public/build/manifest.json &amp;&amp; echo "OK" || echo "MISSING"

# ✓ Was at least one CSS or JS file built?
ls public/build/assets/ | grep -E '\.(css|js)$' &gt; /dev/null &amp;&amp; echo "OK" || echo "EMPTY"

# ✓ Is the hot file gone?
test ! -f public/hot &amp;&amp; echo "OK" || echo "HOT FILE EXISTS"

# ✓ Owner and permissions
ls -l public/build/manifest.json
</code></pre>
<p>The owner doesn't have to be <code>www-data</code>. What matters is that the PHP-FPM user and the web server can <strong>read</strong> the files (for example <code>644</code> for files and <code>755</code> for directories is enough).</p>
<h2>Fix 2: Build in GitHub Actions</h2>
<p>If you want every deploy built from the same clean environment, or you don't want Node on the server, let CI do the build. This section only covers building and shipping <code>public/build</code>; the deploy user, SSH keys and secrets are assumed to be set up already.</p>
<h3>The workflow</h3>
<pre><code class="language-yaml"># .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '24'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build assets
        run: npm run build
        # If your JS uses VITE_ variables, provide them here:
        # env:
        #   VITE_APP_NAME: ${{ vars.VITE_APP_NAME }}

      - name: Verify manifest exists
        run: |
          test -f public/build/manifest.json || (echo "Manifest missing!" &amp;&amp; exit 1)
          test -d public/build/assets || (echo "Assets dir missing!" &amp;&amp; exit 1)

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: vite-build
          path: public/build
          retention-days: 7

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: vite-build
          path: public/build

      - name: Upload build to server
        uses: appleboy/scp-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          source: "public/build"
          target: "/var/www/your-app/public/build-new"
          strip_components: 2
          rm: true

      - name: Swap build on server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            set -e
            cd /var/www/your-app
            test -f public/build-new/manifest.json
            rm -rf public/build-old
            if [ -d public/build ]; then mv public/build public/build-old; fi
            mv public/build-new public/build
            rm -f public/hot
</code></pre>
<p>A few notes on this workflow:</p>
<ul>
<li><strong>Verification runs before the upload.</strong> If no manifest was built, the first job fails and the deploy job never runs, so the site never goes live without a manifest.</li>
<li><strong>The build is uploaded to <code>build-new</code> first and then swapped in with <code>mv</code>.</strong> If you <code>rm -rf public/build</code> and then upload, the site runs without assets for a while. The previous build stays in <code>build-old</code>, so a cached page that still points to old hashes doesn't get 404s.</li>
<li><strong><code>VITE_</code> variables are baked into the JS at build time</strong>, not at runtime. If you use <code>import.meta.env.VITE_...</code>, they must have values in this step (<a href="https://vite.dev/guide/env-and-mode">Vite docs on env variables</a>).</li>
<li>If you want zero half-finished states, swap the whole release with a symlink (atomic deploy) instead of just the build folder.</li>
<li>If you run Laravel Octane, the manifest is cached in a static property and workers live between requests; reload them after deploy with <code>php artisan octane:reload</code>.</li>
</ul>
<h3>Tests in CI throw the same error</h3>
<p>If your CI runs PHPUnit or Pest before building, and a test renders a view that uses <code>@vite</code>, you'll get the same <code>Vite manifest not found</code>. According to the <a href="https://laravel.com/framework/docs/13.x/vite#disabling-vite-in-tests">Laravel docs</a>, either run <code>npm run build</code> before the tests or call <code>$this-&gt;withoutVite()</code> in your tests.</p>
<h3>Cost</h3>
<p>According to <a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions">GitHub's Actions billing docs</a>, standard GitHub-hosted runners are free for public repositories. Private repositories get a monthly quota depending on the plan (for example, GitHub Free includes 2,000 minutes and 500 MB of artifact storage per month). The <code>retention-days: 7</code> above keeps old artifacts from eating that storage.</p>
<h2>Fix 3: Build locally and upload <code>public/build</code> (shared hosting without Node)</h2>
<p>Many shared hosts give you neither Node nor full SSH. In that case you build on your own machine and upload only the output.</p>
<h3>Build locally</h3>
<pre><code class="language-bash"># On your machine, inside the project root
node --version    # an active LTS, e.g. 22 or 24

npm ci
npm run build

# Compress the output
cd public
zip -r build.zip build/
</code></pre>
<p>If you have a <code>public/hot</code> file locally (because <code>npm run dev</code> is running), make sure you don't upload it along with everything else.</p>
<h3>Upload</h3>
<p>Through the panel's File Manager or SFTP:</p>
<ol>
<li>Upload <code>build.zip</code> into <strong>the same public folder Laravel actually uses</strong>.</li>
<li>Extract it so you get <code>build/manifest.json</code> and <code>build/assets/</code>.</li>
<li>Delete the zip file.</li>
</ol>
<h3>The key point: the public path on shared hosting</h3>
<p>A common layout is Laravel's code in <code>/home/user/your-app</code> and the site's web root in <code>public_html</code>. If you copied the contents of <code>public</code> into <code>public_html</code>, Laravel still thinks <code>public_path()</code> is <code>/home/user/your-app/public</code> and looks for <code>/home/user/your-app/public/build/manifest.json</code>; the error message shows exactly that path.</p>
<p>You have two options:</p>
<ul>
<li>put the build where the error message says, or</li>
<li>tell Laravel where the real public folder is. On Laravel 11+, in <code>bootstrap/app.php</code>:</li>
</ul>
<pre><code class="language-php">$app = Application::configure(basePath: dirname(__DIR__))
    // -&gt;withRouting(...) and the rest of your current config
    -&gt;create();

// Adjust the path to match your host's layout
$app-&gt;usePublicPath(dirname(__DIR__, 2).'/public_html');

return $app;
</code></pre>
<h3>Checklist without SSH</h3>
<ul>
<li>In File Manager, check that <code>build/manifest.json</code> and the <code>build/assets/</code> folder sit next to the site's <code>index.php</code>.</li>
<li>Open <code>https://your-domain.com/build/manifest.json</code> in a browser; you should see JSON.</li>
<li>Make sure there's no file called <code>hot</code> next to <code>index.php</code>.</li>
</ul>
<p>You don't need to upload <code>node_modules</code>, <code>package.json</code> or <code>package-lock.json</code>; they play no role at runtime.</p>
<h2>Comparing the three methods</h2>
<table>
<thead>
<tr>
<th>Criterion</th>
<th>Build on VPS</th>
<th>GitHub Actions</th>
<th>Local build + upload</th>
</tr>
</thead>
<tbody><tr>
<td>Setup effort</td>
<td>Easy</td>
<td>Medium</td>
<td>Easy</td>
</tr>
<tr>
<td>Automation</td>
<td>With a deploy script</td>
<td>Full</td>
<td>Manual</td>
</tr>
<tr>
<td>Reproducibility</td>
<td>Medium (depends on the server's Node)</td>
<td>High (clean environment every run)</td>
<td>Low (depends on your machine)</td>
</tr>
<tr>
<td>Node needed on the server?</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td><code>node_modules</code> cache</td>
<td>No</td>
<td>Yes (<code>cache: 'npm'</code>)</td>
<td>No</td>
</tr>
<tr>
<td>Best for</td>
<td>Solo project on a VPS</td>
<td>Teams or automated deploys</td>
<td>Shared hosting</td>
</tr>
<tr>
<td>Extra cost</td>
<td>None</td>
<td>Free for public repos; private repos use the plan's quota</td>
<td>None</td>
</tr>
</tbody></table>
<h2>Common errors and fixes</h2>
<h3>Error 1: <code>EACCES: permission denied, mkdir '/var/www/.npm'</code></h3>
<p>This usually shows up when you run npm as the web server user (for example <code>sudo -u www-data npm ci</code>). That user's home is usually <code>/var/www</code>, which it can't write to, so npm can't create its <code>.npm</code> cache folder.</p>
<pre><code class="language-bash"># Better: run the build as the deploy user that owns the project
cd /var/www/your-app
npm ci
npm run build

# Or, if you must run it as another user, change the cache path for that run
npm ci --cache /tmp/npm-cache
</code></pre>
<h3>Error 2: <code>Cannot find module 'tailwindcss'</code> (or <code>vite</code>, <code>laravel-vite-plugin</code>) during the build</h3>
<p>First check whether dependencies were installed without devDependencies (<code>npm ci --omit=dev</code>, <code>npm install --production</code> or <code>NODE_ENV=production</code>). The build tools live in <code>devDependencies</code> and must be installed. If that's not it, do a clean install:</p>
<pre><code class="language-bash">rm -rf node_modules
npm ci
</code></pre>
<p>Don't delete <code>package-lock.json</code>. <code>npm ci</code> installs exactly from that file; deleting it means the server may end up with different package versions than your machine.</p>
<h3>Error 3: The build succeeds but the site is still broken</h3>
<ul>
<li><strong>Still getting <code>Vite manifest not found</code>:</strong> compare the path in the error with where the build is (see "Diagnose first"). It's almost always a mismatch in the public path or build directory.</li>
<li><strong>The page has no CSS or JS and requests go to <code>localhost:5173</code> or <code>[::1]:5173</code>:</strong> a <code>public/hot</code> file is left behind; delete it.</li>
<li><strong>You're on Octane:</strong> workers still hold the old manifest in memory; run <code>php artisan octane:reload</code>.</li>
<li><strong>JS and CSS files return 404:</strong> the page HTML comes from a cache (full-page cache or CDN) and points to hashes from a build you deleted. Purge the cache and keep the previous build around for a while (like <code>build-old</code> in the workflow above).</li>
</ul>
<h3>Error 4: Files load but the browser won't apply the CSS or run the JS</h3>
<p>Check the Console and Network tabs in DevTools:</p>
<ul>
<li><strong>Mixed Content error:</strong> the site is on HTTPS but the asset URLs are <code>http://</code>. Usually Laravel, sitting behind a proxy or CDN, doesn't know the request was HTTPS. That's the same issue covered in the trustProxies section of our <a href="https://captaincode.tech/en/blog/laravel-419-page-expired-livewire-production">guide to fixing Laravel 419 Page Expired</a>. If you serve assets from another domain, also check <code>ASSET_URL</code>.</li>
<li><strong>MIME type message (for example <code>text/plain</code>):</strong> in your Nginx config, the <code>http</code> block should have <code>include /etc/nginx/mime.types;</code>.</li>
<li><strong>Content Security Policy error:</strong> if you use a CSP, add your asset domain or a nonce to it. Laravel adds a nonce to the tags <code>@vite</code> generates when you call <code>Vite::useCspNonce()</code>.</li>
</ul>
<h2>FAQ</h2>
<p><strong>1. Why does <code>npm run build</code> work locally but not in CI or on the server?</strong></p>
<p>Three common reasons: (a) a different Node version; always set <code>node-version</code> explicitly in GitHub Actions. (b) The <code>VITE_</code> variables you have in your local <code>.env</code> aren't defined in CI. (c) devDependencies weren't installed (Error 2).</p>
<p><strong>2. Can I commit <code>public/build</code> to git?</strong></p>
<p>You can, but it's not recommended. Build files change with every change, clutter the repository history, and there's always a risk that the committed build doesn't match the code. Laravel also puts <code>/public/build</code> in <code>.gitignore</code> by default. If you're on shared hosting without CI, the manual upload method above is cleaner.</p>
<p><strong>3. How do I build in Docker?</strong></p>
<p>Use a multi-stage Dockerfile:</p>
<pre><code class="language-dockerfile"># Stage 1: build assets
FROM node:24-alpine AS assets
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: production
FROM php:8.4-fpm
WORKDIR /var/www/html
COPY . .
# Copy the build after the code so nothing overwrites it
COPY --from=assets /app/public/build ./public/build
</code></pre>
<p>The final image has no Node, and the build is produced from scratch every time. Also list <code>node_modules</code>, <code>public/build</code> and <code>public/hot</code> in <code>.dockerignore</code>. (PHP extensions and <code>composer install</code> are left out of this example.)</p>
<p><strong>4. Is Vite different from Laravel Mix?</strong></p>
<p>Yes. Since Laravel 9.19, new Laravel projects use Vite instead of Mix (which is built on webpack). Mix produced <code>mix-manifest.json</code> and you used <code>mix()</code> in Blade; with Vite you use <code>@vite()</code> and the manifest is written to <code>public/build/manifest.json</code>. If you migrated an old project and still have <code>mix()</code> calls somewhere, replace them too.</p>
<p><strong>5. How do I install Node on the server without nvm?</strong></p>
<p>With NodeSource packages:</p>
<pre><code class="language-bash">curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
</code></pre>
<p>Before piping any script from the internet into <code>bash</code>, take a look at what it does.</p>
<p><strong>6. Should I commit <code>package-lock.json</code>?</strong></p>
<p>Yes, always. <code>npm ci</code> won't run without <code>package-lock.json</code> (or when it's out of sync with <code>package.json</code>), and without a lock file every build may pull different package versions.</p>
<hr />
<p>If I had to sum up this article in one sentence: read the path in the error message, check whether <code>public/build/manifest.json</code> is really there, then pick one consistent way to build (on the VPS, in CI, or on your machine) and make every deploy verify the manifest is in place. Correct, lean assets aren't only about fixing errors; they also affect page speed and Core Web Vitals, which the Vite section of our <a href="https://captaincode.tech/en/blog/laravel-technical-seo-guide">Laravel technical SEO guide</a> covers.</p>
<p><strong>Deploy still failing?</strong> Paste the full error, your <code>npm run build</code> output and <code>vite.config.js</code> into <a href="https://captaincode.tech/en/labs/nyx">Nyx, Captain Code's AI assistant</a>, and work through where the build gets lost step by step. If you'd rather have the whole pipeline (build, deploy and server upkeep) set up properly once so you can stop thinking about it, that's what <a href="https://captaincode.tech/en/services/security-maintenance">website security and maintenance</a> and <a href="https://captaincode.tech/en/services/premium-web-development">custom Laravel development</a> are for; just <a href="https://captaincode.tech/en/contact">send us your project</a>.</p>
<p>If you're still learning backend and deployment and wondering where all of this fits, read <a href="https://captaincode.tech/en/blog/frontend-backend-fullstack-2026">Frontend, Backend, or Full-Stack?</a> and <a href="https://captaincode.tech/en/blog/best-programming-languages-2026">the best programming languages to learn in 2026</a>.</p>
<p>Questions, or stuck somewhere? Ask in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Frontend, Backend, or Full-Stack? Which Pays More in 2026?]]></title><description><![CDATA[Frontend, backend or full-stack in 2026? Salary data from Stack Overflow 2025 and Scalify 2026, time to first income, and when to specialize.]]></description><link>https://capcodetech.hashnode.dev/frontend-backend-fullstack-2026</link><guid isPermaLink="true">https://capcodetech.hashnode.dev/frontend-backend-fullstack-2026</guid><category><![CDATA[backend]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[full stack]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Captain Code]]></dc:creator><pubDate>Wed, 21 Oct 2026 07:33:47 GMT</pubDate><content:encoded><![CDATA[<p>If you want a quick answer: in 2026 <strong>frontend</strong> is the fastest path to your first dollar income (especially freelancing and remote work), <strong>backend</strong> has the highest salary ceiling, and <strong>full-stack</strong> offers the most project variety but the least specialist depth. If you are just starting and want to land a project within six months, frontend is the most logical choice; if you are aiming for a $200K-per-year ceiling, go for backend plus a high-level language like Python or Go.</p>
<h2>Why is this question more serious than ever in 2026?</h2>
<p>A few things in 2025–2026 changed the equation. First, the <strong>Stack Overflow Developer Survey 2025</strong> (published December 29, 2025) showed that 84% of developers now use AI tools, and 51% work with them daily. That means the role of all three branches is shifting toward "reviewer and architect" rather than "line-by-line coder." Second, skilled migration and the rise of remote work pushed Iranian salaries in 2025–2026 up sharply (numbers below). Third, AI coding tools (Cursor, Claude Code) made frontend work faster because visual output is quick to test, while backend work still needs systemic thinking.</p>
<p>What makes this article different: the salary tables cite the <strong>official Stack Overflow 2025 report</strong> and the <strong>Scalify.ai Full-Stack Salary Report 2026</strong>, plus real Iranian job-listing ranges from 2025–2026 — not recycled numbers from older posts. At the end you also get hands-on notes from 5+ years of Laravel freelancing and production projects, not textbook theory.</p>
<h2>The real difference between these three paths (not the textbook definition)</h2>
<p>Before the numbers, it helps to be clear what each path actually looks like day to day. A lot of career-path articles stay shallow or just repeat the same restaurant/shop analogy.</p>
<p><strong>Frontend</strong> is anything the user sees in the browser and interacts with — buttons, forms, animations, page layout. The main technologies in 2026 are <strong>HTML/CSS, JavaScript, TypeScript, React, Next.js, Vue.js or Svelte</strong>, and for styling Tailwind CSS or CSS Modules. A frontend developer works with UX designers and the backend team.</p>
<p><strong>Backend</strong> is the behind-the-scenes logic: database, API, authentication, security, payment processing. The main technologies are <strong>Node.js, Python (Django/FastAPI), PHP (Laravel), Java (Spring), C# (.NET), Go, Rust</strong>, and databases like PostgreSQL, MySQL, and MongoDB. A backend developer works with the database and system architecture.</p>
<p><strong>Full-stack</strong> means being competent enough in both. Not necessarily excellent in each, but enough to deliver a small-to-medium project end to end on your own.</p>
<p>The practical difference nobody talks about: frontend gives you <strong>instant feedback</strong> (you write code, and five seconds later you see it in the browser). Backend is <strong>delayed</strong> (you write code, then have to test the API, read logs, and check the database). That seemingly small difference is huge for learning speed and daily satisfaction.</p>
<h2>2026 salary comparison — real global data</h2>
<p>The data below comes from two sources:</p>
<ul>
<li><strong>Stack Overflow Developer Survey 2025</strong> (49,000+ developers, 177 countries, published December 29, 2025)</li>
<li><strong>Scalify.ai Full-Stack Salary Report 2026</strong> (April 2026, U.S. data)</li>
</ul>
<h3>Table 1: Median annual salary (USD) in 2026</h3>
<table>
<thead>
<tr>
<th>Experience Level</th>
<th>Frontend</th>
<th>Backend</th>
<th>Full-Stack</th>
</tr>
</thead>
<tbody><tr>
<td>Junior (0–2 years)</td>
<td>$75,000</td>
<td>$82,000</td>
<td>$72,000</td>
</tr>
<tr>
<td>Mid-Level (2–5 years)</td>
<td>$108,000</td>
<td>$112,000</td>
<td>$115,000</td>
</tr>
<tr>
<td>Senior (5–9 years)</td>
<td>$148,000</td>
<td>$150,000</td>
<td>$155,000</td>
</tr>
<tr>
<td>Staff/Principal (8+ years)</td>
<td>$180,000+</td>
<td>$190,000+</td>
<td>$198,000+</td>
</tr>
</tbody></table>
<p><strong>Important note:</strong> full-stack at the Mid and Senior levels pays about 5–10% more than the specialists in the Scalify breakdown. That premium mainly holds up to Senior. Above that, specialization (for example, backend for distributed systems) tends to earn more.</p>
<h3>Table 2: Technology stack distribution in full-stack (2026)</h3>
<table>
<thead>
<tr>
<th>Stack</th>
<th>Mid-Level Median</th>
<th>Senior Median</th>
<th>Market Status</th>
</tr>
</thead>
<tbody><tr>
<td>React + Next.js + TypeScript</td>
<td>$122,000</td>
<td>$162,000</td>
<td>Rapidly growing</td>
</tr>
<tr>
<td>React + Python (FastAPI)</td>
<td>$120,000</td>
<td>$162,000</td>
<td>AI premium (higher pay thanks to AI)</td>
</tr>
<tr>
<td>React + Node.js (TypeScript)</td>
<td>$118,000</td>
<td>$158,000</td>
<td>Dominant, stable</td>
</tr>
<tr>
<td>T3 Stack (TypeScript + tRPC)</td>
<td>$118,000</td>
<td>$158,000</td>
<td>Modern, growing</td>
</tr>
<tr>
<td>MERN (Mongo/Express/React/Node)</td>
<td>$112,000</td>
<td>$150,000</td>
<td>Stable</td>
</tr>
<tr>
<td>Vue + Node.js</td>
<td>$108,000</td>
<td>$145,000</td>
<td>Average, stable</td>
</tr>
<tr>
<td>Angular + .NET</td>
<td>$112,000</td>
<td>$150,000</td>
<td>Enterprise, stable</td>
</tr>
</tbody></table>
<p>Source: Scalify.ai 2026 Breakdown.</p>
<h3>Key findings from the Stack Overflow 2025 data</h3>
<ul>
<li><strong>84%</strong> of developers use AI tools (76% in 2024)</li>
<li><strong>51%</strong> of professionals use AI daily</li>
<li><strong>46%</strong> distrust AI output; only <strong>3%</strong> fully trust it</li>
<li><strong>Cursor</strong> (the AI-first editor) captured <strong>18%</strong> of the market in its first appearance</li>
<li><strong>Claude Sonnet</strong> is the most-used model among professionals at <strong>42.8%</strong> (30% among students)</li>
<li><strong>PostgreSQL</strong> overtook MySQL at <strong>55.6%</strong></li>
</ul>
<p>These numbers directly influence your path choice: whichever branch you pick, you need to be <strong>AI-fluent</strong>. 76% of developers do not use AI for deploy or production monitoring — which means if you know how, you have a real competitive edge.</p>
<h2>The Iranian market in 2025–2026 — what are the real numbers?</h2>
<p>The Iranian market ranges below are based on Jobinja, JobVision, and reports from Hooshka and Sadrhesab in 2025–2026:</p>
<h3>Table 3: Monthly developer salary in Iran (2025–2026, toman)</h3>
<table>
<thead>
<tr>
<th>Level</th>
<th>Junior</th>
<th>Mid-Level (1–3 years)</th>
<th>Senior</th>
</tr>
</thead>
<tbody><tr>
<td>Frontend (React/Next.js)</td>
<td>20–40 million</td>
<td>40–85 million</td>
<td>110+ million</td>
</tr>
<tr>
<td>Backend (Laravel/Node/Python)</td>
<td>25–45 million</td>
<td>50–90 million</td>
<td>120+ million</td>
</tr>
<tr>
<td>Full-Stack</td>
<td>25–45 million</td>
<td>55–100 million</td>
<td>150–300 million</td>
</tr>
</tbody></table>
<p><strong>Important note for the Iranian market:</strong> Iranian companies now compete with dollar salaries paid to freelancers. Many Iranian developers work remotely with teams in the UAE, Europe, and Canada and earn about $4,000–$6,000 a month. That means salaries above 150 million toman for Seniors are no longer unusual.</p>
<p>Personal experience in the Iranian market: backend Laravel (PHP) has steady but mid-ceiling demand thanks to traditional corporate sites, e-commerce, and booking systems. React and Next.js are growing faster thanks to startups and B2B products. Backend Python (Django/FastAPI) is seeing more demand because of AI, though its share is still smaller.</p>
<h2>How many months until your first real income?</h2>
<p>This is the question most career articles skip. The table below is an <strong>estimate</strong> from freelance reports and 2025–2026 market experience in Iran and abroad — not a survey median:</p>
<h3>Table 4: Estimated time to first income (serious learning, 3–4 hours/day)</h3>
<table>
<thead>
<tr>
<th>Goal</th>
<th>Frontend</th>
<th>Backend</th>
<th>Full-Stack</th>
</tr>
</thead>
<tbody><tr>
<td>First small freelance project ($50–200)</td>
<td>3–5 months</td>
<td>4–6 months</td>
<td>8–12 months</td>
</tr>
<tr>
<td>First Junior full-time job</td>
<td>5–8 months</td>
<td>6–9 months</td>
<td>9–14 months</td>
</tr>
<tr>
<td>Reaching extractable Mid-Level</td>
<td>12–18 months</td>
<td>15–24 months</td>
<td>20–30 months</td>
</tr>
<tr>
<td>First remote dollar salary</td>
<td>4–8 months</td>
<td>6–10 months</td>
<td>10–14 months</td>
</tr>
</tbody></table>
<p><strong>Why is frontend usually faster?</strong> Three reasons:</p>
<ol>
<li>You can build a <strong>visual portfolio</strong> more easily. Put a sample site on GitHub Pages and the client sees the result quickly.</li>
<li>Freelance demand is high: corporate sites, landing pages, and small e-commerce shops are mostly frontend-heavy.</li>
<li>AI tools like Cursor and Claude Code have made frontend scaffolding faster. Building a React component with AI can take minutes, but debugging a backend API still takes real time.</li>
</ol>
<p>But here is the warning: <strong>reaching income faster ≠ the best long-term path</strong>. If your goal is only income within the first six months, frontend wins. If you want salary growth five years from now, backend or full-stack is usually the smarter bet.</p>
<h2>The full-stack glass ceiling — why specialization sometimes wins</h2>
<p>A common belief says "full-stack is better because you know everything." After 5+ years of freelancing and working with different teams, that belief tends to hold <strong>up to Senior</strong>, and then stops.</p>
<p>Why? Three practical reasons:</p>
<p><strong>1. Depth vs breadth:</strong> full-stack is valuable at the Junior-to-Mid stage because you can deliver small-to-medium projects alone. Once you reach Senior, the market wants <strong>depth</strong>: "How well do you understand distributed systems?", "What is your experience with Event Sourcing?", "How do you scale a system from 10,000 users to 1 million?" Those questions are backend-specialist territory.</p>
<p><strong>2. Freelance hourly rate:</strong> in real projects, a senior backend specialist focused on <strong>Python + AI</strong> often charges about $80–$120 per hour, while a generalist full-stack developer is closer to $40–$60. That gap comes from <strong>deep specialization</strong> in a high-paying niche (personal project rates, not a published survey).</p>
<p><strong>3. Career ceiling:</strong> Table 1 shows full-stack reaching about $198K at the Staff/Principal level in the Scalify data. Backend specialists in niches like financial systems or AI/ML infrastructure often go higher still — those upper bands are market anecdotes, not a single survey cell.</p>
<p><strong>Exception:</strong> if you plan to launch <strong>your own startup</strong> or serve as a <strong>Technical Lead</strong> for a small team, full-stack is often the best choice. You need to understand everything and decide quickly.</p>
<h2>The dollar path — which branch is better for remote work?</h2>
<p>If your goal is international clients and dollar income, look at demand separately. Patterns from platforms like Upwork, Toptal, and A.Team in 2025–2026 (directional, not a single published wage table):</p>
<p><strong>Frontend for remote:</strong></p>
<ul>
<li>Strong demand for short-term projects (corporate sites, dashboards, landing pages)</li>
<li>Mid-Level hourly rate often roughly $35–$70</li>
<li>Large share of short freelance listings</li>
<li><strong>Advantage:</strong> a visual portfolio is easy to show</li>
</ul>
<p><strong>Backend for remote:</strong></p>
<ul>
<li>Longer projects with higher contract value</li>
<li>Mid-Level hourly rate often roughly $50–$90</li>
<li>Higher demand for <strong>Python + AI</strong> and <strong>Go + microservices</strong></li>
<li><strong>Advantage:</strong> more stable income, larger projects</li>
</ul>
<p><strong>Full-stack for remote:</strong></p>
<ul>
<li>Strong fit for <strong>early-stage startups</strong> that want one generalist</li>
<li>Hourly rate often roughly $60–$100 (depends on depth)</li>
<li><strong>Advantage:</strong> flexibility and diverse project types</li>
</ul>
<p><strong>Personal take:</strong> on remote platforms, <strong>React/Next.js + Node.js + a knack for AI</strong> is a strong balance of volume and rate. Going deep into <strong>AI/ML</strong> (Python + LangChain + vector databases) raises the ceiling further because that niche still has a talent shortage.</p>
<p>If you are still picking a first language for that path, see <a href="https://captaincode.tech/en/blog/best-programming-languages-2026">the best programming languages to learn in 2026</a>.</p>
<h2>Summary — when to pick frontend, backend, or full-stack</h2>
<p><strong>Pick frontend if:</strong></p>
<ul>
<li>You want to reach your first income fast (3–6 months)</li>
<li>You enjoy seeing the result of your work (instant feedback)</li>
<li>You care about design and user experience (UX)</li>
<li>You want to freelance or do remote work with short projects</li>
<li>You are still unsure which path you want (frontend is a good starting point)</li>
</ul>
<p><strong>Pick backend if:</strong></p>
<ul>
<li>You enjoy deep problem-solving and logic</li>
<li>You want the highest salary ceiling ($200K+)</li>
<li>You like working with data, system architecture, and performance</li>
<li>You want to get into AI/ML (backend Python is essential)</li>
<li>You are patient and not in a rush for your first income</li>
</ul>
<p><strong>Pick full-stack if:</strong></p>
<ul>
<li>You want to launch a startup or work as an all-rounder freelancer</li>
<li>You like delivering projects end to end</li>
<li>You enjoy diverse learning (with less depth in each area)</li>
<li>You want to become a Technical Lead or CTO</li>
<li>You prefer working independently rather than in a large team</li>
</ul>
<p><strong>One last piece of advice:</strong> if you are a complete beginner, begin with <strong>frontend (React + TypeScript)</strong>. Three months in, if you are drawn to backend and databases, continue toward full-stack. If you get tired of CSS and design, shift toward backend. Your first choice is never your final choice.</p>
<h2>Frequently asked questions (FAQ)</h2>
<p><strong>1. Does full-stack still have demand in 2026?</strong></p>
<p>Yes. Full-stack remains one of the most in-demand roles, especially in startups and small-to-medium companies. According to the Scalify.ai 2026 report, full-stack developers at the Mid to Senior level earn about 5–10% more than specialists. At higher levels (Staff+), deep specialization is usually worth more.</p>
<p><strong>2. Which path is best for career migration?</strong></p>
<p>It depends on the destination. For <strong>Canada and Australia</strong>, backend (especially Java/.NET) is often in high demand. For <strong>Germany</strong>, frontend and full-stack are more common. For <strong>international remote work</strong>, full-stack with React/Next.js + Node.js, or backend Python, is a strong option.</p>
<p><strong>3. Will AI replace programmers?</strong></p>
<p>According to the Stack Overflow Survey 2025, 84% of developers now use AI, but only 3% fully trust AI output. 76% do not use AI for deploy. AI replaces a lot of repetitive coding, not thinking and architecture. AI-fluent developers have the biggest advantage.</p>
<p><strong>4. Which language is best to start with in 2026?</strong></p>
<p>For frontend: <strong>TypeScript</strong> (not only vanilla JavaScript). For backend: <strong>Python</strong> (if you also want AI) or <strong>Node.js</strong> (if you are coming from frontend). For full-stack: <strong>React + Next.js + Node.js + TypeScript</strong> has the highest market demand among the stacks in Table 2.</p>
<p><strong>5. Do I need a computer-science degree?</strong></p>
<p>No. Most 2026 job listings ask for practical skill and a portfolio, not a university degree. According to the Stack Overflow Survey 2025, about 35% of professional developers do not have a formal computer-science degree. For immigration paperwork and some Senior roles, a degree can still help.</p>
<p><strong>6. How long until I reach Mid-Level?</strong></p>
<p>With serious learning (3–4 hours/day): roughly frontend 8–12 months, backend 12–18 months, full-stack 15–24 months if you start from zero. With prior programming experience, expect to move 30–50% faster. These are experience-based ranges, not survey medians.</p>
<p><strong>Sources:</strong></p>
<ul>
<li>Stack Overflow Developer Survey 2025 (49,000+ responses, 177 countries, published December 29, 2025)</li>
<li>Scalify.ai Full-Stack Developer Salary 2026 Breakdown (April 2026)</li>
<li>Iran developer salary ranges 2025–2026 (Jobinja, JobVision, Hooshka, Sadrhesab)</li>
<li>Author experience from 5+ years of Laravel freelancing and production projects</li>
</ul>
<p>Still deciding between frontend, backend, and full-stack — or building a product that needs all three? Try <a href="https://captaincode.tech/en/labs/nyx">Nyx, Captain Code's AI coding assistant</a> when you want a second pair of eyes on architecture or code. If you would rather hand the build off, from <a href="https://captaincode.tech/en/services/premium-web-development">custom web development</a> to <a href="https://captaincode.tech/en/services/security-maintenance">security and maintenance</a>, you can <a href="https://captaincode.tech/en/contact">talk to Captain Code</a>.</p>
<p>More on languages and market demand: <a href="https://captaincode.tech/en/blog/best-programming-languages-2026">Best programming languages to learn in 2026</a>.</p>
<p><em>Originally published on <a href="https://captaincode.tech/en/blog/frontend-backend-fullstack-2026">Captain Code</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Best Programming Languages to Learn in 2026]]></title><description><![CDATA[Python, JavaScript or Rust in 2026? Compare by goal, learning curve and job market, with sourced data and a practical beginner roadmap.]]></description><link>https://capcodetech.hashnode.dev/best-programming-languages-2026</link><guid isPermaLink="true">https://capcodetech.hashnode.dev/best-programming-languages-2026</guid><category><![CDATA[programming]]></category><category><![CDATA[Python]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Captain Code]]></dc:creator><pubDate>Tue, 20 Oct 2026 07:43:20 GMT</pubDate><content:encoded><![CDATA[<p>If you want a one-line answer: in 2026 three languages dominate demand and growth — <strong>Python</strong>, <strong>JavaScript</strong>, and <strong>Rust</strong>. <strong>Python</strong> leads thanks to AI and data, <strong>JavaScript</strong> because of the web and full-stack, and <strong>Rust</strong> because of memory safety and its official entry into the TIOBE Top 10 in July 2026. The choice between the three depends on your goal: AI → Python, web → JavaScript, high-performance systems → Rust.</p>
<h2>Why is this question suddenly important now?</h2>
<p>If you are looking to learn programming in 2026, you are facing a real fork in the road. On one side, the Stack Overflow 2024 and GitHub Octoverse 2024 reports show that Python and JavaScript are still the most widely used languages. On the other side, the TIOBE Index for July 2026 officially announced that <strong>Rust</strong> has joined the world's top ten languages. The map is shifting.</p>
<p>What makes this article different from the rest comes down to two things. First, every piece of data is sourced from authoritative 2024–2026 reports, not copied from older articles. Second, you will get practical experience working with these languages, not just definitions and praise. If you are just starting out, there is also a practical roadmap at the end of the article.</p>
<h2>Real Criteria for Choosing a Language, Not Just "the Most Popular"</h2>
<p>No language is absolutely "the best." Choosing a language depends on four factors you should know before you start:</p>
<ol>
<li><strong>Career goal</strong> — Do you want to do backend web work, build mobile apps, write operating systems, or move into AI? Each domain has its own language.</li>
<li><strong>Learning speed</strong> — Python and JavaScript are picked up faster because of their simple syntax. Rust and C++ have steeper learning curves but higher salaries in return.</li>
<li><strong>The Iranian job market</strong> — Job listings on Jobinja and JobVision in 2025–2026 show that JavaScript, Python, and PHP have the highest demand, but for international remote work Python and Rust pay more in USD.</li>
<li><strong>Ecosystem and future</strong> — The language you learn needs to still be in demand five years from now. Python and JavaScript have that guarantee; Rust is in the middle of proving itself.</li>
</ol>
<h2>Python: The Undisputed King of AI and Data</h2>
<p><strong>Python</strong> in 2026 remains the first choice for artificial intelligence, machine learning, data science, and automation. According to GitHub Octoverse 2024, Python overtook JavaScript and became the most popular language on GitHub — a shock to the developer community, because JavaScript had held the top spot for years.</p>
<p>Why has Python grown so much? The main reason is the explosion of AI tools. Libraries like <strong>TensorFlow</strong>, <strong>PyTorch</strong>, <strong>Hugging Face Transformers</strong>, and <strong>LangChain</strong> were either first written for Python or offer their best support there. Any company that wants to build an AI product today looks for Python specialists first.</p>
<p>Personal experience: on one of my freelance projects I had to build an automated resume-analysis system with AI. Using Django and the spaCy library I delivered the first version in three weeks. That project showed just how unbeatable Python's ecosystem is for startups and MVPs. Development speed is high, debugging is simple, and the support community is huge.</p>
<p><strong>Current version:</strong> Python 3.13 (October 2024), which brings significant runtime-performance improvements.</p>
<h2>JavaScript and TypeScript: The Backbone of the Modern Web</h2>
<p><strong>JavaScript</strong> is the only language that runs in every browser, on the server (via Node.js), inside mobile apps (via React Native), and even on the desktop (via Electron). According to the Stack Overflow Survey 2024, JavaScript is still the most popular language among professional developers. That said, IEEE Spectrum's September 2025 ranking showed JavaScript slipping from third to sixth — but that is not because of declining popularity; it is because the rise of AI Coding has reduced the volume of hand-written code.</p>
<p><strong>TypeScript</strong> has become the default choice for new projects in 2026. If you already know JavaScript, picking up TypeScript only takes two weeks and pays for itself many times over. Almost every backend job listing in Iran in 2025–2026 asks for "JavaScript/TypeScript plus a framework."</p>
<p>The JavaScript framework ecosystem in 2026 includes React 19, Next.js 15, Vue 3.5, Svelte 5, and Angular 19. On the backend side, Nest.js and FastAPI (which is actually Python but is often called from JavaScript) have also become popular.</p>
<p><strong>A note about Iran:</strong> if you want to enter the job market quickly, the <strong>Laravel + Vue.js + MySQL</strong> stack is still the most in-demand combo. But if your goal is international remote work, React + TypeScript + Node.js is the better answer.</p>
<h2>Rust: The Serious Newcomer to the TIOBE Top 10 — Why 2026 Is Rust's Year</h2>
<p><strong>Rust</strong> officially entered the TIOBE top ten in July 2026. That is a historic milestone, because TIOBE has been published since 2001 and until now only C, C++, Java, C#, Python, and JavaScript had held that position. Rust is the first new systems language to reach this rank in twenty years.</p>
<p>Why has Rust become so important? Three reasons:</p>
<ul>
<li><strong>Memory safety without runtime overhead</strong> — unlike C and C++, Rust prevents common memory errors like use-after-free at compile time.</li>
<li><strong>Speed close to C</strong> — perfect for systems where every millisecond matters (financial services, game engines, blockchain).</li>
<li><strong>Big-company backing</strong> — Microsoft, Google, Amazon, and Meta are all migrating important projects to Rust. The Linux kernel has also been adding Rust code since 2022.</li>
</ul>
<p><strong>Hands-on experience:</strong> the first time I wrote Rust code I ran into the "cannot borrow as mutable because it is also borrowed as immutable" error. For most beginners that is a nightmare. Rust's ownership and borrowing system is hard, but once you learn it you cannot go back to C++.</p>
<p>My advice: if you already work with JavaScript or Python, do not jump straight into Rust. Start with a simpler language first, and once your fundamentals are solid, learn Rust as a second language. But if you are a computer-science student or a systems developer, Rust is the best long-term investment right now.</p>
<h2>Final Comparison Table: Top 8 Languages in 2026</h2>
<p>This table is built from TIOBE, the Stack Overflow Survey 2024, GitHub Octoverse 2024, and Iranian job listings (2025–2026):</p>
<table>
<thead>
<tr>
<th>Language</th>
<th>Learning Difficulty</th>
<th>Monthly Salary (Iran, million toman)</th>
<th>Iranian Job Market</th>
<th>Performance (relative)</th>
<th>Main Use Case</th>
<th>Best Framework</th>
<th>Current Version</th>
<th>2026 Outlook</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Python</strong></td>
<td>Easy (2–3 months)</td>
<td>40–90</td>
<td>Excellent</td>
<td>Medium</td>
<td>AI, data, web</td>
<td>Django, FastAPI</td>
<td>3.13</td>
<td>Continued growth</td>
</tr>
<tr>
<td><strong>JavaScript</strong></td>
<td>Easy–Medium (3–4 months)</td>
<td>35–80</td>
<td>Excellent</td>
<td>Medium</td>
<td>Web, full-stack</td>
<td>React, Next.js</td>
<td>ES2024</td>
<td>Stable</td>
</tr>
<tr>
<td><strong>TypeScript</strong></td>
<td>Medium (2 weeks after JS)</td>
<td>50–100</td>
<td>Good</td>
<td>Medium</td>
<td>Pro web, backend</td>
<td>Nest.js, tRPC</td>
<td>5.7</td>
<td>Rising</td>
</tr>
<tr>
<td><strong>Rust</strong></td>
<td>Hard (6–12 months)</td>
<td>70–150 (international remote)</td>
<td>Limited but growing</td>
<td>Very high</td>
<td>Systems, blockchain, CLI</td>
<td>Actix, Tokio</td>
<td>1.83</td>
<td>Sharply rising</td>
</tr>
<tr>
<td><strong>Go</strong></td>
<td>Easy (2 months)</td>
<td>50–90</td>
<td>Good</td>
<td>High</td>
<td>Backend, DevOps</td>
<td>Gin, Echo</td>
<td>1.23</td>
<td>Stable</td>
</tr>
<tr>
<td><strong>Java</strong></td>
<td>Medium (4–6 months)</td>
<td>40–80</td>
<td>Excellent (enterprise)</td>
<td>High</td>
<td>Enterprise apps, Android</td>
<td>Spring Boot</td>
<td>21</td>
<td>Gradual decline</td>
</tr>
<tr>
<td><strong>C++</strong></td>
<td>Hard (8–12 months)</td>
<td>50–100</td>
<td>Medium</td>
<td>Very high</td>
<td>Games, systems, HPC</td>
<td>Qt, Unreal</td>
<td>C++23</td>
<td>Stable</td>
</tr>
<tr>
<td><strong>PHP</strong></td>
<td>Easy (2 months)</td>
<td>30–60</td>
<td>Good</td>
<td>Low</td>
<td>Web, CMS (WordPress)</td>
<td>Laravel</td>
<td>8.3</td>
<td>Declining</td>
</tr>
</tbody></table>
<p>If you are still unsure which one to pick, look closely at the table above. The <strong>Iranian salary and job-market</strong> numbers are estimated from real Jobinja and JobVision listings in summer 2025–2026, not made up.</p>
<h2>Other Languages Worth Learning</h2>
<p>Beyond the three headliners, a few more languages are worth learning depending on your specific need:</p>
<ul>
<li><strong>C++</strong> — if you want to do game development with Unreal Engine or professional systems programming, it is still unrivaled.</li>
<li><strong>Go (Golang)</strong> — Google's language for scalable backend services. In Iran there is real demand, especially in fintech practical setups.</li>
<li><strong>Java</strong> — if your goal is working in Iranian banks, insurance companies, or telecom operators, Java is still the enterprise default.</li>
<li><strong>Kotlin</strong> — if you only want to build Android apps, Kotlin has become the modern replacement for Java.</li>
<li><strong>Swift</strong> — for iOS and macOS development. In Iran, however, Apple's sanctions make the job market limited.</li>
<li><strong>C#</strong> — especially with Unity in game development and .NET 8 in enterprise apps. Iran also has a healthy market for it.</li>
</ul>
<h2>Will AI Coding Replace Language Choice?</h2>
<p>A question I hear a lot lately: "With GitHub Copilot and ChatGPT around, does learning a language even matter anymore?" Short answer: <strong>yes, but the kind of learning changes.</strong></p>
<p>AI Coding tools (especially Vibe Coding, which combines natural-language code generation with output review) have automated repetitive work like writing boilerplate, repeating patterns, and even simple debugging. IEEE Spectrum's September 2025 report touched on exactly this: "JavaScript fell from third to sixth in the Spectrum ranking, perhaps because AI is generating more of that code."</p>
<p>But there are three things AI cannot replace yet:</p>
<ol>
<li><strong>Deep system understanding</strong> — when a project gets complex and you have to make architectural decisions, AI helps but the decision is yours.</li>
<li><strong>Debugging real production issues</strong> — AI is good at common errors but usually misleading on complex production bugs.</li>
<li><strong>API design and user experience</strong> — these need a human understanding.</li>
</ol>
<p>So learning a language is still essential, but how you learn it has to change. Instead of memorizing syntax, focus on <strong>understanding concepts</strong> (algorithms, system design, architecture) and <strong>problem-solving ability</strong>.</p>
<h2>Practical Roadmap: Where Are You Right Now, and Which Language Fits?</h2>
<p>This roadmap is built from real questions I asked users on freelance projects:</p>
<ul>
<li><strong>If you are an absolute beginner and want to enter the job market quickly:</strong> start with Python. In three months you can learn Django or FastAPI and start freelancing. Add JavaScript/TypeScript after six months.</li>
<li><strong>If you want to build websites and web apps:</strong> JavaScript + TypeScript + React. The React + Next.js + TypeScript combo is currently the most in-demand freelance stack in Iran.</li>
<li><strong>If you are a computer-science student or love systems programming:</strong> learn C++ properly, then Rust. That combo has an outstanding international job market.</li>
<li><strong>If you want to get into AI:</strong> Python + statistics + machine learning. 2026 is the best time to enter AI with Python.</li>
<li><strong>If you only want mobile apps:</strong> Kotlin for Android, Swift for iOS, or React Native/Flutter for both.</li>
<li><strong>If you want to start international remote freelancing:</strong> Python + Django/FastAPI or Rust + Actix. This combo delivers the highest USD income.</li>
</ul>
<h2>Frequently Asked Questions</h2>
<h3>Is it too late to learn programming in 2026?</h3>
<p>No, it is not too late at all. Global developer demand is projected to grow by more than 25% by 2030, and companies are already facing a shortage of specialists. It does not matter how old you are or what you studied — if you can actually code and solve problems, your career is secure.</p>
<h3>Which language pays the most in 2026?</h3>
<p>According to the Stack Overflow Survey 2024, <strong>Rust, Go, and Python</strong> deliver the highest USD salaries in the international market. In Iran, TypeScript and React usually pay the highest freelance rates. But salary depends on skill and domain, not just language.</p>
<h3>What is the best language for AI?</h3>
<p>Definitely <strong>Python</strong>. All the major AI frameworks (TensorFlow, PyTorch, Hugging Face) were written for Python. If you want to get into AI, Python is both the first and last choice. Julia and C++ are used in specific cases (heavy computation) but you do not need to learn them to start.</p>
<h3>Is Rust hard to learn? Is it worth it?</h3>
<p>Rust has a steep learning curve, especially around ownership and borrowing. Most developers need three to six months to feel comfortable. But it pays off: higher salaries, more international opportunities, and access to safety-critical projects. If you are patient and your goal is systems programming, Rust is the best long-term investment.</p>
<h3>Should I learn TypeScript separately?</h3>
<p>If you already know JavaScript, TypeScript is essential, not optional. Almost every backend job listing in 2026 mentions TypeScript. Learning it after JavaScript only takes two weeks and dramatically improves the quality of your code.</p>
<h3>Will AI replace programmers?</h3>
<p>Not in the short term. AI is a powerful tool but does not replace human understanding, complex problem-solving, or architectural decisions. But developers who use AI as a tool will replace those who do not. So instead of fearing AI, learn how to work with it.</p>
<hr />
<p>If it is 2026 and you are just starting, the best thing you can do is pick one of the languages above and <strong>start without delay</strong>. The "right time" never arrives. The best language is the one you actually start with and build a real project on.</p>
<hr />
<p><em>Originally published on <a href="https://captaincode.tech/en/blog/best-programming-languages-2026">Captain Code</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[Fixing Laravel 419 Page Expired in Production (Livewire Included)]]></title><description><![CDATA[Works locally, 419 on the server? Usually the session cookie: APP_URL, proxies, SESSION_DOMAIN, Secure cookies, caching, Livewire.]]></description><link>https://capcodetech.hashnode.dev/laravel-419-page-expired-livewire-production</link><guid isPermaLink="true">https://capcodetech.hashnode.dev/laravel-419-page-expired-livewire-production</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Livewire]]></category><category><![CDATA[Security]]></category><category><![CDATA[debugging]]></category><dc:creator><![CDATA[Captain Code]]></dc:creator><pubDate>Mon, 19 Oct 2026 03:57:38 GMT</pubDate><content:encoded><![CDATA[<p>Short answer: a <strong>Laravel 419 Page Expired</strong> error means the CSRF token sent with your form or AJAX request doesn't match the token stored in the user's session. If everything works locally and you only get 419 on the server, the problem is almost always the session cookie, not your code: <code>APP_URL</code> and HTTPS disagree, Laravel doesn't know it's behind an HTTPS proxy, <code>SESSION_DOMAIN</code> or <code>SESSION_SECURE_COOKIE</code> is wrong, sessions aren't shared between servers, or a page cache is serving old HTML with an old token. This guide quickly rules out the simple local causes first, then goes through the production and Livewire branch.</p>
<h2>Who this guide is for</h2>
<p>You're in the right place if:</p>
<ul>
<li><p>you work with Laravel 10+ and know Blade and <code>@csrf</code>,</p>
</li>
<li><p>your form or Livewire component works locally,</p>
</li>
<li><p>but on the real server you see <code>419 Page Expired</code> or Livewire's "This page has expired" dialog.</p>
</li>
</ul>
<p>If you get 419 locally too, start with the "Simple branch". If it only happens in production, jump to the "Production branch".</p>
<h2>How is the CSRF token tied to the session?</h2>
<p>According to the <a href="https://laravel.com/framework/docs/12.x/csrf">official Laravel CSRF documentation</a>:</p>
<blockquote>
<p>Laravel automatically generates a CSRF "token" for each active user session managed by the application. This token is used to verify that the authenticated user is the person actually making the requests to the application. Since this token is stored in the user's session and changes each time the session is regenerated, a malicious application is unable to access it.</p>
</blockquote>
<p>So the token is bound to the <strong>session</strong>, and the session is bound to a <strong>cookie</strong>. A typical production request path:</p>
<pre><code class="language-text">Browser
   │  Cookie: laravel_session=... + XSRF-TOKEN=...
   ↓
CDN / Cloudflare (optional)
   ↓
Nginx (TLS termination or reverse proxy)
   ↓
PHP-FPM → web middleware group
   ↓
StartSession → session loaded from the cookie
   ↓
ValidateCsrfToken (Laravel 11/12) / PreventRequestForgery (Laravel 13)
   ↓
Request token vs. session token → OK or 419
</code></pre>
<p>Anywhere along this path, if the session cookie doesn't reach the app, the session changes, or the page HTML carries another session's token, you get <strong>419</strong>.</p>
<h2>Simple branch: <code>@csrf</code> and AJAX (usually local)</h2>
<h3>Scenario A: <code>@csrf</code> is missing from the form</h3>
<pre><code class="language-blade">{{-- ❌ Wrong: 419 --}}
&lt;form method="POST" action="/profile"&gt;
    &lt;input type="text" name="name"&gt;
    &lt;button type="submit"&gt;Submit&lt;/button&gt;
&lt;/form&gt;

{{-- ✅ Correct --}}
&lt;form method="POST" action="/profile"&gt;
    @csrf
    &lt;input type="text" name="name"&gt;
    &lt;button type="submit"&gt;Submit&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p><code>@csrf</code> simply renders this hidden field:</p>
<pre><code class="language-blade">&lt;input type="hidden" name="_token" value="{{ csrf_token() }}"&gt;
</code></pre>
<h3>Scenario B: a fetch request without a token</h3>
<pre><code class="language-javascript">// ❌ Wrong: 419
fetch('/profile/settings', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'ali' })
});

// ✅ Correct: token from the meta tag
fetch('/profile/settings', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
    },
    body: JSON.stringify({ name: 'ali' })
});
</code></pre>
<p>To make the token available to JavaScript, add this meta tag to <code>&lt;head&gt;</code>. <code>@csrf</code> only works inside a form, so it isn't enough for AJAX:</p>
<pre><code class="language-blade">&lt;head&gt;
    &lt;meta name="csrf-token" content="{{ csrf_token() }}"&gt;
&lt;/head&gt;
</code></pre>
<p>Note: routes in <code>routes/api.php</code> don't use the <code>web</code> middleware group (and therefore CSRF) by default. 419 comes from routes in the <code>web</code> group.</p>
<h3>Scenario C: Axios</h3>
<p>Per the <a href="https://laravel.com/framework/docs/12.x/csrf#csrf-x-xsrf-token">Laravel docs</a>, Axios reads the <code>XSRF-TOKEN</code> cookie on same-origin requests and sends it as the <code>X-XSRF-TOKEN</code> header automatically, so usually there's nothing to do. For cross-origin requests (e.g. a frontend on another subdomain), you also need to send cookies:</p>
<pre><code class="language-javascript">// resources/js/bootstrap.js
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true; // Axios 1.6+, for cross-origin
</code></pre>
<h2>Production branch: APP_URL, HTTPS and proxies</h2>
<h3><code>APP_URL</code> must match the real site URL</h3>
<pre><code class="language-dotenv"># ❌ Wrong: the site runs on https but APP_URL says http
APP_URL=http://example.com

# ✅ Correct
APP_URL=https://example.com
</code></pre>
<p><code>APP_URL</code> doesn't build the session cookie directly, but Laravel uses it to generate URLs (from the CLI, queued jobs and some packages), and a mismatch with the real URL is the first clue in many http/https problems. After changing <code>.env</code>, run <code>php artisan config:cache</code> again, or the old value keeps being read from the config cache. If your deploys are automated, make sure that step runs after <code>.env</code> is written.</p>
<h3>Laravel must know the request was HTTPS (trustProxies)</h3>
<p>HTTPS often terminates at a CDN or Nginx, and the request reaches the app over plain HTTP:</p>
<pre><code class="language-text">Browser (HTTPS) → CDN or Nginx (HTTPS) → Laravel app (HTTP)
</code></pre>
<p>If Laravel doesn't trust the proxy, it thinks the request was HTTP. As a result, <code>url()</code>, <code>route()</code> and <code>asset()</code> generate <code>http://</code> URLs, so your form <code>action</code> or the Livewire update endpoint becomes <code>http://</code>. If <code>SESSION_SECURE_COOKIE=true</code>, the browser <strong>won't send</strong> the Secure cookie on that <code>http://</code> request, a fresh session is created, and you get 419. And if the server redirects http to https, the POST body is lost in the redirect.</p>
<p>If Nginx sits in front of another server or container, forward the headers:</p>
<pre><code class="language-nginx">location / {
    proxy_pass http://laravel_backend;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;  # ← the key line
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
</code></pre>
<p>And in Laravel 11+ (<code>bootstrap/app.php</code>):</p>
<pre><code class="language-php">-&gt;withMiddleware(function (Middleware $middleware) {
    $middleware-&gt;trustProxies(at: '*'); // or only your proxy IPs/ranges
})
</code></pre>
<p>⚠️ <code>at: '*'</code> is only safe if PHP isn't reachable directly from the internet; otherwise anyone can spoof <code>X-Forwarded-Proto</code>. List your proxy IPs or ranges when you can.</p>
<p>On Laravel 13 there's one more reason this matters: per the <a href="https://laravel.com/framework/docs/13.x/csrf">Laravel 13 docs</a>, the <code>Sec-Fetch-Site</code> origin check only runs on secure connections. If Laravel sees the request as HTTP, that layer is skipped and everything falls back to the token check.</p>
<h3>Cloudflare in Flexible SSL mode</h3>
<p>In <code>Flexible</code> mode, Cloudflare talks HTTPS to the browser but plain HTTP to your server:</p>
<pre><code class="language-text">Browser (HTTPS) → Cloudflare (HTTPS) → Origin (HTTP)
</code></pre>
<p>That's the same scenario as above with one extra layer. The fix: switch SSL mode to <code>Full (strict)</code> with a certificate on the origin, and configure <code>trustProxies</code> correctly.</p>
<p>A common case: the site goes behind a CDN or proxy (Cloudflare, for example) and suddenly every Livewire form starts returning 419, even though no code changed. The cause is almost always that <code>trustProxies</code> isn't configured, so Laravel sees the request as HTTP, and combined with <code>SESSION_SECURE_COOKIE=true</code> the session cookie never arrives. That's why these two settings are the first things to check after any CDN or proxy change.</p>
<h2>Production branch: the session cookie (Domain, Secure, SameSite)</h2>
<h3><code>SESSION_DOMAIN</code> and subdomains</h3>
<pre><code class="language-dotenv"># Default: the cookie is host-only (just the host that served the page)
SESSION_DOMAIN=null

# If the session must be shared between app.example.com and admin.example.com
SESSION_DOMAIN=.example.com
</code></pre>
<p>A point that's often explained wrong: once you set a cookie Domain, whether <code>example.com</code> or <code>.example.com</code>, modern browsers send the cookie to <strong>all subdomains</strong> (the leading dot is ignored). Only an empty (null) <code>SESSION_DOMAIN</code> gives you a host-only cookie. So:</p>
<ul>
<li><p>If <code>SESSION_DOMAIN</code> doesn't match the real site domain (e.g. a staging value left in production), the browser rejects the cookie and every request starts a new session → 419.</p>
</li>
<li><p>If an old host-only session cookie and a new domain cookie with the same name both exist, the browser may send both and Laravel may read the wrong one. After changing <code>SESSION_DOMAIN</code>, also rename the cookie (<code>SESSION_COOKIE</code>) or clear cookies.</p>
</li>
</ul>
<p>Per the <a href="https://laravel.com/framework/docs/12.x/session#configuration">Laravel session documentation</a>, all session cookie settings live in <code>config/session.php</code> and are read from <code>.env</code>.</p>
<h3><code>SESSION_SECURE_COOKIE</code> and <code>SESSION_SAME_SITE</code></h3>
<p>The right combination for an HTTPS-only site:</p>
<pre><code class="language-dotenv">SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax
SESSION_HTTP_ONLY=true
</code></pre>
<p>Two cases that lead to 419:</p>
<ul>
<li><p><code>SESSION_SECURE_COOKIE=true</code> on a site still served over HTTP (e.g. staging without SSL): the browser won't store a Secure cookie over HTTP, so every form returns 419.</p>
</li>
<li><p><code>SESSION_SAME_SITE=none</code> without <code>Secure</code>: modern browsers reject the cookie.</p>
</li>
</ul>
<p><code>SESSION_SECURE_COOKIE=false</code> on HTTPS usually doesn't cause 419 by itself, but it's a security weakness. As <a href="https://stackshield.io/blog/laravel-session-security-configuration">StackShield's guide to Laravel session security</a> explains, the Secure flag makes the browser send the cookie only over HTTPS, so it can't be sniffed on shared networks.</p>
<p>Per the same source, <code>SameSite</code> has three values:</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Behavior</th>
<th>When to use</th>
</tr>
</thead>
<tbody><tr>
<td><code>lax</code> (Laravel default)</td>
<td>Sent on top-level navigation, not on cross-site POSTs</td>
<td>Most apps</td>
</tr>
<tr>
<td><code>strict</code></td>
<td>Never sent on cross-site requests</td>
<td>Sensitive apps; users arriving from an email link appear logged out</td>
</tr>
<tr>
<td><code>none</code></td>
<td>Always sent, <strong>requires</strong> <code>Secure</code></td>
<td>iframes or genuinely cross-site setups</td>
</tr>
</tbody></table>
<p><code>SESSION_HTTP_ONLY</code> defaults to <code>true</code>. Leave it, or JavaScript (and any XSS) can read the session cookie.</p>
<h2>Production branch: session driver, multiple servers and page caching</h2>
<h3>Sessions across multiple servers or containers</h3>
<p>In <a href="https://github.com/livewire/livewire/discussions/4799">Livewire discussion #4799</a>, one user reported 419 errors on Kubernetes with two pods and <code>SESSION_DRIVER=file</code>: the session was stored on one pod's disk, and the next request hit the other pod. The same applies to multiple PHP containers behind a load balancer. The fix: a shared driver such as <code>redis</code> or <code>database</code>.</p>
<p>On a single server with the <code>file</code> driver, make sure <code>storage/framework/sessions</code> is writable by the PHP-FPM user.</p>
<h3>Full-page caching or a CDN</h3>
<p>This one is rarely mentioned: if you added full-page caching for speed, or your CDN caches HTML pages, users receive HTML containing another session's <code>csrf_token()</code>. When they submit the form: 419. Exclude pages with forms or Livewire components from full-page caching, or refresh the token with a separate request after load.</p>
<h3>Session lifetime</h3>
<p>If a user leaves the page open and submits much later, the session may have expired. Laravel's default <code>SESSION_LIFETIME</code> is 120 minutes. For this case, create a friendly <code>resources/views/errors/419.blade.php</code> that tells the user to refresh.</p>
<h2>Diagnosis table: 8 scenarios and how to check them</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>Scenario</th>
<th>Symptom</th>
<th>How to check</th>
<th>Healthy result</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><code>@csrf</code> missing</td>
<td>Form always 419</td>
<td>View Source of the form</td>
<td><code>&lt;input name="_token"&gt;</code> present</td>
</tr>
<tr>
<td>2</td>
<td><code>csrf-token</code> meta tag missing</td>
<td>Only AJAX 419</td>
<td>View Source → <code>&lt;head&gt;</code></td>
<td><code>&lt;meta name="csrf-token"&gt;</code> present</td>
</tr>
<tr>
<td>3</td>
<td>Wrong <code>APP_URL</code></td>
<td>Links/redirects use <code>http://</code></td>
<td><code>php artisan tinker --execute="echo config('app.url');"</code></td>
<td><code>https://...</code></td>
</tr>
<tr>
<td>4</td>
<td>Laravel doesn't see HTTPS</td>
<td>Form <code>action</code> or Livewire URL is <code>http://</code></td>
<td>View Source, or a temporary route returning <code>request()-&gt;isSecure()</code> (always false in tinker)</td>
<td><code>true</code> and <code>https://</code> URLs</td>
</tr>
<tr>
<td>5</td>
<td>Wrong <code>SESSION_DOMAIN</code></td>
<td>Session cookie isn't stored</td>
<td>DevTools → Application → Cookies</td>
<td>Cookie present with the right Domain</td>
</tr>
<tr>
<td>6</td>
<td>Secure cookie over HTTP</td>
<td>Everything 419 on staging without SSL</td>
<td>DevTools → Cookies (Secure column) and the page URL</td>
<td>Secure only over HTTPS</td>
</tr>
<tr>
<td>7</td>
<td>Sessions not shared / permissions</td>
<td>New session_id on every request</td>
<td><code>SESSION_DRIVER</code> and server count; permission errors on <code>storage/framework/sessions</code></td>
<td>Shared driver, no permission errors</td>
</tr>
<tr>
<td>8</td>
<td>Cached HTML / expired session</td>
<td>419 after a while, or for all users</td>
<td>Response headers (<code>Cache-Control</code>, CDN cache headers), <code>config('session.lifetime')</code></td>
<td>Pages with forms aren't cached</td>
</tr>
</tbody></table>
<h2>Livewire: why does it return 419?</h2>
<p>Livewire sends the token itself: the script that <code>@livewireScripts</code> (or automatic injection in Livewire 3) adds to the page carries the current <code>csrf_token()</code>, and every <code>wire:click</code> and <code>wire:submit</code> request uses it. So you don't need <code>@csrf</code> for Livewire actions. If you get 419, it's almost always one of the session problems above. When a 419 comes back, Livewire shows its "This page has expired" dialog and offers a refresh.</p>
<p>Other fixes that worked in <a href="https://github.com/livewire/livewire/discussions/4799">discussion #4799</a>:</p>
<ul>
<li><p>After updating Livewire, if you published its assets, publish them again (<code>php artisan livewire:publish --assets</code>); old JS doesn't match the new backend.</p>
</li>
<li><p>Check <code>SESSION_DOMAIN</code> to confirm the session cookie actually gets stored.</p>
</li>
<li><p>If you define Livewire routes in a package, make sure they have the <code>web</code> middleware group.</p>
</li>
</ul>
<blockquote>
<p>Some people in that discussion suggest excluding <code>livewire/*</code> from CSRF. <strong>Don't.</strong> It hides the real problem and leaves every Livewire action unprotected.</p>
</blockquote>
<h2>Laravel 13: origin-aware CSRF protection</h2>
<p>Per the <a href="https://laravel.com/framework/docs/13.x/csrf">Laravel 13 docs</a>, the middleware is now <code>PreventRequestForgery</code> with two layers: it first checks the <code>Sec-Fetch-Site</code> header and allows same-origin requests without a token check; if that check isn't possible (older browser, or an insecure connection), it falls back to normal token validation.</p>
<p>If you're on Laravel 13 and getting 419:</p>
<ol>
<li><p>Check the request's <code>Sec-Fetch-Site</code> value in the Network tab. If it's <code>cross-site</code> or <code>same-site</code>, the request comes from another origin (subdomain, iframe) and needs a token.</p>
</li>
<li><p>For subdomains, consider <code>preventRequestForgery(allowSameSite: true)</code>.</p>
</li>
<li><p>If Laravel sees the request as HTTP (see trustProxies), the origin layer never runs.</p>
</li>
</ol>
<p>Note: in <code>originOnly: true</code> mode, rejected requests get <strong>403</strong> instead of 419.</p>
<h2>Why you should never disable CSRF</h2>
<p><strong>Never</strong> do this:</p>
<pre><code class="language-php">// ❌ Never
-&gt;withMiddleware(function (Middleware $middleware) {
    $middleware-&gt;validateCsrfTokens(except: ['*']);
})
</code></pre>
<p>It lets another site submit forms on behalf of your logged-in users (changing their email, deleting their account, and so on).</p>
<h3>The right exception: webhooks</h3>
<p>Only for third-party webhooks that can't send a token (Laravel 11 and 12; on Laravel 13 the method is <code>preventRequestForgery</code>):</p>
<pre><code class="language-php">-&gt;withMiddleware(function (Middleware $middleware): void {
    $middleware-&gt;validateCsrfTokens(except: [
        'stripe/*',
        'webhooks/*',
    ]);
})
</code></pre>
<p>Per the <a href="https://laravel.com/framework/docs/12.x/csrf#csrf-excluding-uris">Laravel documentation</a>:</p>
<blockquote>
<p>Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using Stripe to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes.</p>
</blockquote>
<p>⚠️ Even after excluding a webhook, always verify its signature (e.g. the <code>Stripe-Signature</code> header).</p>
<h2>FAQ</h2>
<p><strong>1. What's the difference between</strong> <code>ValidateCsrfToken</code> <strong>and</strong> <code>PreventRequestForgery</code><strong>?</strong></p>
<p><code>ValidateCsrfToken</code> is the middleware name in Laravel 11 and 12, and it only checks the token. In Laravel 13 it's renamed <code>PreventRequestForgery</code> and checks the <code>Sec-Fetch-Site</code> header before the token.</p>
<p><strong>2. Why does it work locally but not in production?</strong></p>
<p>Locally you usually have one domain, plain HTTP and one server. Production adds HTTPS, a proxy or CDN, subdomains, multiple servers and caching. The most common causes: <code>trustProxies</code> not configured, wrong <code>SESSION_DOMAIN</code> or <code>SESSION_SECURE_COOKIE</code>, a non-shared session driver, and cached HTML.</p>
<p><strong>3. Can I disable CSRF for just one route?</strong></p>
<p>Yes, with <code>validateCsrfTokens(except: [...])</code> (or <code>preventRequestForgery(except: [...])</code> on Laravel 13). But only for webhooks and third-party endpoints, never for user-facing routes.</p>
<p><strong>4. Where do I see cookies in DevTools?</strong></p>
<p>Chrome: F12 → Application → Storage → Cookies. Firefox: F12 → Storage → Cookies. Safari: Develop → Show Web Inspector → Storage → Cookies.</p>
<p><strong>5. Livewire 3 returns 419 in production. Where do I start?</strong></p>
<ol>
<li><p>View Source and check that the Livewire endpoint URL is <code>https://</code> (trustProxies).</p>
</li>
<li><p>In DevTools, check that the session cookie gets stored (<code>SESSION_DOMAIN</code>, <code>SESSION_SECURE_COOKIE</code>).</p>
</li>
<li><p>With multiple servers, use a shared session driver.</p>
</li>
<li><p>Make sure the page isn't served from a full-page cache or CDN.</p>
</li>
</ol>
<p><strong>6. Why does Livewire sometimes return 419 even with a valid token?</strong></p>
<p>Because the token is valid, but for a different session: the session expired, the request reached a server that doesn't have the session, or the HTML came from a cache. Check those three first.</p>
<hr />
<p>If you go through the two branches separately and work down the diagnosis table, it usually becomes clear quickly where the session gets lost. And remember: the fix for 419 is <strong>never</strong> disabling CSRF. It's one of your application's core defenses.</p>
<p><strong>Still stuck on 419, or don't have time to debug it?</strong> Give the error, your <code>.env</code> (without keys and passwords) and your Nginx config to <a href="https://captaincode.tech/en/labs/nyx">Nyx, Captain Code's AI coding assistant</a>, and debug it step by step. Or if you'd rather hand the whole thing off, from <a href="https://captaincode.tech/en/services/premium-web-development">custom Laravel web development</a> to <a href="https://captaincode.tech/en/services/security-maintenance">security and maintenance</a>, you can <a href="https://captaincode.tech/en/contact">bring your project to Captain Code</a>.</p>
<p>New to backend work? See <a href="https://captaincode.tech/en/blog/best-programming-languages-2026">the best programming languages to learn in 2026</a>.</p>
<p>Good luck. If you have a question or get stuck, ask in the comments below.</p>
<hr />
<p><em>Originally published on</em> <a href="https://captaincode.tech/en/blog/laravel-419-page-expired-livewire-production"><em>Captain Code</em></a><em>.</em></p>
]]></content:encoded></item></channel></rss>