nextjs-i18n-seo — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited nextjs-i18n-seo (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
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.
curl -I https://example.com/some-page
# Look for: Location: and HTTP/2 301 vs 307https://example.com/robots.txt/api/ blocked)https://example.com/sitemap.xmlalternates.languages hreflang entries present?Look for NextResponse.redirect(url) — if no status code is passed, it's a 307.
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 transferThe 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.
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();
}// 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' },
];
}// English URLs use no prefix
const enUrl = `${baseUrl}/hexagrams/${h.id}`;
// Other locales keep prefix
const zhCNUrl = `${baseUrl}/zh-CN/hexagrams/${h.id}`;<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" />[ ] 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)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.
| 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 | — |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.