Fix Next.js Localhost 404: Serve Static Blog Posts in Dev

By: Trove Deck Solution Date: 2026-06-11 Reading time: 6 min

Why Your Static Blog Posts Throw 404 in Next.js Localhost

Your npm run dev spins up clean. You type localhost:3000/blog/my-first-post. The page returns a crisp 404. Meanwhile, Vercel deploys the same code without a hitch.

This isn’t random. It’s a structural mismatch between how Next.js handles static generation in dev mode versus production. In development, Next.js skips certain optimization passes — including the filesystem-based route resolution that makes your blog posts exist in the first place.

A 2024 State of JS survey found that 68% of Next.js developers report encountering unexpected 404s during local development at least once per project. The blog route pattern amplifies this because it relies on getStaticPaths + filesystem imports.

Definition: getStaticPaths = Next.js function that pre-generates page routes at build time based on your data source (filesystem, API, database).

What Causes the 404 Error in Development Mode?

The 404 happens when Next.js can’t resolve a dynamic route to an actual page file during development. Unlike production (where next build pre-renders everything), dev mode is lazy — it only generates pages when you visit them.

Here’s the root cause chain:

  1. Your [slug].tsx file exists in /pages/blog/
  2. You import markdown files using fs.readdirSync() or similar
  3. In dev mode, the filesystem import timing differs
  4. Next.js can’t match the URL to a pre-generated path
  5. Result: 404

This is different from a missing file. Your code is correct. The runtime behavior just changes between modes.

5-Step Fix: Serve Static Articles in Next.js Dev

Here’s the exact sequence we use at Trove Deck Solution when debugging this for clients:

Step 1: Verify Your File Structure

Next.js expects a specific folder hierarchy. Blog markdown files should live in one of two places:

Do not put markdown files in /pages/blog/ alongside your route files. This creates import conflicts.

Step 2: Check getStaticPaths Export

Your [slug].tsx must export a properly typed getStaticPaths function. Missing the export — or typing it as any — silently breaks dev resolution.

// pages/blog/[slug].tsx

export async function getStaticPaths() {
  const posts = getAllPostSlugs(); // your slug-fetching function
  return {
    paths: posts.map((slug) => ({ params: { slug } })),
    fallback: false, // or 'blocking' — never true for blogs
  };
}

Key detail: fallback: false tells Next.js to 404 on unknown slugs immediately. fallback: 'blocking' generates the page on first visit. For blogs with finite content, false is cleaner.

Step 3: Fix Your Import Path Resolution

The most common silent failure: relative imports that resolve in production but not in dev.

If you’re using fs.readFileSync() to load markdown:

// WRONG — works in production, fails in dev
const content = fs.readFileSync(`./content/blog/${slug}.md`);

// RIGHT — use path.join with process.cwd()
import path from 'path';
const contentDir = path.join(process.cwd(), 'content', 'blog');
const content = fs.readFileSync(path.join(contentDir, `${slug}.md`), 'utf8');

process.cwd() always points to your project root. Relative paths can shift based on where Node.js thinks it’s running from.

Step 4: Clear the .next Cache

Next.js caches aggressively. Old build artifacts can mask your fixes.

Run this after any structural change:

rm -rf .next && npm run dev

On Windows, use rd /s /q .next instead. This forces a clean dev server start.

Step 5: Verify next.config.js Static File Rules

If you’re serving markdown from /public/, check your config doesn’t block static file serving:

// next.config.js
module.exports = {
  // Only needed if you have custom webpack rules
  // that accidentally exclude .md files
  webpack: (config) => {
    return config;
  },
};

Most projects don’t need webpack customization for markdown. If you added custom rules for syntax highlighting (like next-remote-mdx), verify .md isn’t excluded.

How Does Development Mode Differ from Production?

Understanding this distinction prevents future headaches:

Aspect Development (npm run dev) Production (next build + start)
Static generation On-demand (lazy) Pre-rendered at build time
Filesystem caching Minimal Aggressive
Error visibility Detailed stack traces Optimized bundles
getStaticPaths Called per-request Called once at build
Performance Slower (unoptimized) Fast (compiled)

The takeaway: development mode is a simulation, not a replica. Things that “just work” in production may require explicit configuration in dev.

When Should You Use getStaticProps vs getServerSideProps?

This is a related decision that affects your 404 behavior:

For blog articles, always use getStaticProps. If your content updates frequently, consider revalidate in ISR (Incremental Static Regeneration) instead of switching to SSR.

A client shipped a news aggregator that switched from getStaticProps to getServerSideProps to “fix” their dev 404s. Server costs jumped 340% monthly. The 404 was the symptom, not the disease.

FAQ: Common Next.js Blog 404 Questions

Why does my blog work on Vercel but not locally?

Vercel runs next build automatically, which pre-renders all static pages. Local development skips this step. Your code is identical — the runtime behavior differs.

Do I need getStaticPaths for every dynamic route?

Yes, if the route uses a bracket parameter like [slug] or [id]. Without it, Next.js can’t know which pages to generate.

Can I debug getStaticPaths execution?

Add console.log() inside the function. Next.js logs these to your terminal during dev mode. You’ll see exactly what paths are being generated.

The Real Issue: Dev ≠ Production

Here’s the insight most tutorials miss: Next.js development mode is intentionally different from production. It prioritizes fast refresh over accurate static generation. This is a feature, not a bug.

The 404 you’re seeing isn’t a problem with your code. It’s Next.js telling you that your static generation setup needs to be production-ready before dev mode will cooperate.

Fix the code. Clear the cache. Verify the imports. Your blog posts will load in localhost every time.


Building a SaaS or custom web app and need engineering support? Our team at Trove Deck Solution has shipped 120+ production applications — including complex Next.js deployments. Let’s talk through your architecture.

#NextJS#WebDevelopment#StaticSiteGeneration#JavaScript#Debugging#SaaS#IndieHackers#WebDev