Nextjs i18n seo
Audit/fix Next.js i18n SEO for the App Router — crawlability, 307→301 redirects, locale URLs, sitemap, hreflang. Use when diagnosing lost PageRank or i18n redirect bugs in middleware.From its SKILL.md
npx -y skills add augchan42/inkstone --skill nextjs-i18n-seoAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 1 command, including `curl -I https://example.com/some-page`.
SKILL.md
5.2 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Next.js i18n SEO Fix
Multilingual Next.js sites commonly ship with a crawlability-killing bug: locale redirects that use 307 Temporary instead of 301 Permanent. This means Googlebot never transfers PageRank to the actual indexed URL. Fix this first — it's a one-line change with immediate impact.
The Core Problem
When a user (or Googlebot) hits /hexagrams/14, Next.js middleware redirects to /en/hexagrams/14. The default NextResponse.redirect(url) issues a 307 Temporary Redirect. Google does not transfer PageRank on 307s. Result: any backlink to the clean URL is wasted.
Pages Router doesn't have this problem because getStaticPaths generates pages at their final URL with no redirect hop.
Phase 1: Audit the Site
1. Check the redirect status code
curl -I https://example.com/some-page
# Look for: Location: and HTTP/2 301 vs 307
2. Check robots.txt
https://example.com/robots.txt
- All paths should be allowed (or only
/api/blocked) - Sitemap URL must be listed
3. Check the sitemap
https://example.com/sitemap.xml
- Are all pages listed with their final (locale-prefixed) URL?
- Are
alternates.languageshreflang entries present?
4. Inspect the middleware
Look for NextResponse.redirect(url) — if no status code is passed, it's a 307.
Phase 2: Fix the 307 → 301 Bug
File: src/middleware.ts (or middleware.ts at root)
// ❌ BEFORE — issues 307 Temporary Redirect
const response = NextResponse.redirect(newUrl);
// ✅ AFTER — issues 301 Permanent Redirect (passes PageRank)
const response = NextResponse.redirect(newUrl, 301);
Also fix any locale alias redirects (e.g. zh-hans → zh-CN) the same way — they should already be 301 if added later, but verify.
Commit message:
fix: change locale redirect from 307 to 301 for SEO PageRank transfer
Phase 3 (Optional but Recommended): No-Prefix Default Locale
The locale prefix (/en/hexagrams/14) adds an extra path segment vs. /hexagrams/14. For English-first sites, removing the prefix from the default locale gives cleaner URLs and eliminates the redirect entirely for the primary language.
Middleware changes
const defaultLocale = 'en';
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const pathnameHasLocale = nonDefaultLocales.some(
(l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`
);
// Already has a non-default locale — pass through
if (pathnameHasLocale) return NextResponse.next();
// Has explicit /en/ prefix — strip it (301 to clean URL)
if (pathname.startsWith('/en/') || pathname === '/en') {
const url = request.nextUrl.clone();
url.pathname = pathname.replace(/^\/en/, '') || '/';
return NextResponse.redirect(url, 301);
}
// No locale prefix — serve as default (English) directly, no redirect
return NextResponse.next();
}
generateStaticParams changes
// Layout and page components: include '' (no prefix) for English
export async function generateStaticParams() {
return [
{ locale: '' }, // English — served at root paths
{ locale: 'zh-CN' },
{ locale: 'zh-TW' },
];
}
Sitemap changes
// English URLs use no prefix
const enUrl = `${baseUrl}/hexagrams/${h.id}`;
// Other locales keep prefix
const zhCNUrl = `${baseUrl}/zh-CN/hexagrams/${h.id}`;
Hreflang
<link rel="alternate" hreflang="en" href="https://example.com/hexagrams/14" />
<link rel="alternate" hreflang="zh-CN" href="https://example.com/zh-CN/hexagrams/14" />
<link rel="alternate" hreflang="x-default" href="https://example.com/hexagrams/14" />
Checklist
[ ] curl -I site.com/some-page → confirm 301 (not 307)
[ ] curl -I site.com/en/some-page → confirm 301 to /some-page (if no-prefix)
[ ] sitemap.xml lists final URLs (no redirect hops)
[ ] hreflang present on all pages with x-default
[ ] robots.txt allows all content paths, blocks /api/
[ ] generateStaticParams covers all locale variants
[ ] No cookie-only locale detection (Googlebot ignores cookies)
Why Pages Router Appeared Better
Pages Router with getStaticPaths serves pages at their exact URL with zero redirect hops. App Router with locale middleware adds a redirect layer. The framework itself isn't the problem — the redirect type (307 vs 301) and the locale URL strategy are what matter. App Router with 301 redirects and/or no-prefix default locale matches or exceeds Pages Router crawlability.
Real-World Comparison
| sixlines.online (App Router) | bookofchanges.academy (Pages Router) | |
|---|---|---|
| Hexagram URL | /en/hexagrams/14 | /hexagrams/14 |
| Redirect on clean URL | 307 → locale URL ❌ | none ✅ |
| Sitemap entries | 192 (64 × 3 locales) | 64 |
| Middleware on every req | yes | no |
| Fix | Change to 301 + consider no-prefix | — |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most marketing audience skills give in ~1.3k tokens
Counted across 690 of the 894 authors here whose files we hold, read 2026-08-07
- Apply Poppins font to headingsin 41 of 690, across 6 files
- Apply Lora font to body textin 41 of 690, across 6 files
- Use Arial fallback for headingsin 39 of 690, across 4 files
- Use Georgia fallback for body textin 39 of 690, across 4 files
- Maintain text hierarchy and formattingin 39 of 690, across 4 files
- Use accent colors for non-text shapesin 38 of 690, across 3 files
- Use RGB values for precise color matchingin 38 of 690, across 3 files
- Use brand colors for primary text and backgroundsin 36 of 690, across 1 file
- Read product marketing context file before asking questions, starting, or auditingin 35 of 690, across 23 files
- Use active voice instead of passive voicein 26 of 690, across 10 files
- Implement or generate appropriate JSON-LD structured datain 24 of 690, across 17 files
- Prioritize clarity over clevernessin 22 of 690, across 8 files
Said here and by no other author read
- Check redirect status codes using curl
- Verify sitemap lists final URLs
- Change locale redirects from 307 to 301
- Strip default locale prefixes for cleaner URLs
- Generate static parameters for all locale variants
- Update sitemap configurations for locale URLs
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.