از سیاست URL و اصلاح Slug تا Redirect، Sitemap، لینکسازی داخلی، SSR و پایش خودکار
در بسیاری از وبسایتها، پیادهسازی Canonical به افزودن یک تگ در بخش <head> صفحه محدود میشود:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
وجود این تگ ضروری و مفید است، اما بهتنهایی نشاندهنده وجود یک معماری صحیح Canonical نیست.
ممکن است یک صفحه تگ Canonical داشته باشد، اما همزمان:
- از چند URL مختلف با پاسخ
200 OKدر دسترس باشد؛ - Sitemap نسخه دیگری از URL را معرفی کند؛
- لینکهای داخلی به آدرس متفاوتی اشاره کنند؛
- نسخه دارای Slug اشتباه بدون Redirect نمایش داده شود؛
- پارامترهای Query باعث تولید تعداد زیادی URL مشابه شوند؛
- Frontend و Backend از دو الگوریتم متفاوت برای ساخت URL استفاده کنند؛
- Canonical در SSR با خروجی Client متفاوت باشد؛
- یا Cache، Canonical یک صفحه را به صفحه دیگری منتقل کند.
در چنین شرایطی، تگ Canonical وجود دارد، اما سامانه پیام منسجمی درباره URL اصلی محتوا ارسال نمیکند.
موتور جستوجو از میان مجموعهای از URLهای تکراری یا بسیار مشابه، یک URL را بهعنوان نسخه نماینده انتخاب میکند. URL اعلامشده از سوی سایت یکی از سیگنالهای این تصمیم است، اما موتور جستوجو ممکن است بر اساس مجموعه سیگنالهای موجود، URL دیگری را انتخاب کند.
بنابراین، Canonical را نباید فقط یک تگ HTML در نظر گرفت.
Canonical Architecture مجموعهای از سیاستها، قراردادها و سازوکارهای فنی است که باعث میشود تمام اجزای سامانه درباره هویت اصلی یک منبع و URL رسمی آن، پیام یکسانی ارسال کنند.
در این مقاله بررسی خواهیم کرد که چنین معماریای چگونه طراحی میشود، چه لایههایی در آن نقش دارند و چرا تصمیم Canonical باید از مدل کسبوکار آغاز شود و تا Monitoring و CI/CD ادامه پیدا کند.
Canonicalization دقیقاً چه مسئلهای را حل میکند؟
یک محتوا ممکن است به دلایل مختلف از چند URL در دسترس باشد.
برای مثال:
https://example.com/products/125
https://example.com/products/125/
https://example.com/Products/125
https://example.com/products/125/industrial-pump
https://example.com/products/125/wrong-slug
https://example.com/products/125/industrial-pump?utm_source=newsletter
https://example.com/products/125/industrial-pump?ref=homepage
از دید سامانه، ممکن است تمام این URLها به یک رکورد محصول با شناسه 125 منتهی شوند.
از دید کاربر نیز شاید همه آنها محتوای تقریباً یکسانی نمایش دهند.
اما از دید پروتکل HTTP، هر URL یک نشانی مستقل است. اگر همه این آدرسها پاسخ موفق دریافت کنند، موتور جستوجو باید تشخیص دهد که آیا با چند صفحه مستقل مواجه است یا چند نسخه از یک محتوا.
Canonicalization فرایند انتخاب URL نماینده برای مجموعهای از صفحات تکراری یا بسیار مشابه است. موتور جستوجو تلاش میکند یک نسخه را بهعنوان URL اصلی انتخاب و سایر نسخهها را در آن تجمیع کند.
مسئله فقط جلوگیری از نمایش چند URL مشابه در نتایج نیست.
یک Canonicalization ضعیف ممکن است پیامدهای زیر را داشته باشد:
- تقسیم سیگنالهای لینک میان چند URL؛
- Crawl شدن مکرر نسخههای کمارزش؛
- دشوارشدن تحلیل دادهها؛
- نمایش URL نامطلوب در نتایج؛
- بیثباتی گزارشهای Indexing؛
- کاهش قابلیت پیشبینی رفتار موتور جستوجو؛
- افزایش پیچیدگی مهاجرت و تغییر مسیرها.
محتوای تکراری درون یک سایت الزاماً بهمعنای تخلف از سیاستهای Spam نیست؛ اما میتواند تجربه کاربری نامناسبی ایجاد کند و منابع Crawl را صرف URLهایی کند که برای مالک سایت اهمیت ندارند.
Canonical URL با Canonical Tag یکسان نیست
یکی از مهمترین تمایزها در این حوزه، تفاوت میان این دو مفهوم است:
Canonical URL
URLی که بهعنوان نسخه اصلی و نماینده محتوا انتخاب میشود.
Canonical Tag
یکی از روشهایی که مالک سایت برای اعلام URL ترجیحی خود استفاده میکند.
برای مثال:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
این تگ میگوید:
مالک سایت ترجیح میدهد این URL بهعنوان نسخه اصلی این محتوا در نظر گرفته شود.
اما این تگ نمیگوید:
موتور جستوجو موظف است بدون توجه به سایر سیگنالها همین URL را انتخاب کند.
Canonical یک Hint یا نشانه قوی است، نه فرمانی که تمام تناقضهای معماری سایت را نادیده بگیرد.
اگر تگ Canonical یک URL را معرفی کند، اما Redirectها، Sitemap، لینکهای داخلی، Protocol یا محتوای صفحات پیام متفاوتی ارسال کنند، موتور جستوجو باید میان این سیگنالهای متناقض تصمیمگیری کند.
به همین دلیل، هدف معماری Canonical افزایش تعداد تگها نیست.
هدف، کاهش تناقض میان سیگنالهاست.
معماری Canonical از کجا آغاز میشود؟
معماری Canonical از تابع تولید تگ <link> آغاز نمیشود.
نقطه آغاز آن، پاسخ به این پرسش است:
منبع اصلی یا Entity اصلی این صفحه چیست؟
برای مثال، در یک Marketplace ممکن است URL زیر وجود داشته باشد:
/supplier-products/125/industrial-electric-motor
سامانه باید بداند:
- شناسه
125به چه موجودیتی اشاره میکند؟ - آیا این موجودیت Product است یا SupplierProduct؟
- آیا همین موجودیت در دستهبندی دیگری نیز نمایش داده میشود؟
- آیا تغییر تأمینکننده، نام یا دستهبندی هویت آن را تغییر میدهد؟
- آیا صفحه دیگری برای همین موجودیت وجود دارد؟
- آیا URL فقط مسیر دسترسی است یا بخشی از هویت عمومی منبع؟
این تصمیمها به مدل Domain وابسته هستند.
اگر موجودیت اصلی با شناسه پایدار شناسایی شود، URLهای مختلف را میتوان به همان موجودیت Resolve کرد و سپس URL رسمی آن را ساخت.
اما اگر سامانه فقط بر اساس متن Slug یا مسیر فعلی دستهبندی تصمیم بگیرد، کوچکترین تغییر در نام یا ساختار درختی میتواند هویت URL را تغییر دهد.
معماری پیشنهادی را میتوان چنین نمایش داد:
Incoming Request
↓
Route Parsing
↓
Stable Entity Resolution
↓
Entity State Validation
↓
Canonical URL Policy
↓
Slug and Path Generation
↓
Redirect Decision
↓
SSR Metadata Output
↓
Sitemap and Internal Links
اصل اول: هر Canonical باید به یک موجودیت یا منبع پایدار متصل باشد
Canonical نباید صرفاً از window.location.href یا URL فعلی درخواست ساخته شود.
برای مثال، این رویکرد از نظر معماری ضعیف است:
const canonical = `${baseUrl}${route.fullPath}`
چرا؟
زیرا route.fullPath ممکن است شامل موارد زیر باشد:
- Slug اشتباه
- Query Parameter
- Tracking Parameter
- حروف بزرگ
- Trailing Slash نامطلوب
- مسیر قدیمی
- فیلترهای غیرCanonical
- URLای که فقط به دلیل خطای Routing قابل دسترسی شده است
Canonical باید از داده معتبر Domain و سیاست مرکزی URL تولید شود.
برای نمونه:
const canonical = buildProductCanonicalUrl({
id: product.id,
name: product.name
})
در این مدل، Canonical از وضعیت رسمی موجودیت ساخته میشود، نه از آدرسی که کاربر اتفاقاً با آن وارد شده است.
اصل دوم: URL ورودی و URL Canonical دو مفهوم متفاوتاند
کاربر ممکن است با URL زیر وارد شود:
/products/125/wrong-name
اما URL رسمی موجودیت این باشد:
/products/125/industrial-pump
سامانه باید ابتدا URL ورودی را Parse کند، سپس Entity را با شناسه پایدار بیابد و بعد URL رسمی را بسازد.
Request URL:
/products/125/wrong-name
Resolved Entity:
Product ID = 125
Name = Industrial Pump
Canonical URL:
/products/125/industrial-pump
از اینجا یک تصمیم مهم ایجاد میشود:
- آیا صفحه با پاسخ
200نمایش داده شود و فقط Canonical صحیح داشته باشد؟ - یا درخواست باید به URL صحیح Redirect شود؟
در اغلب صفحات موجودیتمحور، زمانی که URL ورودی یک نسخه غیررسمی و بدون ارزش مستقل از همان محتواست، Redirect سمت سرور به URL رسمی معمولاً معماری تمیزتری ایجاد میکند.
اما این تصمیم همیشه خودکار نیست و باید با توجه به نوع اختلاف URL گرفته شود.
Canonical یا Redirect؛ کدامیک مناسبتر است؟
Canonical و Redirect دو ابزار متفاوتاند.
Redirect
در Redirect، کاربر و خزنده از URL قدیمی یا غیررسمی به URL جدید منتقل میشوند.
GET /products/125/wrong-slug
HTTP/1.1 301 Moved Permanently
Location: /products/125/industrial-pump
Redirect در عمل میگوید:
این منبع اکنون در URL دیگری قرار دارد.
Redirectها برای تغییر محل یک صفحه و هدایت کاربران و موتور جستوجو به آدرس جدید استفاده میشوند.
Canonical
در Canonical، صفحه همچنان از URL فعلی قابل دسترسی است، اما ترجیح مالک سایت برای نسخه اصلی اعلام میشود.
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
Canonical میگوید:
این صفحه از نظر محتوا متعلق به مجموعهای از صفحات مشابه است و URL ترجیحی این مجموعه آدرس دیگری است.
قاعده عملی
اگر نسخه جایگزین دیگر برای کاربر و سامانه ارزش مستقلی ندارد و باید کنار گذاشته شود، Redirect معمولاً انتخاب مناسبتری است.
اگر چند نسخه به دلایل فنی یا محصولی باید همچنان در دسترس بمانند، اما یک URL باید نماینده اصلی باشد، Canonical میتواند مناسبتر باشد.
انتخاب میان 301 و 308
کدهای 301 و 308 هر دو Redirect دائمی را بیان میکنند.
در 308 Permanent Redirect، منبع به یک URI دائمی جدید منتقل شده و Client باید روش درخواست را در انتقال حفظ کند. RFC 9110 تعریف این کد را در کنار سایر وضعیتهای Redirect قرار داده است.
برای درخواستهای معمول GET، تفاوت عملی 301 و 308 اغلب محدود است؛ اما در درخواستهای غیرGET، حفظ Method اهمیت بیشتری پیدا میکند.
برای صفحات عمومی SEO، استفاده از 301 بسیار رایج و قابل فهم است.
برای Routeهایی که باید Method و Body درخواست بدون تغییر حفظ شوند، 308 معنای دقیقتری دارد.
با این حال، تصمیم نباید فقط بر اساس SEO گرفته شود. رفتار Clientها، Reverse Proxy، Framework و API نیز باید بررسی شود.
302 و 307 برای تغییرات موقت
اگر انتقال واقعاً موقت است، استفاده از Redirect موقت مناسبتر است.
302 Found
307 Temporary Redirect
در 307، Method درخواست حفظ میشود.
یک اشتباه رایج این است که تمام Redirectها بهصورت 302 تولید شوند، حتی زمانی که URL قدیمی برای همیشه کنار گذاشته شده است.
اشتباه دیگری نیز وجود دارد: استفاده از Redirect دائمی برای شرایط موقتی مانند Maintenance یا آزمایش کوتاهمدت.
نوع Redirect باید معنای واقعی تغییر را بیان کند.
آیا Slug اشتباه باید Redirect شود؟
فرض کنیم URL رسمی محصول این باشد:
/products/125/industrial-pump
اما درخواست زیر دریافت شود:
/products/125/electric-generator
شناسه هنوز به همان محصول اشاره میکند، اما Slug اشتباه است.
سه رویکرد ممکن وجود دارد.
رویکرد اول: نمایش صفحه با پاسخ 200 و Canonical صحیح
200 OK
Canonical: /products/125/industrial-pump
مزیت:
- پیادهسازی سادهتر
- کاربر به محتوای صحیح میرسد
محدودیت:
- تعداد زیادی URL قابل دسترسی باقی میماند
- هر متن دلخواهی ممکن است به همان شناسه متصل شود
- Log، Analytics و Crawl پراکنده میشوند
- لینکهای اشتباه ممکن است سالها باقی بمانند
رویکرد دوم: Redirect به URL صحیح
301 → /products/125/industrial-pump
مزیت:
- فقط یک URL عملی باقی میماند
- لینکها و Crawl به نسخه رسمی منتقل میشوند
- URL مرورگر اصلاح میشود
- Analytics تمیزتر خواهد بود
این رویکرد در بسیاری از صفحات Entity-based مناسبتر است.
رویکرد سوم: پاسخ 404
اگر Slug بخشی از هویت منبع باشد یا عدم تطابق آن احتمال اشاره به موجودیت دیگری را نشان دهد، میتوان URL را نامعتبر در نظر گرفت.
این روش در معماریهایی مناسب است که Route صرفاً با ID Resolve نمیشود.
تفاوت SSR Redirect با Client-side Redirect
در برنامههای SPA یا Frameworkهایی مانند Nuxt، ممکن است اصلاح URL فقط در Client انجام شود:
onMounted(() => {
router.replace(correctUrl)
})
این روش برای Canonicalization اصلی مناسب نیست.
چرا؟
زیرا پیش از اجرای JavaScript:
- سرور پاسخ
200برای URL اشتباه فرستاده است؛ - HTML اولیه ممکن است Canonical دیگری داشته باشد؛
- خزنده باید JavaScript را اجرا کند تا اصلاح را ببیند؛
- کاربر ممکن است تغییر URL را با تأخیر مشاهده کند؛
- امکان Flash یا Hydration mismatch وجود دارد.
برای URLهای عمومی و قابلایندکس، تصمیم Redirect بهتر است در SSR، Server Middleware، Backend یا Reverse Proxy اجرا شود.
نمونه مفهومی در Nuxt:
export default defineNuxtRouteMiddleware(async (to) => {
const id = Number(to.params.id)
if (!Number.isInteger(id) || id <= 0) {
throw createError({
statusCode: 404,
statusMessage: 'Page not found'
})
}
const entity = await getProductRouteIdentity(id)
if (!entity) {
throw createError({
statusCode: 404,
statusMessage: 'Product not found'
})
}
const canonicalPath = buildProductPath(entity)
if (to.path !== canonicalPath) {
return navigateTo(canonicalPath, {
redirectCode: 301
})
}
})
نکته مهم این است که منطق Route Identity نباید به درخواست کامل و پرهزینه صفحه وابسته باشد، مگر آنکه Cache مناسبی وجود داشته باشد.
حساسیت به حروف بزرگ و کوچک
در بخشی از زیرساختها، URLهای زیر ممکن است دو مسیر متفاوت تلقی شوند:
/Products/125
/products/125
در بخش دیگری از سیستم، هر دو ممکن است به یک Handler برسند.
این عدم قطعیت باید با سیاست روشن برطرف شود.
معمولاً بهتر است URLهای عمومی دارای یک فرم استاندارد باشند؛ برای مثال:
lowercase path
اگر درخواست با Case متفاوت وارد شود، سامانه میتواند آن را به نسخه رسمی Redirect کند.
اما باید مراقب بود:
- آیا فایلهای Static به Case حساس هستند؟
- آیا CDN همان رفتار Origin را دارد؟
- آیا Nginx و Application رفتار یکسانی دارند؟
- آیا Redirect باعث Loop نمیشود؟
- آیا URLهای قدیمی با حروف متفاوت در بیرون سایت وجود دارند؟
Trailing Slash
URLهای زیر از نظر نحوی متفاوتاند:
/products/125
/products/125/
وبسایت باید یکی از این دو حالت را بهعنوان سیاست رسمی انتخاب کند.
سپس تمام لایهها باید همان سیاست را اجرا کنند:
- Route generation
- Redirect
- Canonical
- Sitemap
- Internal links
- Open Graph
- APIهای عمومی
- Cache key
- CDN rules
اشتباه رایج این است که Canonical بدون Slash باشد، اما Framework لینکهای داخلی را با Slash تولید کند و Nginx نیز هر دو نسخه را با پاسخ 200 ارائه دهد.
HTTP و HTTPS
در سایت Production، Canonical معمولاً باید به نسخه HTTPS اشاره کند.
اما صرفاً تغییر Canonical کافی نیست.
نسخه HTTP نیز باید بهصورت دائمی به HTTPS Redirect شود:
http://example.com/page
↓ 301
https://example.com/page
اگر نسخه HTTP پاسخ 200 بدهد و فقط Canonical آن HTTPS باشد، هنوز دو نسخه عملیاتی از محتوا در دسترس هستند.
همچنین باید مراقب Headerهای Proxy مانند موارد زیر بود:
X-Forwarded-Proto
X-Forwarded-Host
Forwarded
اگر Application پشت CDN یا Reverse Proxy قرار دارد و Protocol خارجی را بهدرستی تشخیص ندهد، ممکن است Canonicalهای HTTP تولید کند، در حالی که کاربر از HTTPS وارد شده است.
Host و نسخههای دامنه
ممکن است سایت از چند Host قابل دسترسی باشد:
https://example.com
https://www.example.com
https://origin.example.com
https://example.ir
معماری باید مشخص کند:
- Host اصلی چیست؟
- آیا دامنه جایگزین Redirect میشود؟
- آیا Origin مستقیماً قابل دسترسی است؟
- آیا Canonical همیشه Host اصلی را تولید میکند؟
- آیا CDN Host را به Application منتقل میکند؟
- آیا محیط Staging ممکن است Canonical Production تولید کند یا برعکس؟
بهتر است Canonical Base URL از Configuration معتبر محیط دریافت شود، نه از Host خام درخواست؛ بهخصوص زمانی که سایت پشت چند Proxy قرار دارد.
const canonicalBaseUrl = runtimeConfig.public.canonicalBaseUrl
Query Parameterها و Canonical
Query Parameterها یکی از اصلیترین منابع تولید URLهای مشابه هستند.
برای مثال:
/products/125/industrial-pump?utm_source=linkedin
/products/125/industrial-pump?ref=homepage
/products/125/industrial-pump?campaign=summer
اگر این پارامترها فقط برای Tracking باشند و محتوای اصلی صفحه را تغییر ندهند، Canonical معمولاً باید به URL بدون این پارامترها اشاره کند:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
اما همه Query Parameterها قابل حذف نیستند.
برای مثال:
/category/pumps?page=2
/category/pumps?sort=price
/category/pumps?pressure=10bar
/category/pumps?brand=sample
هرکدام معنای متفاوتی دارند.
پارامترهای Tracking
معمولاً هویت محتوا را تغییر نمیدهند.
Pagination
نمایانگر بخش دیگری از فهرست است.
Sort
ترتیب نمایش را تغییر میدهد، اما ممکن است مجموعه آیتمها همان باشد.
Filter
ممکن است مجموعهای کاملاً متفاوت و دارای Search Intent مستقل ایجاد کند.
Search Query
ممکن است صفحهای پویا و کمارزش یا در برخی پروژهها Landing Pageای برنامهریزیشده باشد.
بنابراین نباید تمام Queryها را بدون تحلیل حذف یا به صفحه پایه Canonical کرد.
خطر Canonical کردن تمام فیلترها به دستهبندی اصلی
فرض کنید این URL وجود دارد:
/pumps?type=centrifugal
اگر این صفحه:
- محتوای مستقل دارد؛
- عنوان و Heading مشخص دارد؛
- تقاضای جستوجویی مستقلی را پاسخ میدهد؛
- محصولات مرتبط و کافی دارد؛
- لینک داخلی دریافت میکند؛
ممکن است یک Landing Page معتبر باشد.
اگر Canonical آن به /pumps اشاره کند، سایت عملاً اعلام میکند که صفحه فیلترشده نسخه مستقلی نیست.
در مقابل، ترکیبی مانند زیر شاید ارزش مستقلی نداشته باشد:
/pumps?type=centrifugal&sort=price-desc&view=grid&session=123
Canonical Architecture باید Query Parameterها را طبقهبندی کند، نه اینکه فقط آنها را حذف کند.
نمونه یک Policy:
type QueryParameterPolicy =
| 'tracking'
| 'presentation'
| 'pagination'
| 'indexable-filter'
| 'non-indexable-filter'
| 'invalid'
سپس تصمیم Canonical بر اساس این طبقهبندی انجام میشود.
Pagination و Canonical
یکی از خطاهای رایج این است که تمام صفحات Pagination به صفحه اول Canonical شوند:
/category/pumps?page=2
Canonical → /category/pumps
اگر صفحه دوم مجموعه متفاوتی از آیتمها را نمایش دهد، صفحه دوم نسخه تکراری کامل صفحه اول نیست.
Canonical کردن تمام صفحات به صفحه اول ممکن است سیگنال نادرستی درباره محتوای صفحات ایجاد کند.
در بسیاری از معماریها، هر صفحه Pagination باید Self-referencing Canonical داشته باشد:
/category/pumps?page=2
Canonical → /category/pumps?page=2
البته تصمیم نهایی به ساختار صفحه، مدل پیمایش و سیاست Indexability وابسته است.
اگر صفحات بعدی noindex باشند، باز هم باید بررسی شود که آیا دسترسی خزنده و لینکهای آیتمها حفظ میشود یا خیر.
Sort و View Mode
پارامترهایی مانند موارد زیر معمولاً هویت محتوایی مستقلی ایجاد نمیکنند:
?sort=price-asc
?sort=newest
?view=grid
?view=list
در این شرایط، Canonical معمولاً میتواند به نسخه استاندارد دستهبندی اشاره کند.
اما این تصمیم باید با Cache، Internal Links و Routing هماهنگ باشد.
بهتر است لینکهای داخلی اصلی مستقیماً به URL Canonical اشاره کنند و پارامترهای نمایشی از طریق State یا کنترلهای محدود مدیریت شوند.
Self-referencing Canonical
یک صفحه اصلی و Canonical نیز بهتر است Canonical خودارجاع داشته باشد:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
مزایای آن:
- سیاست URL صفحه بهصراحت اعلام میشود؛
- پارامترهای ناشناخته راحتتر از نسخه اصلی تفکیک میشوند؛
- پیادهسازی تمام Page Typeها یکدست میشود؛
- تست خودکار سادهتر خواهد بود؛
- تغییرات ناخواسته سریعتر شناسایی میشوند.
Self-referencing Canonical نباید از URL خام Request ساخته شود. باید از Canonical Policy تولید شود.
Canonical باید URL مطلق باشد
بهتر است Canonical با URL کامل تولید شود:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
نه فقط مسیر نسبی:
<link
rel="canonical"
href="/products/125/industrial-pump"
/>
استفاده از URL مطلق ابهام مربوط به Host، Protocol و Base URL را کاهش میدهد.
البته تولید URL مطلق نیز نیازمند Configuration صحیح محیط و Proxy است.
Canonical در فایلهای غیرHTML
Canonical فقط محدود به تگ HTML نیست.
برای منابع غیرHTML مانند PDF یا اسناد، میتوان از HTTP Header استفاده کرد:
Link: <https://example.com/documents/technical-guide.pdf>; rel="canonical"
مستندات Google نیز استفاده از Header با رابطه canonical را برای فایلهای غیرHTML توضیح میدهند.
این قابلیت در سامانههایی مهم است که یک محتوا را هم در قالب صفحه HTML و هم فایل PDF یا Word منتشر میکنند.
با این حال باید تصمیم گرفت که کدام نسخه واقعاً منبع اصلی است.
Canonical و Sitemap
Sitemap باید URLهای اصلی، قابلایندکس و Canonical را معرفی کند.
اگر صفحهای چنین Canonical داشته باشد:
Canonical:
/products/125/industrial-pump
اما Sitemap این URL را ثبت کند:
/products/125
سایت دو پیام متفاوت ارسال میکند.
Sitemap نباید فقط خروجی خام جدول پایگاه داده یا تمام Routeهای قابل تولید باشد.
پیش از ورود URL به Sitemap باید بررسی شود:
- Entity فعال است؟
- صفحه قابلایندکس است؟
- URL رسمی چیست؟
- آیا Redirect میشود؟
- آیا Canonical آن به خودش اشاره میکند؟
- آیا محتوای کافی دارد؟
- آیا دسترسی عمومی دارد؟
- آیا Host و Protocol صحیح هستند؟
یک طراحی مناسب این است که Sitemap و Canonical از یک URL Policy مشترک استفاده کنند:
const canonicalPath = urlPolicy.buildProductPath(product)
همین تابع یا سرویس باید در موارد زیر استفاده شود:
- Canonical
- Sitemap
- Internal links
- Redirect validation
- Open Graph URL
- Schema
url - Breadcrumb item URL
Canonical و لینکسازی داخلی
اگر Canonical صفحه به URL رسمی اشاره کند، اما سایت دائماً به نسخه غیررسمی لینک دهد، موتور جستوجو پیامهای متناقض دریافت میکند.
مثال نامناسب:
<a href="/products/125">مشاهده محصول</a>
در حالی که Canonical این است:
/products/125/industrial-pump
در این حالت، هر کلیک داخلی باعث Redirect میشود.
پیامدها:
- افزایش Redirect داخلی
- پیچیدهشدن Crawl
- آلودگی Log
- کاهش کیفیت Analytics
- افزایش Latency
- احتمال ایجاد Chain در آینده
اصل مناسب:
لینکهای داخلی سایت باید تا حد ممکن مستقیماً به URL Canonical اشاره کنند.
Redirect باید برای سازگاری با لینکهای قدیمی و ورودیهای خارجی باشد، نه بهعنوان بخشی عادی از Navigation داخلی.
Canonical و Breadcrumb
URLهای موجود در Breadcrumb نیز باید با URL Policy مرکزی تولید شوند.
فرض کنیم محصول در مسیر زیر نمایش داده میشود:
Home
→ Industrial Equipment
→ Pumps
→ Industrial Pump
اگر Breadcrumb به URL قدیمی دستهبندی لینک دهد، در حالی که Sitemap و Canonical نسخه جدید را معرفی میکنند، معماری دوباره دچار ناسازگاری میشود.
در ساختارهای چندوالدی، باید مشخص شود که Breadcrumb:
- مسیر Navigation فعلی است؛
- مسیر اصلی و ترجیحی موجودیت است؛
- یا مسیری است که کاربر از آن وارد شده است.
برای SEO و Structured Data، معمولاً نیاز به یک مسیر پایدار و قابل پیشبینی داریم.
Canonical و Schema.org
فیلدهای URL در دادههای ساختیافته نیز باید با Canonical هماهنگ باشند.
برای مثال:
{
"@type": "Product",
"@id": "https://example.com/products/125/industrial-pump#product",
"url": "https://example.com/products/125/industrial-pump",
"name": "Industrial Pump"
}
اگر url یا @id به نسخه دیگری از URL اشاره کند، گراف موجودیتها دچار چندگانگی میشود.
بهتر است URLهای زیر همگی از یک Source تولید شوند:
- Canonical
WebPage.urlProduct.url@id- Breadcrumb items
- Open Graph URL
- Sitemap entry
Canonical و Open Graph
Open Graph معمولاً شامل فیلدی مانند زیر است:
<meta
property="og:url"
content="https://example.com/products/125/industrial-pump"
/>
این URL باید با Canonical هماهنگ باشد.
اگر کاربر صفحهای با Tracking Parameter را Share کند، بهتر است Preview اجتماعی به URL رسمی متصل شود، مگر آنکه نیاز محصولی مشخصی خلاف آن وجود داشته باشد.
Canonical در معماری Microservice
در معماری Microservice، اطلاعات لازم برای تولید Canonical ممکن است در چند سرویس توزیع شده باشد.
برای مثال:
- Route در Frontend تعریف شده است؛
- شناسه و نام محصول در Product Service است؛
- نوع موجودیت در Category Service مشخص میشود؛
- وضعیت انتشار در Catalog Service قرار دارد؛
- Tenant یا Domain در Gateway تعیین میشود.
اگر Frontend برای هر Canonical مجبور باشد چند سرویس را صدا بزند، هزینه SSR افزایش پیدا میکند.
راهکارهای ممکن:
SEO Route Identity Contract
یک API سبک برای هویت Route:
{
"id": 125,
"entityType": "product",
"name": "Industrial Pump",
"slug": "industrial-pump",
"isActive": true,
"canonicalPath": "/products/125/industrial-pump"
}
Cache کردن Route Identity
اطلاعات هویتی URL معمولاً نسبت به دادههای عملیاتی کمتر تغییر میکنند و میتوانند Cache شوند.
تولید Canonical در Backend
Backend میتواند canonicalPath را به DTO اضافه کند.
مزیت:
- منطق URL در یک نقطه قرار میگیرد.
محدودیت:
- Backend به ساختار Route رابط کاربری وابسته میشود.
Shared URL Policy
Frontend و Backend از یک Specification یا Contract مشترک استفاده کنند.
این راهکار زمانی مفید است که چند Client یا چند Frontend وجود دارد.
آیا Canonical را Backend تولید کند یا Frontend؟
پاسخ عمومی واحدی وجود ندارد.
تولید در Backend
مزایا:
- دسترسی مستقیم به Entity
- کنترل مرکزی
- کاهش احتمال الگوریتم متفاوت
محدودیتها:
- وابستگی Domain Service به ساختار Public URL
- دشواری پشتیبانی از چند Domain یا Client
- احتمال سختشدن مهاجرت Frontend
تولید در Frontend
مزایا:
- آگاهی از Route
- کنترل نزدیک به Rendering
- انعطاف در چند Domain
محدودیتها:
- احتمال منطق تکراری
- وابستگی به داده ناقص
- ناهماهنگی میان Client و Server
- خطر تولید از Route خام
راهکار متعادل
Backend هویت و داده پایدار Entity را فراهم کند و یک URL Policy مرکزی در لایه Web، URL نهایی را تولید کند.
Backend:
Entity Identity
Web Application:
Public URL Policy
SEO Engine:
Canonical Output
Canonical در SSR و Hydration
Canonical باید در HTML اولیه SSR وجود داشته باشد.
نمونه:
<head>
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
</head>
اگر Canonical فقط پس از Hydration اضافه شود:
- HTML اولیه آن را ندارد؛
- ممکن است Client مقدار متفاوتی تولید کند؛
- خطاهای Hydration ایجاد شود؛
- ابزارهای ساده بررسی صفحه خروجی ناقص ببینند.
همچنین State مربوط به Canonical باید میان Navigationها پاکسازی شود.
یکی از خطاهای خطرناک در برنامههای SSR این است که Canonical صفحه قبلی برای لحظهای یا در شرایط Race Condition روی صفحه بعدی باقی بماند.
یک طراحی مناسب:
const canonicalState = useState<string | null>(
'canonical-url',
() => null
)
در شروع Navigation:
canonicalState.value = null
و پس از Resolve شدن Entity:
canonicalState.value = buildCanonicalUrl(entity)
اما باید مراقب State مشترک سمت سرور بود. State هر Request باید کاملاً ایزوله باشد.
خطر تولید Canonical از route.fullPath
نمونه نامناسب:
useHead({
link: [
{
rel: 'canonical',
href: `${baseUrl}${route.fullPath}`
}
]
})
اگر URL چنین باشد:
/products/125/industrial-pump?utm_source=email
Canonical نیز پارامتر Tracking را حفظ میکند.
یا اگر کاربر با Slug اشتباه وارد شده باشد:
/products/125/wrong-slug
Canonical همان URL اشتباه خواهد شد.
روش بهتر:
const canonicalUrl = computed(() =>
product.value
? buildProductCanonicalUrl(product.value)
: null
)
Canonical برای صفحات حذفشده
اگر موجودیت حذف یا غیرفعال شده باشد، Canonical بهتنهایی راهحل مناسبی نیست.
باید وضعیت واقعی منبع مشخص شود.
منبع جایگزین واقعی دارد
اگر صفحه جدید دقیقاً جایگزین صفحه قدیمی است:
301 → Replacement URL
منبع برای همیشه حذف شده است
404 Not Found
یا در مواردی که حذف دائمی و آگاهانه باید صریحتر بیان شود:
410 Gone
محصول موقتاً ناموجود است
اگر خود موجودیت همچنان وجود دارد، صفحه میتواند 200 OK باقی بماند و وضعیت عدم موجودی را نمایش دهد.
Redirect کردن تمام محصولات ناموجود به دستهبندی والد، معمولاً رفتار درستی نیست؛ زیرا دستهبندی جایگزین دقیق همان محصول نیست.
Canonical کردن صفحه حذفشده به دستهبندی نیز فقط برای پنهانکردن وضعیت حذف، معماری مناسبی محسوب نمیشود.
Canonical به صفحه والد
یکی از خطاهای رایج این است که صفحات کممحتوا یا غیرفعال به دستهبندی والد Canonical شوند:
/products/125/industrial-pump
Canonical → /categories/pumps
اما صفحه محصول و صفحه دستهبندی دو هدف و محتوای متفاوت دارند.
Canonical برای صفحات تکراری یا بسیار مشابه است، نه ابزاری عمومی برای انتقال اهمیت هر صفحه به صفحه قویتر.
اگر صفحه محصول نباید وجود داشته باشد، باید درباره Redirect، 404، 410 یا Noindex تصمیم گرفت.
Canonical Chain
Canonical Chain زمانی رخ میدهد که:
Page A → Canonical B
Page B → Canonical C
معماری بهتر این است که تمام نسخهها مستقیماً به URL نهایی اشاره کنند:
Page A → Canonical C
Page B → Canonical C
Page C → Canonical C
Chainها معمولاً نتیجه مهاجرت ناقص یا استفاده از داده قدیمی هستند.
برای جلوگیری از آنها، Canonical باید از وضعیت فعلی Entity ساخته شود، نه از Canonical ذخیرهشده در رکورد قبلی.
Canonical Loop
Canonical Loop نمونهای مانند این است:
Page A → Canonical B
Page B → Canonical A
این وضعیت نشان میدهد سیستم هویت اصلی محتوا را نمیداند.
Loop میتواند به دلایل زیر ایجاد شود:
- دادههای مهاجرت اشتباه؛
- Ruleهای متناقض؛
- Canonical مبتنی بر Referer؛
- Tenant اشتباه؛
- الگوریتم متفاوت میان Frontend و Backend؛
- Cache قدیمی.
Canonical Loop باید در تست و Monitoring بهصورت خودکار شناسایی شود.
Cross-domain Canonical
گاهی یک محتوا در چند Domain منتشر میشود و یک نسخه باید بهعنوان منبع اصلی معرفی شود.
در این حالت Cross-domain Canonical میتواند استفاده شود:
<link
rel="canonical"
href="https://primary.example.com/article/technical-seo"
/>
اما پیش از آن باید بررسی شود:
- آیا محتوا واقعاً تکراری یا بسیار مشابه است؟
- آیا مالک هر دو Domain مشخص است؟
- آیا مقصد قابلایندکس است؟
- آیا Canonical مقصد Self-referencing است؟
- آیا سایت مقصد پاسخ موفق و پایدار دارد؟
- آیا این تصمیم با قرارداد انتشار محتوا سازگار است؟
Cross-domain Canonical نباید صرفاً برای انتقال اعتبار از یک صفحه نامرتبط استفاده شود.
صفحات چندزبانه و Canonical
Canonical و hreflang دو مسئله متفاوتاند.
نسخه فارسی و انگلیسی یک مقاله، اگر ترجمههای واقعی و صفحات مستقل باشند، معمولاً باید Self-referencing Canonical داشته باشند:
/fa/technical-seo
Canonical → /fa/technical-seo
/en/technical-seo
Canonical → /en/technical-seo
سپس رابطه زبانی با hreflang اعلام میشود.
Canonical کردن همه زبانها به نسخه انگلیسی میتواند باعث شود نسخههای زبانی بهعنوان صفحات مستقل در نظر گرفته نشوند.
Canonical و محتوای شخصیسازیشده
فرض کنیم صفحه برای کاربران مختلف بخشی از اطلاعات را متفاوت نمایش میدهد:
- کاربر مهمان
- کاربر واردشده
- عضو ویژه
- خزنده
اگر URL یکسان است، باید مراقب Cloaking و ناپایداری HTML بود.
Canonical URL احتمالاً همان URL عمومی است، اما:
- محتوای اصلی باید برای تمام کاربران معنای یکسانی داشته باشد؛
- Schema نباید اطلاعاتی را اعلام کند که در صفحه عمومی وجود ندارد؛
- Cache نباید نسخه شخصیسازیشده را به دیگران نمایش دهد؛
- Canonical نباید بر اساس وضعیت Login تغییر کند.
Canonical هویت منبع است و معمولاً نباید به Session وابسته باشد.
Canonical و Cache
Cache میتواند خطاهای Canonical را بهصورت گسترده منتشر کند.
برای مثال، اگر Cache Key فقط Path باشد اما Host یا Tenant را در نظر نگیرد:
tenant-a.example.com/products/125
tenant-b.example.com/products/125
ممکن است HTML Tenant اول برای Tenant دوم Serve شود و Canonical اشتباه داشته باشد.
یا اگر Query Parameterها در Cache Key نادیده گرفته شوند، نسخه متفاوتی از صفحه با Metadata نامناسب بازگردانده شود.
موارد مهم در طراحی Cache:
- Host
- Protocol
- Path
- Query Policy
- Locale
- Tenant
- Authentication State
- Preview Mode
- Device Variation در صورت وجود
- Bot Variation در صورت اجبار
بهتر است Canonical تا حد ممکن از داده پایدار و مستقل از Requestهای شخصی تولید شود.
Canonical در محیط Staging
یکی از خطاهای بسیار خطرناک این است که محیط Staging:
- قابل Crawl باشد؛
- Canonical به خودش تولید کند؛
- یا برعکس، محتوای آزمایشی آن Canonical دامنه Production داشته باشد.
Staging بهتر است با چند لایه محافظت شود:
- Authentication یا محدودیت شبکه؛
noindex;- جلوگیری از درج در Sitemap؛
- عدم انتشار لینک عمومی؛
- تنظیم Base URL محیط؛
- تست Configuration در Pipeline.
Canonical Production روی Staging بهتنهایی راهحل ایمنسازی Staging نیست.
موتور مرکزی Canonical
در سامانه بزرگ، بهتر است Canonical از یک Policy Engine مرکزی تولید شود.
نمونه مدل:
interface CanonicalContext {
pageType: PageType
entityId?: number
entityName?: string
routePath: string
query: Record<string, string | string[]>
pageNumber?: number
locale?: string
host?: string
isIndexable: boolean
}
خروجی:
interface CanonicalDecision {
canonicalUrl: string | null
redirectUrl: string | null
redirectCode?: 301 | 302 | 307 | 308
robots: 'index,follow' | 'noindex,follow' | 'noindex,nofollow'
reason: string
}
نمونه تصمیم:
function resolveProductCanonical(
context: ProductCanonicalContext
): CanonicalDecision {
const canonicalUrl = buildProductUrl(context.product)
if (!context.product.isActive) {
return {
canonicalUrl: null,
redirectUrl: null,
robots: 'noindex,follow',
reason: 'Product is inactive'
}
}
if (context.requestedPath !== canonicalUrl.pathname) {
return {
canonicalUrl: canonicalUrl.toString(),
redirectUrl: canonicalUrl.toString(),
redirectCode: 301,
robots: 'index,follow',
reason: 'Requested slug does not match canonical slug'
}
}
return {
canonicalUrl: canonicalUrl.toString(),
redirectUrl: null,
robots: 'index,follow',
reason: 'Canonical product URL'
}
}
وجود reason برای Debugging و Log بسیار مفید است.
Canonical نباید فقط یک رشته باشد
در طراحی ساده، Canonical فقط یک URL است.
در طراحی بهتر، Canonical نتیجه یک تصمیم مستند است:
{
"requestedUrl": "/products/125/wrong-slug?utm_source=email",
"entityId": 125,
"entityType": "product",
"canonicalUrl": "/products/125/industrial-pump",
"redirectRequired": true,
"redirectCode": 301,
"removedParameters": [
"utm_source"
],
"reason": "Slug mismatch and tracking parameter"
}
این مدل امکان موارد زیر را فراهم میکند:
- Log
- Debugging
- تست خودکار
- Monitoring
- تحلیل URLهای اشتباه
- شناسایی لینکهای داخلی ناسازگار
- گزارش Migration
روش پیشنهادی تصمیمگیری Canonical
یک جریان تصمیم عمومی میتواند به این صورت باشد:
1. آیا Route شناختهشده است؟
├── خیر → 404
└── بله
↓
2. آیا Entity وجود دارد؟
├── خیر → 404 یا 410
└── بله
↓
3. آیا Entity قابل انتشار است؟
├── خیر → Redirect / 404 / Noindex
└── بله
↓
4. URL رسمی Entity چیست؟
↓
5. آیا Host و Protocol صحیحاند؟
├── خیر → Redirect
└── بله
↓
6. آیا Path و Slug صحیحاند؟
├── خیر → Redirect
└── بله
↓
7. Query Parameterها چه نوعی هستند؟
├── Tracking → حذف از Canonical
├── Presentation → حذف از Canonical
├── Pagination → تصمیم مستقل
├── Indexable Filter → Self Canonical
├── Invalid → Block / Redirect / 404
└── Unknown → Policy
↓
8. Canonical Self-referencing تولید شود
تست Canonical
Canonical Architecture بدون تست قابل اعتماد نیست.
Unit Test
برای URL Builder:
describe('buildProductCanonicalUrl', () => {
it('creates a normalized product URL', () => {
const result = buildProductCanonicalUrl({
id: 125,
name: 'Industrial Pump'
})
expect(result).toBe(
'https://example.com/products/125/industrial-pump'
)
})
})
تست Unicode
it.each([
['نرم افزار', 'نرم-افزار'],
['نرمافزار', 'نرم-افزار'],
['كالا', 'کالا'],
['يادگيري', 'یادگیری']
])('normalizes Persian slug', (input, expected) => {
expect(toSlug(input)).toBe(expected)
})
Integration Test
Request:
/products/125/wrong-slug
Expected:
301
Location:
/products/125/industrial-pump
SSR Output Test
بررسی HTML اولیه:
<link
rel="canonical"
href="https://example.com/products/125/industrial-pump"
/>
Sitemap Consistency Test
تمام URLهای Sitemap باید:
- پاسخ 200 بدهند؛
- Redirect نشوند؛
noindexنباشند؛- Self-referencing Canonical داشته باشند.
Internal Link Test
لینکهای داخلی نباید به URLهایی اشاره کنند که Redirect میشوند.
تست خاص Query Parameter
برای هر Page Type باید جدول تصمیم وجود داشته باشد:
| URL | Canonical | Robots | Redirect |
|---|---|---|---|
/products/125/item | خودش | index | خیر |
/products/125/item?utm=x | URL بدون Query | index | اختیاری |
/products/125/wrong | URL صحیح | index | 301 |
/category/pumps?page=2 | خودش یا Policy مشخص | وابسته به سیاست | خیر |
/category/pumps?sort=price | دستهبندی اصلی | وابسته به سیاست | معمولاً خیر |
/category/pumps?invalid=x | بر اساس سیاست | noindex یا خطا | ممکن |
هدف جدول، تبدیل تصمیمهای مبهم به Contract قابل تست است.
Monitoring Canonical در Production
تست پیش از Deploy کافی نیست.
داده، Route و Configuration ممکن است پس از انتشار تغییر کنند.
سیستم Monitoring میتواند بهصورت دورهای مجموعهای از صفحات نمونه را بررسی کند:
- Status Code
- Redirect destination
- Canonical presence
- Canonical uniqueness
- Canonical target status
- Canonical target indexability
- Canonical chain
- Canonical loop
- Host
- Protocol
- Trailing Slash
- Sitemap consistency
- Robots
- Schema URL
نمونه هشدارها:
Canonical target returns 404
Canonical target redirects
Canonical points to another domain
Canonical missing
Multiple canonical tags found
Canonical differs between SSR and rendered DOM
Sitemap URL is noncanonical
Internal link points to redirected URL
تحلیل Log برای Canonical
Log سرور میتواند نشان دهد چه URLهای غیرCanonical هنوز Crawl یا استفاده میشوند.
برای مثال:
/products/125
/products/125/
/Products/125
/products/125/wrong-slug
اگر پس از اجرای Redirect هنوز حجم زیادی درخواست به نسخه قدیمی وجود دارد، منابع احتمالی عبارتاند از:
- لینک داخلی قدیمی
- Sitemap قدیمی
- لینک خارجی
- Cache
- اپلیکیشن موبایل
- Email template
- API response
- Schema قدیمی
- JavaScript bundle قدیمی
Canonical Monitoring فقط بررسی تگ نیست؛ باید مسیر ورود URLهای اشتباه نیز پیدا شود.
Canonical در CI/CD
در Pipeline میتوان Quality Gate تعریف کرد.
برای مثال، Deploy در شرایط زیر متوقف شود:
- URL Builder Test شکست خورده است؛
- بیش از یک Canonical در صفحه وجود دارد؛
- Canonical نسبی تولید شده است؛
- Host محیط اشتباه است؛
- Sitemap شامل URL Redirectشونده است؛
- Schema URL با Canonical تفاوت دارد؛
- صفحه Indexable فاقد Canonical است؛
- Slug Frontend و Backend یکسان نیستند.
به این ترتیب، Canonical از یک تنظیم دستی به بخشی از مهندسی نرمافزار تبدیل میشود.
خطاهای رایج Canonical
ساخت Canonical از URL فعلی
باعث حفظ Slug و Query اشتباه میشود.
Canonical کردن تمام صفحات به Home
این کار ارتباط معنایی صفحات را از بین میبرد.
Canonical کردن محصولات حذفشده به دستهبندی
دستهبندی لزوماً جایگزین همان محصول نیست.
قرار دادن URLهای Redirectشونده در Sitemap
Sitemap باید URL نهایی را معرفی کند.
استفاده از Canonical بهجای Redirect در مهاجرت کامل
وقتی URL قدیمی دیگر نباید استفاده شود، Redirect سیگنال عملیتری است.
تولید Canonical فقط در Client
HTML اولیه فاقد تصمیم Canonical میماند.
چند Canonical در یک صفحه
ممکن است Theme، Plugin و Application همزمان Canonical تولید کنند.
Canonical وابسته به User یا Session
Canonical باید هویت عمومی منبع را نشان دهد.
Canonical به URL خطادار
مقصد Canonical باید قابل دسترسی و مناسب Index باشد.
Canonical متفاوت با Sitemap و لینک داخلی
این وضعیت سیگنالهای متناقض ایجاد میکند.
ذخیره Canonical کامل در پایگاه داده
اگر Domain یا ساختار Route تغییر کند، هزاران رکورد باید Migrated شوند.
اغلب بهتر است داده پایدار Entity ذخیره و URL بر اساس Policy ساخته شود.
استفاده از Canonical برای پنهانکردن معماری بد URL
Canonical جایگزین اصلاح Routing، Redirect و لینکسازی نیست.
آیا Canonical باید در پایگاه داده ذخیره شود؟
در بیشتر سامانههای Entity-based، ذخیره URL کامل Canonical در پایگاه داده ضروری نیست.
بهتر است موارد پایدار ذخیره شوند:
- Entity ID
- Name
- Preferred Slug در صورت نیاز
- Locale
- Route type
- Historical Slugs در صورت نیاز
سپس URL از Policy ساخته شود.
مزایا:
- تغییر Domain سادهتر است؛
- مهاجرت Route کنترلپذیرتر است؛
- URLها از یک Rule مشترک پیروی میکنند؛
- داده تکراری کاهش مییابد.
اما گاهی Canonical دستی لازم است:
- محتوای Syndicated
- Cross-domain Canonical
- Legacy CMS
- صفحهای که منبع اصلی آن خارج از سامانه است
- Override تحریریه
در این حالت بهتر است Override صریح و محدود وجود داشته باشد:
canonicalOverride?: string
و اعتبارسنجی شود که:
- URL مطلق است؛
- Protocol مجاز دارد؛
- Domain مورد اعتماد است؛
- Loop ایجاد نمیکند؛
- مقصد قابل دسترسی است.
Versioning سیاست URL
URL Policy نیز بخشی از معماری نرمافزار است و ممکن است تغییر کند.
برای مثال:
Version 1:
/product/125
Version 2:
/products/125/industrial-pump
مهاجرت باید شامل موارد زیر باشد:
- Mapping URL قدیم به جدید
- Redirect دائمی
- اصلاح لینکهای داخلی
- Sitemap جدید
- Canonical جدید
- Schema URL جدید
- Breadcrumb جدید
- حفظ Redirectهای قدیمی
- Monitoring
- تحلیل Log
- جلوگیری از Chain
اشتباه رایج:
/product/125
↓
/products/125
↓
/products/125/industrial-pump
بهتر است URL قدیمی مستقیماً به مقصد نهایی Redirect شود:
/product/125
↓
/products/125/industrial-pump
چه مدت Redirectها حفظ شوند؟
Redirectهای مربوط به URLهای عمومی و دارای سابقه بهتر است بخشی از دارایی پایدار سایت در نظر گرفته شوند، نه تنظیماتی موقت که پس از چند هفته حذف شوند.
URL قدیمی ممکن است همچنان در این مکانها وجود داشته باشد:
- Backlink
- Bookmark
- ایمیل
- فایل PDF
- شبکه اجتماعی
- نرمافزار موبایل
- Cache مرورگر
- مستندات قدیمی
حذف زودهنگام Redirect باعث تبدیل ورودی معتبر قبلی به 404 میشود.
سیاست نگهداری Redirect باید بر اساس ارزش URL، هزینه نگهداری، Log و برنامه مهاجرت تعیین شود.
طراحی پیشنهادی Canonical Architecture
یک معماری قابل نگهداری میتواند اجزای زیر را داشته باشد:
┌────────────────────────────┐
│ Incoming HTTP Request │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ Route Parser │
│ ID / Slug / Query / Locale │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ Entity Resolver │
│ Stable Domain Identity │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ URL Policy Engine │
│ Host / Path / Slug / Query │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ Canonical Decision │
│ Redirect / Canonical / 404 │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ SSR SEO Orchestrator │
│ Head / Robots / Schema │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ Shared URL Consumers │
│ Sitemap / Links / Graph │
└──────────────┬─────────────┘
↓
┌────────────────────────────┐
│ Tests & Monitoring │
└────────────────────────────┘
مسئولیت هر لایه
Domain Layer
- هویت موجودیت
- وضعیت فعال یا حذفشده
- روابط کسبوکار
- نام رسمی
- Locale
- جایگزین احتمالی
URL Policy
- Path
- Slug
- Host
- Protocol
- Query
- Trailing Slash
- Case
- نسخه Route
Routing Layer
- Parse درخواست
- Resolve Entity
- تشخیص URL ناسازگار
- Redirect
- 404 یا 410
SEO Orchestrator
- Canonical tag
- Robots
- Open Graph URL
- Schema URL
- Metadata
Sitemap Generator
- فقط URLهای Canonical
- فقط صفحات Indexable
- حذف Redirect و خطا
Internal Link Builder
- لینک مستقیم به URL نهایی
- عدم ایجاد Redirect داخلی
Monitoring
- کشف خطا و Regression
- تحلیل Targetها
- هشدار
چکلیست طراحی Canonical
پیش از انتشار هر Page Type باید به این پرسشها پاسخ داده شود:
هویت
- Entity اصلی صفحه چیست؟
- شناسه پایدار آن چیست؟
- آیا چند Entity در صفحه وجود دارد؟
mainEntityکدام است؟
URL
- URL رسمی چگونه تولید میشود؟
- ID در URL وجود دارد؟
- Slug چه نقشی دارد؟
- تغییر نام چه اثری دارد؟
- تغییر دستهبندی چه اثری دارد؟
- Case Policy چیست؟
- Trailing Slash Policy چیست؟
Request
- Slug اشتباه چه رفتاری دارد؟
- Query ناشناخته چه رفتاری دارد؟
- Host اشتباه چه رفتاری دارد؟
- HTTP چه رفتاری دارد؟
- URL قدیمی چه رفتاری دارد؟
Canonical
- Self-referencing است؟
- URL مطلق است؟
- مقصد پاسخ 200 دارد؟
- مقصد Indexable است؟
- Chain یا Loop ندارد؟
- در SSR تولید میشود؟
سایر سیگنالها
- Sitemap همان URL را دارد؟
- لینکهای داخلی همان URL را دارند؟
- Breadcrumb همان URL را دارد؟
- Schema همان URL را دارد؟
- Open Graph همان URL را دارد؟
عملیات
- Unit Test وجود دارد؟
- Integration Test وجود دارد؟
- Monitoring وجود دارد؟
- Redirectها Log میشوند؟
- Migration Plan وجود دارد؟
یک نمونه سیاست کامل برای صفحه محصول
فرض کنیم URL رسمی محصول چنین است:
https://example.com/products/{id}/{slug}
قواعد:
idهویت اصلی Entity است.slugاز نام Normalizeشده محصول ساخته میشود.- Path همیشه Lowercase است.
- Trailing Slash استفاده نمیشود.
- HTTPS اجباری است.
- Host اصلی
example.comاست. - Tracking Parameterها از Canonical حذف میشوند.
- Slug اشتباه با 301 اصلاح میشود.
- URL بدون Slug با 301 به URL کامل منتقل میشود.
- محصول حذفشده 404 یا 410 دریافت میکند.
- محصول دارای جایگزین واقعی با 301 منتقل میشود.
- Sitemap فقط URL کامل را ثبت میکند.
- لینکهای داخلی مستقیماً به URL کامل اشاره میکنند.
- Canonical در SSR تولید میشود.
- Schema و Open Graph از همان URL استفاده میکنند.
نمونهها:
HTTP:
http://example.com/products/125/industrial-pump
→ 301 HTTPS
Host:
https://www.example.com/products/125/industrial-pump
→ 301 Main Host
Missing Slug:
https://example.com/products/125
→ 301 Full URL
Wrong Slug:
https://example.com/products/125/wrong
→ 301 Correct URL
Tracking:
https://example.com/products/125/industrial-pump?utm_source=email
→ 200
Canonical بدون Query
Canonical:
https://example.com/products/125/industrial-pump
→ 200 Self-referencing
این همان تفاوت میان «داشتن Canonical» و «داشتن Canonical Architecture» است.
جمعبندی
Canonical فقط یک تگ در بخش <head> صفحه نیست.
این تگ، خروجی نهایی مجموعهای از تصمیمهای عمیقتر است:
Domain Identity
↓
URL Policy
↓
Route Resolution
↓
Slug Normalization
↓
Redirect Decision
↓
Canonical Generation
↓
Sitemap
↓
Internal Linking
↓
Schema and Metadata
↓
Testing and Monitoring
اگر هرکدام از این لایهها URL متفاوتی را بهعنوان نسخه اصلی معرفی کنند، سایت سیگنالهای متناقض ارسال خواهد کرد.
یک معماری Canonical حرفهای باید بتواند بهصورت شفاف پاسخ دهد:
- این صفحه نماینده کدام Entity است؟
- URL پایدار و رسمی آن چیست؟
- URLهای اشتباه یا قدیمی چگونه مدیریت میشوند؟
- Query Parameterها چه معنایی دارند؟
- چه زمانی Redirect و چه زمانی Canonical استفاده میشود؟
- Sitemap و لینکهای داخلی به کدام URL اشاره میکنند؟
- چگونه این تصمیمها در SSR، Cache و Microserviceها پایدار میمانند؟
- و چگونه از بازگشت خطاها در Deployهای بعدی جلوگیری میشود؟
مهمترین اصل این است:
سایت نباید فقط از طریق یک تگ، URL ترجیحی خود را اعلام کند؛ تمام معماری سایت باید همان URL را بهعنوان نسخه اصلی بشناسد و استفاده کند.
در مقاله بعدی این مجموعه، به طراحی یک موتور متمرکز Metadata خواهیم پرداخت و بررسی خواهیم کرد که چگونه Title، Description، Canonical، Robots، Open Graph و Structured Data را میتوان بر اساس Page Type، Entity، Route Context و سیاستهای کسبوکار، بهصورت یکپارچه و قابل تست تولید کرد.

