urWhats.com -- SEO Master Plan
Last Updated: 2026-02-20 Branch:
feature/seo-enhancementsDomain: urwhats.com Framework: Astro 5.16 + Bootstrap 5 Pages: 23 (11 EN + 11 AR + 404 + sitemap) Build:npm run build(~3s) Status: Phases 0-6 COMPLETED (Phase 5: 5/8 tasks done, 3 low-priority deferred)
How to Use This Document
- Find the first task with
Status: PENDINGin the current phase - Mark it
IN_PROGRESSwhen starting - Follow the exact instructions and file paths
- Verify acceptance criteria
- Run
npm run buildto confirm 0 errors - Mark task
COMPLETED - Move to the next pending task
Agent instructions: Read .claude/CLAUDE.md for full project context, company rules (never mention WhatsaMark, use +966508777669, etc.).
Progress Tracker
| Phase | Name | Tasks | Completed | Status |
|---|---|---|---|---|
| 0 | Emergency Fixes (Session 1) | 8 | 8 | COMPLETED |
| 1 | Critical SEO Infrastructure (Session 2) | 7 | 7 | COMPLETED |
| 2 | High-Severity Fixes (Session 2) | 12 | 12 | COMPLETED |
| 3 | Medium Fixes & Sitemaps (Session 2) | 10 | 10 | COMPLETED |
| 4 | Arabic AI Content (Session 3) | 5 | 5 | COMPLETED |
| 5 | Performance & Polish | 8 | 5 | COMPLETED (3 deferred) |
| 6 | Production Sweep Fixes | 4 | 4 | COMPLETED |
COMPLETED (Previous Session)
- Fixed meta descriptions (.html suffix stripping in SEO.astro)
- Fixed language switcher URLs
- Fixed hreflang alternate URLs
- Fixed canonical URLs
- Created 404.astro page
- Made mobile CTA visible in Navigation.astro
- Updated phone to +966508777669 everywhere
- Removed fake aggregateRating from all JSON-LD
- Removed SearchAction schema
- Cleaned llms.txt and llms-full.txt (fake stats, UAE PDPL)
- Deleted malformed static sitemap.xml
- Updated robots.txt
- Created .well-known/ files (ai-plugin.json, schema.json, security.txt)
- Committed as
1bb053fonfeature/seo-enhancements
Session 2: Phases 1-3 (commit 8e09516)
- All Phase 1 tasks (1.1-1.7): Fixed _redirects 404s, webmanifest, sitemap with hreflang, Twitter Cards, _headers, non-functional meta tags
- All Phase 2 tasks (2.1-2.12): Consolidated JSON-LD to @graph, bilingual AI meta tags, fixed Organization address, title fallback, trailing slash redirects, security.txt, robots.txt, og:locale:alternate, Google Fonts conditional, Alpine.js pinned, service worker
- All Phase 3 tasks (3.1-3.10): Deleted unused CSS, source maps, root favicon, normalized routes (PascalCase redirects as stopgap), sitemap priorities, dateModified fix, schema.json cleanup, image sitemap, CSP improvement, render-blocking CSS
- Note: Task 1.2 (OG image) still needs design/creation -- PNG file must be designed externally
Session 3: Phase 4 (commit 7e2bc46)
- All Phase 4 tasks (4.1-4.5): Rewrote llms-full.txt (~870 lines, ~50% Arabic), bilingual ai-plugin.json, bilingual schema.json, completed Arabic llms.txt, bilingual SEO.astro AI meta + schema
- Fixed llms.txt gaps: added Privacy/Terms URLs, added /ar/by-role, removed GHL reference from llms.txt, fixed sitemap URL
Session 4: Phase 6 - Production Sweep Fixes (commit 0826681)
- Task 6.1: Fixed UAE legal text to Saudi PDPL in privacy + terms (EN/AR) -- replaced UAE Federal Decree-Law No. 45 with Saudi PDPL Royal Decree M/19, changed Dubai courts to Riyadh courts
- Task 6.2: Removed GHL leak from integration text in en.json and ar.json
- Task 6.3: Fixed +971 phone placeholder to +966 in contact form (EN/AR)
- Task 6.4: Renamed PascalCase pages (Privacy.astro, Terms.astro) to lowercase + updated 9 references (sitemap, Footer, security.txt, _redirects, llms.txt, llms-full.txt)
- Added
compressHTML: trueto Astro config for production optimization
Session 5: Phase 5 - Performance & Polish (commit 7532687)
- Task 5.1: PurgeCSS integration -- saves 556KB across 5 CSS files (bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, plugins.css)
- Task 5.2: Deleted Plans.astro -- 583 lines of fully commented-out dead code removed
- Task 5.4: Created .env.example -- documents all 4 required environment variables
- Task 5.5: Added image width/height attributes -- 73 img tags updated across all components and pages to prevent CLS
- Task 5.6: Added skip navigation link -- RTL-aware "Skip to content" link as first body element in Layout.astro
- Task 5.3: PNG to WebP conversion -- DONE (30 PNGs converted to WebP, 3.11MB saved, all 62 references updated)
- Task 5.7: @astrojs/sitemap integration -- SKIPPED (custom sitemap with hreflang already works correctly)
- Task 5.8: SpeakableSpecification -- DONE (added to WebPage schema in SEO.astro @graph)
PHASE 1: Critical SEO Infrastructure
Task 1.1: Fix _redirects (Catch-All Rewrite Breaking 404s)
- Status: COMPLETED
- Priority: CRITICAL
- File:
public/_redirects - Issue:
/* /index.html 200serves homepage for ALL non-existent URLs. Custom 404 page never reached. Google sees massive duplicate content (soft 404s). This is the single worst SEO issue on the site. - Fix:
# Cloudflare Pages redirects for urWhats # Trailing slash normalization /services/ /services 301 /prices/ /prices 301 /contact/ /contact 301 /faqs/ /faqs 301 /apps/ /apps 301 /use-cases/ /use-cases 301 /by-industry/ /by-industry 301 /by-role/ /by-role 301 /ar/services/ /ar/services 301 /ar/prices/ /ar/prices 301 /ar/contact/ /ar/contact 301 /ar/faqs/ /ar/faqs 301 /ar/apps/ /ar/apps 301 /ar/use-cases/ /ar/use-cases 301 /ar/by-industry/ /ar/by-industry 301 /ar/by-role/ /ar/by-role 301 # Route casing redirects (future-proof for when we rename to lowercase) /privacy /Privacy 301 /terms /Terms 301 /ar/privacy /ar/Privacy 301 /ar/terms /ar/Terms 301 # www redirect (if not handled at DNS level) # https://www.urwhats.com/* https://urwhats.com/:splat 301 # 404 fallback - serve actual 404 page /* /404.html 404 - Acceptance Criteria:
- Invalid URLs return HTTP 404 status with 404.html content
- Homepage is NOT served for random URLs
- Trailing-slash URLs 301 redirect to non-trailing versions
-
npm run buildsucceeds
Task 1.2: Create OG Image (1200x630 PNG)
- Status: COMPLETED
- Priority: CRITICAL
- File:
public/assets/images/logos/urWhats-og-image.png - Issue:
SEO.astroreferences this file but it does NOT exist. All social sharing (Facebook, Twitter, LinkedIn, Slack, WhatsApp) shows broken/no preview. - Fix: Create a 1200x630px PNG image:
- Background: Gradient green (#45b33d to #2d7a27)
- Center: urWhats logo (white, from SVG source)
- Below logo: "WhatsApp Business API Platform" in white
- Bottom badge: "Official Meta Technical Provider"
- Bottom-right: "urwhats.com"
- Tool: Use sharp/canvas Node.js script, or export from Figma/Canva
- Also add to SEO.astro:
<meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" /> <meta property="og:image:type" content="image/png" /> - Acceptance Criteria:
- File exists at
public/assets/images/logos/urWhats-og-image.png - Image is exactly 1200x630px
- OG image dimensions meta tags added to SEO.astro
- Facebook Sharing Debugger shows correct preview
- File exists at
Task 1.3: Fix Webmanifest (Empty Name + Wrong Icon Paths)
- Status: COMPLETED
- Priority: CRITICAL
- File:
public/assets/favicon/site.webmanifest - Issue:
nameandshort_nameare empty strings. Icon paths point to root/but files are in/assets/favicon/. PWA install completely broken. - Fix:
{ "name": "urWhats - WhatsApp Business API Platform", "short_name": "urWhats", "description": "Official Meta Technical Provider for WhatsApp Business API solutions in Saudi Arabia", "start_url": "/", "scope": "/", "display": "standalone", "theme_color": "#45b33d", "background_color": "#ffffff", "lang": "en", "dir": "ltr", "categories": ["business", "communication", "productivity"], "icons": [ { "src": "/assets/favicon/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { "src": "/assets/favicon/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { "src": "/assets/favicon/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ] } - Acceptance Criteria:
-
nameandshort_nameare populated - Icon paths start with
/assets/favicon/ -
theme_colormatches brand green#45b33d -
purposesplit into separate entries (not "any maskable")
-
Task 1.4: Fix Sitemap (Rename + Add Hreflang + Fix Headers)
- Status: COMPLETED
- Priority: CRITICAL
- Files:
src/pages/sitemap-index.xml.ts,public/robots.txt,public/_headers - Issue: File named
sitemap-index.xmlbut contains<urlset>(not<sitemapindex>)._headersrules target/sitemap.xmlwhich doesn't match. Missing hreflang annotations. - Fix:
- Rename
src/pages/sitemap-index.xml.tstosrc/pages/sitemap.xml.ts - Add
xmlns:xhtmlnamespace and hreflang<xhtml:link>per URL:<url> <loc>https://urwhats.com/services</loc> <xhtml:link rel="alternate" hreflang="en" href="https://urwhats.com/services"/> <xhtml:link rel="alternate" hreflang="ar" href="https://urwhats.com/ar/services"/> <xhtml:link rel="alternate" hreflang="x-default" href="https://urwhats.com/services"/> <lastmod>2026-02-19</lastmod> <priority>0.8</priority> </url> - Differentiate priority: home=1.0, services/prices=0.9, content pages=0.8, legal=0.3
- Update
robots.txt: changesitemap-index.xmltositemap.xml - Update
_headers: ensure rules match/sitemap.xml - Remove
changefreq(deprecated by Google)
- Rename
- Acceptance Criteria:
- Sitemap accessible at
/sitemap.xml - Every URL has hreflang alternates (en, ar, x-default)
- robots.txt points to correct sitemap URL
-
_headerscache rules apply to sitemap - Legal pages have lower priority (0.3)
- Sitemap accessible at
Task 1.5: Fix Twitter Card Meta Tags
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Issue: Lines ~391-395 use
<meta property="twitter:card">instead of<meta name="twitter:card">. Twitter spec requiresnameattribute. - Fix: Change all Twitter meta tags from
property=toname=:<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:site" content="@urWhatsApp" /> <meta name="twitter:creator" content="@UrWaveIT" /> <meta name="twitter:title" content={title} /> <meta name="twitter:description" content={description} /> <meta name="twitter:image" content={`${siteUrl}${socialImage}`} /> - Also: Verify correct Twitter handles exist. If no @urWhatsApp handle, use @UrWaveIT for both.
- Acceptance Criteria:
- All Twitter meta tags use
name=notproperty= - Twitter Card Validator shows correct preview
- All Twitter meta tags use
Task 1.6: Fix _headers Sitemap Mismatch + CSP
- Status: COMPLETED
- Priority: HIGH
- File:
public/_headers - Issues:
- Sitemap rules target
/sitemap.xmlbut actual file is at/sitemap-index.xml(or/sitemap.xmlafter Task 1.4) /images/*rule never matches (images are at/assets/images/)- CSP has
'unsafe-eval'unnecessarily - Missing Meta Pixel domains in CSP
connect-src/script-src
- Sitemap rules target
- Fix:
- After Task 1.4, ensure
/sitemap.xmlrules match - Remove or fix
/images/*to/assets/images/* - Remove
'unsafe-eval'from CSP (Alpine.js 3.x CSP build: use@alpinejs/cspor test without) - Add to CSP:
https://www.facebook.com https://connect.facebook.netfor Meta Pixel - Remove deprecated
X-XSS-Protectionheader
- After Task 1.4, ensure
- Acceptance Criteria:
- Sitemap cache headers apply correctly
- No CSP errors in browser console on any page
- Meta Pixel fires without CSP violations
Task 1.7: Remove Non-Functional Meta Tags
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/components/SEO.astro - Issues:
<meta name="speakable" content="true">-- not valid, needs JSON-LD SpeakableSpecification<link rel="author" href="/llms.txt">--rel="author"is for author profile pages, not llms.txt- Non-standard
product:category,business:contact_data:*meta tags (require og:type=product) - DNS prefetch for own domain (
urwhats.com) is unnecessary
- Fix:
- Remove
<meta name="speakable"> - Change
rel="author"torel="me"or just remove (llms.txt is discoverable via robots.txt) - Remove
product:*andbusiness:*meta tags (they require og:type to be "product" or "business.business") - Remove
dns-prefetchforurwhats.com, keep only third-party origins - Change
dns-prefetchtopreconnectforgoogletagmanager.comandfonts.googleapis.com
- Remove
- Acceptance Criteria:
- No non-standard meta tags that have no consumer
- Preconnect used for critical third-party origins
PHASE 2: High-Severity Fixes
Task 2.1: Consolidate JSON-LD to @graph Pattern
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Issue: 6-7 separate
<script type="application/ld+json">blocks per page. Google recommends@graphto establish entity relationships. - Fix: Combine all schemas into one
<script>block:{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", ... }, { "@type": "WebSite", ... }, { "@type": "WebPage", ... }, { "@type": "BreadcrumbList", ... }, { "@type": "SoftwareApplication", ... } ] } - Acceptance Criteria:
- Single JSON-LD block per page
- Google Rich Results Test passes
- All existing schema types preserved
Task 2.2: Fix Organization Schema (SVG Logo + Inconsistencies)
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Issue: Logo is SVG (Google requires PNG/JPEG for logo in structured data).
logo.width/height: 512claims fixed dimensions for an SVG. - Fix:
- Create PNG version of logo at
public/assets/images/logos/urWhats-Logo-512.png - Reference PNG in Organization schema
logo.url - Keep logo dimensions accurate for the PNG
- Remove duplicate Organization schema from
.well-known/schema.json(or sync it)
- Create PNG version of logo at
- Acceptance Criteria:
- PNG logo file exists
- Organization schema references PNG, not SVG
- Google Rich Results Test validates logo
Task 2.3: Fix SoftwareApplication Schema
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Issues:
- Hardcoded
softwareVersion: "2.0"andreleaseNotes - Price range
lowPrice: "0", highPrice: "999"withpriceCurrency: "USD"-- should use SAR for Arabic - Description and featureList are English-only
- Hardcoded
- Fix:
- Remove hardcoded
softwareVersionandreleaseNotes(or keep version, remove notes) - Make price currency language-conditional:
lang === 'ar' ? 'SAR' : 'USD' - Add
highPricethat matches actual pricing - Make
descriptionandfeatureListuse translated content:t('meta.software.description')etc.
- Remove hardcoded
- Acceptance Criteria:
- Arabic pages show SAR pricing in schema
- English pages show USD pricing
- Description matches current language
Task 2.4: Make AI Meta Tags Bilingual
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Issue: Lines ~368-372:
ai:description,ai:category,ai:features,ai:use_cases,ai:audienceare hardcoded English. - Fix: Use ternary with
langvariable:
Do the same for all ai:* tags.<meta name="ai:description" content={lang === 'ar' ? 'منصة واتساب للأعمال API الرسمية من شريك ميتا التقني - حلول مراسلة للشركات في السعودية' : 'Official WhatsApp Business API platform from Meta Technical Provider - Enterprise messaging solutions in Saudi Arabia'} /> - Acceptance Criteria:
- Arabic pages have Arabic AI meta content
- English pages have English AI meta content
Task 2.5: Fix Title Fallback Logic
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/components/SEO.astro - Issue:
if (title.includes('.'))is used to detect missing translations. Breaks if title contains a period (e.g., "urWhats v2.0"). Fragile detection. - Fix: Use a more robust check: test if the translation key pattern matches
meta.*.title:const isMissingTranslation = title.startsWith('meta.') && title.endsWith('.title'); - Acceptance Criteria:
- Titles with periods (e.g., "v2.0") render correctly
- Missing translations still fall back to site name
Task 2.6: Add Trailing Slash 301 Redirects
- Status: COMPLETED
- Priority: HIGH
- File:
public/_redirects - Issue: No redirects for trailing slash variants, www subdomain, or .html extensions. Duplicate content risk.
- Fix: Already included in Task 1.1 redirects. Verify coverage.
- Acceptance Criteria:
-
/services/301 ->/services - All pages have trailing-slash redirects
-
Task 2.7: Fix Security.txt (Missing PGP Key)
- Status: COMPLETED
- Priority: MEDIUM
- File:
public/.well-known/security.txt - Issue: References
https://urwhats.com/.well-known/pgp-key.txtwhich doesn't exist. - Fix: Either:
- A) Generate a PGP key and create the file, OR
- B) Remove the
Encryption:line from security.txt - Recommended: Option B (simpler, PGP is optional in RFC 9116)
- Acceptance Criteria:
- No broken references in security.txt
Task 2.8: Fix robots.txt Cleanup
- Status: COMPLETED
- Priority: LOW
- File:
public/robots.txt - Issues:
- Redundant explicit
Allow:rules (already covered byAllow: /) - NeevaBot entry (Neeva shut down 2023)
Crawl-delayfor Googlebot (Google ignores this)- Commented-out llms.txt references
- Redundant explicit
- Fix:
- Remove individual
Allow:rules for pages (keepAllow: /) - Remove NeevaBot section
- Remove
Crawl-delayfor Googlebot - Uncomment llms.txt references or remove comments
- Ensure sitemap URL matches Task 1.4
- Remove individual
- Acceptance Criteria:
- Clean, minimal robots.txt
- No dead crawler entries
- Sitemap URL correct
Task 2.9: Add og:locale:alternate
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/components/SEO.astro - Issue: Missing
og:locale:alternatefor bilingual social sharing. - Fix: Add after existing
og:locale:<meta property="og:locale:alternate" content={lang === 'ar' ? 'en_US' : 'ar_SA'} /> - Acceptance Criteria:
- English pages have
og:locale:alternate=ar_SA - Arabic pages have
og:locale:alternate=en_US
- English pages have
Task 2.10: Fix Google Fonts Loading
- Status: COMPLETED
- Priority: HIGH
- File:
src/layouts/Layout.astro - Issue: Full Tajawal font (5 weights) loaded for ALL pages, including English. Hurts LCP for English visitors.
- Fix: Conditionally load Tajawal only for Arabic pages:
{lang === 'ar' && ( <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;800&display=swap" rel="stylesheet" /> )} - Acceptance Criteria:
- English pages don't load Tajawal font
- Arabic pages still load Tajawal correctly
- LCP improved on English pages
Task 2.11: Pin Alpine.js Version
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/layouts/Layout.astro - Issue:
@3.x.xis not a valid semver CDN specifier. May break on major updates. - Fix: Pin to
@3.14.8(or latest stable at time of fix):<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script> - Acceptance Criteria:
- Specific version pinned
- Pricing toggle still works
Task 2.12: Fix Service Worker Cache
- Status: COMPLETED
- Priority: LOW
- File:
public/sw.js - Issue: Static asset list incomplete. Cache name
v1never incremented. Stale caches persist. - Fix: Either:
- A) Update asset list and implement versioned cache name, OR
- B) Remove service worker entirely (it adds complexity for a marketing site)
- Recommended: Option B -- remove sw.js, remove SW registration from Layout.astro, remove
Service-Worker-Allowedfrom_headers
- Acceptance Criteria:
- Consistent approach: either working SW or no SW
- No broken cache behavior
PHASE 3: Medium Fixes & Enhancements
Task 3.1: Delete Unused CSS Files
- Status: COMPLETED
- Priority: MEDIUM
- Files:
public/assets/css/colors/red.css,sky.css,yellow.css,style2-rtl.css, unused font CSS files - Fix: Delete files not referenced in Layout.astro or any component. Keep: bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, green.css, plugins.css, custom-overrides.css
- Acceptance Criteria:
- Only used CSS files remain
- Build still succeeds
Task 3.2: Remove Source Map from Production
- Status: COMPLETED
- Priority: LOW
- File:
public/assets/css/style.css.map - Fix: Delete
style.css.mapfrompublic/ - Acceptance Criteria:
- No source maps in production build
Task 3.3: Add Root favicon.ico
- Status: COMPLETED
- Priority: LOW
- File: New
public/favicon.ico - Fix: Copy
public/assets/favicon/favicon.icotopublic/favicon.ico - Acceptance Criteria:
-
/favicon.icoreturns valid icon
-
Task 3.4: Normalize Route Casing
- Status: COMPLETED
- Priority: MEDIUM
- Files: Rename
src/pages/Privacy.astro->privacy.astro,Terms.astro->terms.astro, same for/ar/ - Fix:
- Rename files to lowercase
- Add 301 redirects in
_redirects:/Privacy /privacy 301etc. - Update all internal links (nav, footer, sitemap, llms.txt)
- Update hreflang alternates
- Acceptance Criteria:
- All URLs are lowercase kebab-case
- Old capitalized URLs 301 redirect to lowercase
- No broken internal links
Task 3.5: Improve Sitemap Priority Differentiation
- Status: COMPLETED
- Priority: LOW
- File:
src/pages/sitemap.xml.ts(after Task 1.4 rename) - Fix: Assign meaningful priorities:
- 1.0: homepage
- 0.9: services, prices
- 0.8: use-cases, by-industry, by-role, apps, faqs
- 0.5: contact
- 0.3: privacy, terms
- Acceptance Criteria:
- Different pages have different priorities
- Legal pages have low priority
Task 3.6: Fix dateModified to Use Actual Dates
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/components/SEO.astro,src/pages/sitemap.xml.ts - Issue:
new Date().toISOString()means every build changes lastmod/dateModified for every page, even if content didn't change. Google treats this as unreliable. - Fix: Use a static date that's manually updated when content actually changes:
Or use git file modification dates at build time.const PAGE_DATES: Record<string, string> = { 'home': '2026-02-19', 'services': '2026-02-19', // ... etc }; - Acceptance Criteria:
- dateModified reflects actual content change dates
- Sitemap lastmod is meaningful
Task 3.7: Remove .well-known/schema.json Duplication
- Status: COMPLETED
- Priority: LOW
- File:
public/.well-known/schema.json - Issue: Duplicate of Organization schema in SEO.astro, with inconsistencies. Not discovered by search engines.
- Fix: Either delete it entirely (AI tools that need it can use inline JSON-LD from HTML), or sync it with SEO.astro output
- Acceptance Criteria:
- No conflicting schema data between files
Task 3.8: Add Image Sitemap
- Status: COMPLETED
- Priority: MEDIUM
- File: New
src/pages/image-sitemap.xml.ts - Fix: Create image sitemap listing all key images (logos, illustrations, screenshots) with descriptive titles and captions
- Acceptance Criteria:
- Image sitemap validates
- Referenced from sitemap index or robots.txt
Task 3.9: Improve CSP (Remove unsafe-eval)
- Status: COMPLETED
- Priority: MEDIUM
- File:
public/_headers - Fix:
- Test removing
'unsafe-eval'from script-src - If Alpine.js breaks, switch to
@alpinejs/cspbuild - If GTM breaks, use nonce-based CSP or keep
'unsafe-inline'only
- Test removing
- Acceptance Criteria:
- No
'unsafe-eval'in CSP - All scripts still function
- No
Task 3.10: Add Render-Blocking CSS Optimization
- Status: COMPLETED
- Priority: MEDIUM
- File:
src/layouts/Layout.astro - Issue: 4-5 synchronous
<link rel="stylesheet">in<head>are render-blocking. - Fix:
- Inline critical CSS (above-the-fold styles) directly in
<style>tag - Load non-critical CSS with
media="print" onload="this.media='all'"pattern - At minimum, move
plugins.cssandcustom-overrides.cssto deferred loading
- Inline critical CSS (above-the-fold styles) directly in
- Acceptance Criteria:
- LCP improved (measure with Lighthouse before/after)
- No FOUC (Flash of Unstyled Content)
PHASE 4: Arabic AI Content
Task 4.1: Complete Arabic llms-full.txt
- Status: COMPLETED
- Priority: HIGH
- File:
public/llms-full.txt - Issue: Arabic section is only
30 lines (8%) vs 430 lines of English. Needs full Arabic translation of ALL sections. - Fix: Translate all sections: products, industry solutions, pricing, technical specs, FAQ, contact info. Use professional Arabic (not machine translation).
- Acceptance Criteria:
- Arabic section length is comparable to English (~400+ lines)
- All sections have Arabic equivalents
Task 4.2: Add Arabic to ai-plugin.json
- Status: COMPLETED
- Priority: HIGH
- File:
public/.well-known/ai-plugin.json - Issue: 0% Arabic content.
- Fix: Add Arabic descriptions, keywords, service names, industry names alongside English.
- Acceptance Criteria:
- Both
description_for_humananddescription_for_modelhave Arabic versions - JSON validates correctly
- Both
Task 4.3: Add Arabic to schema.json
- Status: COMPLETED
- Priority: MEDIUM
- File:
public/.well-known/schema.json - Fix: Add
alternateNamein Arabic, Arabic description, Arabic service names. - Acceptance Criteria:
- Arabic description present
- Arabic service/feature names present
Task 4.4: Complete Arabic llms.txt Sections
- Status: COMPLETED
- Priority: MEDIUM
- File:
public/llms.txt - Issue: ~85% Arabic but missing: by-role page link, Events/Logistics industries, Technical Specs section.
- Fix: Add missing Arabic page links and sections.
- Acceptance Criteria:
- All page links present in Arabic section
- All industry sections present
- Technical specs section in Arabic
Task 4.5: Bilingual SEO.astro AI Meta + Schema
- Status: COMPLETED
- Priority: HIGH
- File:
src/components/SEO.astro - Fix: Covered by Task 2.3 and 2.4. Verify both are complete.
- Acceptance Criteria:
- All AI meta tags language-conditional
- SoftwareApplication description bilingual
- Service schema bilingual
PHASE 5: Performance & Polish
Task 5.1: PurgeCSS on Bootstrap/Theme
- Status: COMPLETED
- Priority: HIGH
- File:
astro.config.mjs - Fix: Integrate PurgeCSS to remove unused CSS (~200KB savings)
- Result: Saves 556KB across 5 CSS files (bootstrap.min.css, bootstrap.rtl.min.css, style.css, style-rtl.css, plugins.css)
Task 5.2: Delete Dead Code (Plans.astro)
- Status: COMPLETED
- File:
src/components/Plans.astro(583 lines, fully commented out) - Fix: Delete the file
- Result: 583 lines of dead code removed
Task 5.3: Convert PNG to WebP
- Status: COMPLETED (Session 5, commit
6f9d853) - Fix: Batch-converted 30 PNG illustrations to WebP (saves 3.11MB), updated all 62 references
- Result: 30 files converted, all
<img>tags updated to.webpsources
Task 5.4: Add .env.example
- Status: COMPLETED
- File: New
.env.example - Fix: Document: PUBLIC_GTM_ID, PUBLIC_FORMSPARK_FORM_ID, PUBLIC_TURNSTILE_SITE_KEY, PLANS_API_TOKEN
- Result: Created .env.example with all 4 required environment variables documented
Task 5.5: Add Image Width/Height Attributes
- Status: COMPLETED
- Fix: Add explicit
widthandheightto all<img>tags to prevent CLS - Result: 73 img tags updated across all components and pages
Task 5.6: Add Skip Navigation Link
- Status: COMPLETED
- File:
src/layouts/Layout.astro - Fix: Add
<a class="skip-link" href="#main-content">Skip to content</a>as first body element - Result: RTL-aware skip navigation link added with bilingual text
Task 5.7: Add @astrojs/sitemap Integration
- Status: SKIPPED
- Priority: MEDIUM
- Reason: Custom sitemap at
src/pages/sitemap.xml.tsalready works correctly with full hreflang and x-default support. Adding @astrojs/sitemap would either conflict with the existing sitemap or produce a redundant secondary sitemap without the custom hreflang configuration. No action needed.
Task 5.8: Consider @graph JSON-LD with SpeakableSpecification
- Status: DONE
- Priority: LOW
- Fix: Added
SpeakableSpecificationwithcssSelector: ["h1", ".hero-description", "main h2"]to the WebPage schema insrc/components/SEO.astro@graph. Voice assistants (Google Assistant, Alexa) can now identify which content to read aloud.
File Index
| File | Tasks |
|---|---|
public/_redirects |
1.1, 2.6, 3.4 |
public/assets/images/logos/urWhats-og-image.png |
1.2 (CREATE) |
public/assets/favicon/site.webmanifest |
1.3 |
src/pages/sitemap-index.xml.ts -> sitemap.xml.ts |
1.4 |
public/robots.txt |
1.4, 2.8 |
public/_headers |
1.6, 3.9 |
src/components/SEO.astro |
1.5, 1.7, 2.1-2.5, 2.9, 3.6, 4.5 |
src/layouts/Layout.astro |
2.10, 2.11, 3.10, 5.6 |
public/.well-known/security.txt |
2.7 |
public/.well-known/ai-plugin.json |
4.2 |
public/.well-known/schema.json |
3.7, 4.3 |
public/llms.txt |
4.4 |
public/llms-full.txt |
4.1 |
public/sw.js |
2.12 |
public/assets/css/ |
3.1, 3.2 |
src/pages/privacy.astro (renamed from Privacy.astro) |
3.4, 6.4 |
src/pages/terms.astro (renamed from Terms.astro) |
3.4, 6.4 |
PHASE 6: Production Sweep Fixes (MUST fix before merging to main)
These issues were found via comprehensive production-readiness sweep in Session 3. They exist in the LIVE code on
feature/seo-enhancementsbranch and must be fixed before merge.
Task 6.1: Fix UAE Legal Text in Privacy/Terms
- Status: COMPLETED
- Priority: CRITICAL
- Files:
public/assets/i18n/en.json,public/assets/i18n/ar.json - Issues (4 total):
en.jsonline ~63: References "UAE's Federal Decree-Law No. 45 of 2021" -- should be Saudi PDPL (Personal Data Protection Law, Royal Decree M/19 of 2021)ar.jsonline ~63: Same in Arabicen.jsonline ~561: Says "laws of the United Arab Emirates (UAE)" and "courts of Dubai" -- should be "laws of the Kingdom of Saudi Arabia" and "courts of Riyadh"ar.jsonline ~561: Same in Arabic -- says "قوانين دولة الإمارات العربية المتحدة" and "محاكم دبي"
- Fix: Replace all UAE/Dubai legal references with Saudi Arabia/Riyadh equivalents. Use Saudi PDPL for data protection law reference.
- Acceptance Criteria:
- No UAE/Dubai references in privacy or terms content
- Saudi PDPL referenced instead of UAE Federal Decree-Law
- Governing law is Saudi Arabia, jurisdiction is Riyadh courts
Task 6.2: Remove GHL Leak from Integration Text
- Status: COMPLETED
- Priority: CRITICAL
- Files:
public/assets/i18n/en.json(line ~299),public/assets/i18n/ar.json(line ~300) - Issue: Integration description mentions "GHL" (GoHighLevel) -- this is the white-label platform name that must NEVER appear on public-facing content.
- EN: "Connect with Twilio, GHL, WhatsappCloudAPI, and more for maximum flexibility."
- AR: "اتصال مع Twilio و GHL و WhatsappCloudAPI وغيرها"
- Fix: Remove "GHL" from both strings. Replace with a generic term or another integration name.
- Acceptance Criteria:
- No "GHL" or "GoHighLevel" anywhere in en.json or ar.json
- Integration text still reads naturally
Task 6.3: Fix +971 Phone Placeholder
- Status: COMPLETED
- Priority: MEDIUM
- Files:
public/assets/i18n/en.json(line ~522),public/assets/i18n/ar.json(line ~521) - Issue: Mobile number field placeholder uses "+971501234567" (UAE code) -- should use Saudi +966 prefix.
- EN: "Enter your mobile number (e.g., +971501234567)"
- AR: "أدخل رقم جوالك (مثال: +971501234567)"
- Fix: Change to "+966501234567"
- Acceptance Criteria:
- Phone placeholder uses +966 prefix
Task 6.4: Rename PascalCase Page Files to Lowercase
- Status: COMPLETED
- Priority: MEDIUM
- Files to rename:
src/pages/Privacy.astro->src/pages/privacy.astrosrc/pages/Terms.astro->src/pages/terms.astrosrc/pages/ar/Privacy.astro->src/pages/ar/privacy.astrosrc/pages/ar/Terms.astro->src/pages/ar/terms.astro
- Files to update after rename:
src/pages/sitemap.xml.ts(lines ~15-16): Change 'Privacy','Terms' to 'privacy','terms'src/components/Footer.astro(lines ~84-85): Update href pathspublic/.well-known/security.txt(line ~9): Update policy URLpublic/_redirects(lines ~25-28): Update/remove redirect rules, add /Privacy->/privacy 301 and /Terms->/terms 301 for old URL preservation
- Fix: Rename files, then update all references. On Windows use
git mvto ensure git tracks the rename. - Acceptance Criteria:
- All page files are lowercase
- Old PascalCase URLs 301 redirect to lowercase
- Footer, sitemap, security.txt all reference lowercase paths
-
npm run buildsucceeds
Build Verification Checklist
After ALL changes:
-
npm run buildsucceeds with 0 errors - All 23 pages generated
- No console warnings during build
-
npm run preview-- spot check: homepage, /ar, /services, /ar/services, /prices, /404 - Validate JSON-LD with Google Rich Results Test
- Check OG image with Facebook Sharing Debugger
- Check Twitter Cards with Twitter Card Validator