devops

How I Audit and Fix SEO Issues on a Next.js Blog (Real Checklist From a Live Site)

A practical walkthrough of auditing SEO on a Next.js 14 blog — fixing dependency CVEs, configuring Vercel env vars, custom domain DNS, and a full on-page SEO review checklist.

March 18, 2026·7 min read·
#seo#next.js#vercel#devops#security#dns

Running a technical blog isn't just writing — it's operating a small web platform. And like any production system, it needs regular audits.

This is a real walkthrough from auditing my own Next.js 14 blog. I'll cover dependency security, environment variable setup, DNS configuration, and a full on-page SEO checklist. Everything here is reproducible on your own site.


1. Dependency Security: Fixing a Vercel Build Block

The first thing I ran into was a blocked deployment. Vercel's security gate caught a vulnerable dependency before it could go live:

Error: Vulnerable version of next-mdx-remote detected (5.0.0).
Please update to version 6.0.0 or later.

This is actually Vercel doing its job. The platform scans your package.json and package-lock.json for known CVEs. If it finds a severity above the threshold, the build fails.

How to fix it:

# Check what's vulnerable
npm audit

# Update the specific package
npm install next-mdx-remote@^6.0.0

# Verify no remaining issues
npm audit --audit-level=high

For next-mdx-remote v5 → v6, the import path is compatible — no code changes needed. But always verify by checking the library's CHANGELOG before upgrading.

Key rule: Use ^X.0.0 ranges (caret), not ~X.0.0 (tilde). Caret allows minor version bumps which include security patches. Tilde only allows patch bumps — you'll miss security fixes.


2. Environment Variables in Vercel

For a Next.js site with AdSense, you need NEXT_PUBLIC_ADSENSE_ID set correctly in Vercel. This trips people up.

The NEXT_PUBLIC_ prefix means the variable is bundled at build time and available in the browser. Variables without this prefix are server-only and won't be accessible in client components.

Setting it in Vercel:

  1. Go to your project → SettingsEnvironment Variables
  2. Add NEXT_PUBLIC_ADSENSE_ID with value ca-pub-XXXXXXXXXXXXXXXX
  3. Check all three environments: Production, Preview, Development
  4. Trigger a redeploy — Vercel doesn't automatically redeploy when env vars change

Common mistake: Only setting it for Production. Preview deployments (PRs) will show ad placeholders and you won't catch display issues until they're already live.

Testing locally:

# .env.local (never commit this)
NEXT_PUBLIC_ADSENSE_ID=ca-pub-XXXXXXXXXXXXXXXX
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

Verify it's working in dev:

// Quick sanity check — add temporarily, then remove
console.log('AdSense ID:', process.env.NEXT_PUBLIC_ADSENSE_ID);

3. Custom Domain DNS Configuration

Connecting a custom domain to Vercel is straightforward but has two approaches — and the choice matters:

Option A: Vercel Nameservers (easier, less control)

Change your domain's nameservers at the registrar to Vercel's nameservers. Vercel manages all DNS records. You lose the ability to add custom records (like MX for email).

Option B: A Record + CNAME (recommended for most people)

Type    Name    Value               TTL
A       @       76.76.19.61         300
CNAME   www     cname.vercel-dns.com 300

Keep your nameservers at Cloudflare or your registrar. Add the A record for the apex domain and CNAME for www. SSL provisioning is automatic — Vercel uses Let's Encrypt and renews certificates automatically.

Use Option B if you need email (Google Workspace, Resend, etc.) or any other DNS records beyond what Vercel manages.

Propagation: DNS changes take 5 minutes to 48 hours depending on the registrar and TTL settings. Lower TTL (300s) means faster propagation. Cloudflare is near-instant for propagation within their network.


4. Full On-Page SEO Audit Checklist

This is the systematic checklist I run on every article before and after publishing. I'll use a real article as the example.

Technical SEO

| Check | How to verify | Status | |-------|--------------|--------| | <title> tag present | View page source or Chrome DevTools → Elements → <head> | ✅ | | Meta description (150-160 chars) | Check metadata object in page.tsx | ✅ | | Canonical URL | <link rel="canonical"> in <head> | ✅ via Next.js | | Open Graph tags | og:title, og:description, og:image | ✅ | | Schema markup (Article) | JSON-LD in <head> | ❌ missing | | Sitemap includes page | [site]/sitemap.xml | ✅ | | No broken links | Check with a crawler or manually | ⚠️ check | | Image alt text | Inspect <img> tags | ❌ missing on some |

Content SEO

| Check | Target | Status | |-------|--------|--------| | Target keyword in H1 | First 5 words preferred | ✅ | | Keyword in first 100 words | Naturally, not stuffed | ✅ | | Word count | 1,200–2,500 words | ⚠️ some articles short | | H2/H3 structure | At least 3 H2s | ✅ | | Internal links | 2–3 links to other posts | ❌ missing | | External links | 1–2 authoritative sources | ✅ | | Code examples | Increases time-on-page | ✅ |

Critical Gaps Found

1. Missing JSON-LD schema markup

Google can show article rich snippets in search results — but only if you have Article schema. For a DevOps/trading blog, this is free real estate.

Add to src/app/blog/[slug]/page.tsx:

const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  description: post.description,
  author: {
    '@type': 'Person',
    name: post.author,
  },
  datePublished: post.date,
  dateModified: post.date,
  publisher: {
    '@type': 'Organization',
    name: 'DevToCash',
    url: 'https://devtocash.com',
  },
};

// In the JSX:
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>

2. Missing internal links

Every article should link to 2-3 related articles. This keeps readers on the site (improves bounce rate, session duration — both AdSense signals) and spreads PageRank.

For a trading article → link to the DevOps article about automation. For a DevOps tutorial → link to the career article about consulting. Build a mental map of which articles relate to which.

3. Missing alt text on images

Any <img> without alt text is invisible to Google Image Search and fails accessibility audits. In MDX:

![Kubernetes architecture diagram showing pod scheduling across nodes](./kubernetes-arch.png)

Not just ![](./kubernetes-arch.png). Descriptive alt text is the difference between ranking for image searches and not.


5. FAQ Schema: The Underused Rich Snippet

For "how to" articles and guides, FAQ schema is one of the highest-value additions you can make. Google shows expandable FAQ dropdowns directly in search results — that's prime SERP real estate without needing to rank #1.

const faqJsonLd = {
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: [
    {
      '@type': 'Question',
      name: 'How long does DNS propagation take for Vercel?',
      acceptedAnswer: {
        '@type': 'Answer',
        text: 'DNS propagation typically takes 5 minutes to 48 hours. Using Cloudflare as your DNS provider speeds this up significantly, often to under 5 minutes within their network.',
      },
    },
    {
      '@type': 'Question',
      name: 'What is the NEXT_PUBLIC_ prefix for in Next.js?',
      acceptedAnswer: {
        '@type': 'Answer',
        text: 'Variables with the NEXT_PUBLIC_ prefix are bundled at build time and available in the browser. Without this prefix, environment variables are server-only and cannot be accessed in client components.',
      },
    },
  ],
};

Add this to any article that naturally contains Q&A sections. The boost to click-through rate is measurable.


6. Automated SEO Checks with Lighthouse

Run a Lighthouse audit on every article before pushing:

# Install Chrome Lighthouse CLI
npm install -g lighthouse

# Run audit
lighthouse https://devtocash.com/blog/your-slug --output=html --view

Target scores for an AdSense-viable blog:

  • Performance: ≥ 85
  • Accessibility: ≥ 90
  • Best Practices: ≥ 90
  • SEO: ≥ 95

If Performance is low, the usual culprits on a Next.js blog:

  • Large images not using next/image (get WebP conversion and lazy loading for free)
  • Font loading blocking render (use font-display: swap)
  • Third-party scripts (GA, AdSense) loading on the critical path

Summary: What to Fix First

If you're starting from scratch on a Next.js blog, prioritize in this order:

  1. Dependency securitynpm audit, fix any high/critical CVEs before deploying
  2. Env vars — set all NEXT_PUBLIC_ vars in Vercel across all environments
  3. DNS — A record + CNAME, keep nameservers at Cloudflare
  4. Content SEO — proper H1, meta description, 1200+ word articles
  5. Schema markup — add Article JSON-LD to every post page
  6. Internal linking — link every new article to 2-3 existing ones
  7. Image alt text — every image needs descriptive alt text
  8. FAQ schema — add to any how-to article for potential rich snippets

None of these are hard. They're just not default — you have to add them intentionally.


Found an issue I missed? Send it to hello@devtocash.com — I'll add it to the checklist.

#seo#next.js#vercel#devops#security#dns
D
DevToCashAuthor

Senior DevOps/SRE Engineer · 10+ years · Professional Trader (IDX, Crypto, US Equities)

I write about real infrastructure patterns and trading strategies I use in production and in live markets. No courses, no affiliate hype — just documentation of what actually works.

More about me →