一键导入
url-canonicalization
Enforce URL rules to prevent SEO issues and cost incidents. CRITICAL - never add searchParams to listing pages ($300 incident).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Enforce URL rules to prevent SEO issues and cost incidents. CRITICAL - never add searchParams to listing pages ($300 incident).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guide for adding environment variables. Use when adding new env vars to ensure all 5 locations are updated (code, env files, Coolify, workflow, GitHub secrets).
Enforce the internal API proxy layer pattern. Use when adding API endpoints, fetching data from backend, implementing HMAC signing, or working with lib/api/* or app/api/*.
Best practices for React 19 and Next.js 16 App Router. Use when creating components, hooks, or pages. Enforces server-first rendering, proper client boundaries, and modern React patterns.
MANDATORY checklist before writing ANY new code. Use this skill FIRST before implementing any feature, component, utility, type, or hook. Enforces DRY principle and existing pattern reuse.
Evaluate external PR suggestions (Gemini, CodeRabbit) against project conventions. Use when asked to review or agree with external code review comments.
Guide for CSP, security headers, and external scripts. Use fetchWithHmac for APIs, safeFetch for external services.
| name | url-canonicalization |
| description | Enforce URL rules to prevent SEO issues and cost incidents. CRITICAL - never add searchParams to listing pages ($300 incident). |
Enforce URL canonicalization rules to prevent SEO issues and CRITICAL cost incidents from dynamic page rendering.
NEVER add searchParams to page components in app/[place]/ routes.
Reading searchParams in a page component makes the page dynamic, creating a separate cache entry for every unique URL+query combination.
Real Incident: This caused a $300+ cost spike on December 28, 2025.
// ❌ FORBIDDEN - Makes page dynamic, creates millions of cache entries
export default async function PlacePage({
params,
searchParams, // ← NEVER DO THIS in listing pages
}: {
params: { place: string };
searchParams: { search?: string; distance?: string };
}) {
// This will cost you hundreds of dollars
const events = await fetchEvents({ ...searchParams });
}
// ✅ CORRECT - Page stays static (ISR)
export default async function PlacePage({
params,
}: {
params: { place: string };
}) {
// Base data fetched server-side (static)
const initialEvents = await fetchEvents({ place: params.place });
// Query params handled CLIENT-SIDE via SWR
return <HybridEventsList initialEvents={initialEvents} />;
}
| Condition | Canonical URL |
|---|---|
date = tots AND category = tots | /place |
date = tots AND category ≠ tots | /place/category |
date ≠ tots AND category = tots | /place/date |
date ≠ tots AND category ≠ tots | /place/date/category |
search, distance, lat, lon → Stay as query paramsdistance omitted when default (50km)?category=X&date=Y → Redirect to path segmentsThe proxy (proxy.ts) handles canonical redirects via handleCanonicalRedirects:
// These are handled automatically by middleware:
// /barcelona/tots/concerts → /barcelona/concerts (301)
// /barcelona?category=concerts&date=avui → /barcelona/avui/concerts (301)
// /catalunya/tots/tots → /catalunya (301)
import { buildCanonicalUrl, buildFilterUrl } from "@utils/url-filters";
// ✅ Correct - uses helper that enforces rules
const url = buildCanonicalUrl(filters);
// Alternative for dynamic categories:
// buildCanonicalUrlDynamic(filters, dynamicCategories)
// ❌ Wrong - manual string concatenation
const url = `/${place}/${date}/${category}`;
import { parseFiltersFromUrl, urlToFilterState } from "@utils/url-parsing";
import { getRedirectUrl } from "@utils/middleware-redirects";
Filtered URLs (with search, distance) should have noindex:
X-Robots-Tag header in proxy.tssearchParamssearchParams in a listing page? → STOP, use client-side SWRbuildCanonicalUrlDynamic/tots/ to URLs? → Check omission rules above/tots/ in URLs → Gets redirected, use canonical form