# تأیید زندهٔ فیکس‌های SEO بعد از deploy ## پروژه `nobat724_front` ## زمینه فیکس‌های ممیزی SEO (P1–P11 از `seo-critical-fixes-live-audit.md`) پیاده و تست شده‌اند، اما تأیید روی **build محلی production** انجام شد، نه روی دامنه‌های واقعی: ``` NEXT_PUBLIC_API_URL=https://clinic-pro.ir DEV_MODE=FALSE npm run build NODE_TLS_REJECT_UNAUTHORIZED=0 npx next start -p 3111 # سپس curl با هدر Host برای شبیه‌سازی دامنه‌های شهری ``` معیارهای پذیرش اصلی «curl روی production» بودند. این پرامپت همان‌ها را روی دامنه‌های واقعی اجرا می‌کند. **دو نکتهٔ حیاتی قبل از تفسیر هر خروجی:** ۱. **`DEV_MODE` باید `FALSE` باشد.** با `DEV_MODE=TRUE` کل سایت `noindex, nofollow, nocache` می‌گیرد و همهٔ تست‌های robots بی‌معنی می‌شوند. این مقدار در `next.config.js` از طریق `env` در **زمان build** درون کد جاسازی می‌شود — تغییر آن در runtime بی‌اثر است و نیاز به build مجدد دارد. ۲. **تغییرات هنوز commit نشده‌اند** (وضعیت کاری). قبل از deploy باید commit و push شوند. ## هدف اجرای کامل معیارهای پذیرش روی production و ثبت خروجی واقعی؛ و در صورت اختلاف با نتایج محلی، ریشه‌یابی. ## وظایف ### ۱. پیش از deploy - `git status` را بررسی کن؛ تغییرات SEO را commit کن. - تأیید کن build با `DEV_MODE=FALSE` انجام می‌شود. - تأیید کن `NEXT_PUBLIC_API_URL` در محیط production درست است (نه `clinic-pro.ddev.site` که مقدار فعلی `.env` محلی است). ### ۲. اجرای معیارهای پذیرش روی production بعد از deploy، این‌ها را اجرا کن و **خروجی واقعی هر کدام را در گزارش نقل کن**: ```bash # P3 — noindex سیستمیک لیست‌ها (باید خالی باشد) for h in yasuj-nobat.ir tehran-nobat.ir yazd-nobat.ir nobat724.com; do for p in doctors clinics; do printf "%s/%s: " "$h" "$p" curl -s "https://$h/$p" | grep -o ']*>' | head -1; echo done done # P3 — فیلتر واقعی باید noindex بماند curl -s "https://yasuj-nobat.ir/doctors?name=x" | grep -o 'content="noindex[^"]*"' curl -s "https://yasuj-nobat.ir/doctors?specialty=پوست%20و%20مو&gender=woman" | grep -o 'content="noindex[^"]*"' # P4 — C1-a self-canonical for h in yazd-nobat.ir nobat724.com yasuj-nobat.ir; do for p in "" doctors clinics specialties blogs; do printf "%s/%s -> " "$h" "$p" curl -s "https://$h/$p" | grep -o 'rel="canonical" href="[^"]*"'; echo done done # P4 — C1-c استاتیک مشترک → دامنهٔ اصلی curl -s https://yasuj-nobat.ir/about-us | grep -o 'rel="canonical" href="[^"]*"' curl -s https://yasuj-nobat.ir/contact-us | grep -o 'rel="canonical" href="[^"]*"' # P4 — C1-b موجودیت → دامنهٔ شهر خودش D=beaca548-816f-4613-937d-360db01bd7c8 # شهر: یاسوج C=9c163d69-0051-4745-a423-c830135b1c01 # شهر: یاسوج curl -s "https://nobat724.com/doctor/$D" | grep -o 'rel="canonical" href="[^"]*"' # → yasuj-nobat.ir curl -s "https://yazd-nobat.ir/doctor/$D" | grep -o 'rel="canonical" href="[^"]*"' # → yasuj-nobat.ir curl -s "https://nobat724.com/clinic/$C" | grep -o 'rel="canonical" href="[^"]*"' # → yasuj-nobat.ir # P4 — هماهنگی JSON-LD با canonical (تناقض زندهٔ قبلی) curl -s "https://nobat724.com/clinic/$C" | grep -o '"url":"[^"]*"' # M3-B — pagination curl -s "https://yasuj-nobat.ir/doctors?page=2" | grep -o 'rel="canonical" href="[^"]*"' # → ?page=2 curl -s "https://yasuj-nobat.ir/doctors?page=1" | grep -o 'rel="canonical" href="[^"]*"' # → بدون page # P6 — Soft-404 (هر چهار باید 404 بدهند) for p in doctor/invalid-xxx clinic/invalid-xxx blog/invalid-xxx specialties/invalid-slug; do printf "%s -> " "$p"; curl -o /dev/null -s -w "%{http_code}\n" "https://nobat724.com/$p" done # و موجودیت معتبر باید 200 بماند curl -o /dev/null -s -w "valid doctor: %{http_code}\n" "https://nobat724.com/doctor/$D" # P7 — دقیقاً یک h1 و شهرمحور for p in doctors clinics specialties; do printf "%s h1count=" "$p"; curl -s "https://yasuj-nobat.ir/$p" | grep -c "]*>[^<]*" done curl -s "https://yasuj-nobat.ir/clinic/$C" | grep -c "[^<]*" # → بدون «نوبت 724» به‌جای شهر # P8 — breadcrumb curl -s https://yasuj-nobat.ir/clinics | grep -c 'aria-label="breadcrumb"' curl -s https://yasuj-nobat.ir/clinics | grep -c 'BreadcrumbList' curl -s "https://yasuj-nobat.ir/clinic/$C" | grep -c 'aria-label="breadcrumb"' # P9 — صفحات فرود تخصص curl -o /dev/null -s -w "%{http_code}\n" https://tehran-nobat.ir/specialties/dermatology # → 200 curl -o /dev/null -s -w "%{http_code}\n" https://tehran-nobat.ir/specialties/invalid-slug # → 404 curl -s "https://tehran-nobat.ir/doctors?specialty=پوست%20و%20مو" | grep -o 'rel="canonical" href="[^"]*"' curl -s https://yasuj-nobat.ir/specialties | grep -c "doctors?specialty=" # → 0 # P5 — sitemap curl -s https://nobat724.com/sitemap.xml | grep -c "" curl -s https://yasuj-nobat.ir/sitemap.xml | grep -c "" curl -s https://yasuj-nobat.ir/sitemap.xml | grep -c "$D" # → 1 curl -s https://nobat724.com/sitemap.xml | grep -c "$D" # → 0 curl -s https://yasuj-nobat.ir/sitemap.xml | grep -c "9c163d69" # → 0 (کلینیک بی‌محتوا) # P11 — FAQPage بدون شکستن اسکیماهای موجود curl -s https://tehran-nobat.ir/specialties/dermatology | grep -c FAQPage curl -s "https://yasuj-nobat.ir/doctor/$D" | grep -c FAQPage curl -s "https://yasuj-nobat.ir/doctor/$D" | grep -c Physician # باید سالم بماند ``` ### ۳. اعتبارسنجی ساختاریافته - **Rich Results Test گوگل** روی یک صفحهٔ پزشک و یک صفحهٔ تخصص: تأیید کن `Physician` / `MedicalClinic` / `Review` / `BreadcrumbList` قبلی سالم‌اند و `FAQPage` معتبر است. - تأیید کن همهٔ بلوک‌های JSON-LD پارس می‌شوند (در تست محلی: ۰ خطای پارس، انواع `Organization, WebSite, Physician, FAQPage, BreadcrumbList`). ### ۴. بازبینی بصری صفحات تغییریافته را با نسخهٔ قبل مقایسه کن. تغییر تگ‌ها باید بدون اثر بصری باشد: | صفحه | تغییر | انتظار | |------|-------|--------| | `/` | خط برند `h1` → `p`؛ شعار `h2` → `h1` | ظاهر یکسان | | `/doctors` | متن دکمهٔ موقعیت `h1` → `span` + h1 جدید و پاراگراف مقدمه | دکمه بدون تغییر؛ عنوان و متن جدید بالای لیست | | `/clinics` | عنوان پویا + پاراگراف مقدمه + breadcrumb با «خانه» | «خانه >» به breadcrumb اضافه شده | | `/clinic/[id]` | ۳ عنوان بخش `h1` → `h2`؛ breadcrumb بصری جدید | تایپوگرافی یکسان | ### ۵. Search Console برای هر دامنهٔ درگیر: URL Inspection روی صفحات کلیدی (`/`، `/doctors`، `/clinics`، یک صفحهٔ پزشک، یک صفحهٔ تخصص) و ثبت sitemap. re-index چند هفته طول می‌کشد. ## نکات مهم - **رگرسیون احتمالی که باید مشخصاً چک شود:** حذف `loading.js` از سه مسیر `doctor/[slug]`، `clinic/[slug]`، `blog/[slug]`. این کار برای رفع Soft-404 لازم بود (Suspense باعث می‌شد پاسخ با ۲۰۰ استریم شود و `notFound()` دیگر نتواند status را عوض کند). هزینه‌اش نبودِ اسکلت لودینگ هنگام ناوبری کلاینتی است — تجربهٔ کاربری این سه مسیر را عملاً بررسی کن. - اگر `robots` روی همهٔ صفحات `noindex, nofollow, nocache` بود، تقریباً قطعاً `DEV_MODE=TRUE` در build بوده — کد را دنبال نکن، اول build را بررسی کن. - ArvanCloud ممکن است روی دامنهٔ اصلی bot-challenge داشته باشد؛ اگر curl خروجی غیرمنتظره داد، قبل از دیباگ کد این را رد کن. (خارج از دامنهٔ اپ — DevOps) - sitemap دامنهٔ اصلی ~۱۳ ثانیه طول می‌کشد (۳۵ sweep برای تفکیک شهر). اگر timeout خورد، این انتظار است نه باگ — رفعش در `sitemap-simplify-with-city.md`.