From d5e6df42d3fb75daf476edffb76164d6b026d66f Mon Sep 17 00:00:00 2001 From: Your Name <119736744+aborayan2022@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:47:43 +0200 Subject: [PATCH] Add Plausible and Google Site Verification support to analytics and environment configuration --- .env.example | 3 + docs/seo_strategy.md | 252 +++++++++++++++++++++++++++++ frontend/.env.example | 3 + frontend/src/services/analytics.ts | 92 +++++++++-- 4 files changed, 334 insertions(+), 16 deletions(-) create mode 100644 docs/seo_strategy.md diff --git a/.env.example b/.env.example index 9ed3769..62abcc7 100644 --- a/.env.example +++ b/.env.example @@ -28,6 +28,9 @@ CORS_ORIGINS=http://localhost:5173,http://localhost:3000 # Frontend Analytics / Ads (Vite) VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX +VITE_PLAUSIBLE_DOMAIN= +VITE_PLAUSIBLE_SRC=https://plausible.io/js/script.js +VITE_GOOGLE_SITE_VERIFICATION= VITE_ADSENSE_CLIENT_ID=ca-pub-XXXXXXXXXXXXXXXX VITE_ADSENSE_SLOT_HOME_TOP=1234567890 VITE_ADSENSE_SLOT_HOME_BOTTOM=1234567891 diff --git a/docs/seo_strategy.md b/docs/seo_strategy.md new file mode 100644 index 0000000..a6079ea --- /dev/null +++ b/docs/seo_strategy.md @@ -0,0 +1,252 @@ +# SaaS-PDF — SEO & Growth Strategy + +> Roadmap to **500 000 monthly visits** for a multilingual (EN / AR / FR) free-tool SaaS. + +--- + +## 1. Current Technical SEO Foundation + +| Layer | Status | +|-------|--------| +| **Canonical URLs** | Every page emits `` via `SEOHead` | +| **OpenGraph tags** | `og:title`, `og:description`, `og:url`, `og:type`, `og:site_name`, `og:locale` on all pages | +| **Twitter cards** | `twitter:card`, `twitter:title`, `twitter:description` on all pages | +| **Structured data** | `WebSite`, `Organization`, `WebPage`, `WebApplication`, `BreadcrumbList`, `FAQPage` JSON-LD | +| **Sitemap** | Auto-generated via `scripts/generate_sitemap.py` — 37 URLs (5 pages + 32 tools) | +| **robots.txt** | Allows all crawlers; blocks `/api/`, `/account`, auth pages | +| **Internationalization** | Full i18n in EN, AR, FR — all tool pages, SEO content, and static pages | +| **Font loading** | `dns-prefetch` + `preconnect` + `display=swap` for Google Fonts | +| **Analytics** | GA4 (opt-in via `VITE_GA_MEASUREMENT_ID`) + Plausible (opt-in via `VITE_PLAUSIBLE_DOMAIN`) | +| **Search Console** | Verification via `VITE_GOOGLE_SITE_VERIFICATION` meta tag | +| **Page speed** | Code-split (lazy routes), Vite manual chunks, CSS minification, nginx gzip | + +--- + +## 2. Analytics Setup + +### Google Analytics 4 + +```env +VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX +``` + +- Auto-loaded via `initAnalytics()` on first render +- Page views tracked on every route change +- Custom events via `trackEvent('tool_used', { tool: 'compress-pdf' })` + +### Plausible (Privacy-Friendly Alternative) + +```env +VITE_PLAUSIBLE_DOMAIN=saas-pdf.com +VITE_PLAUSIBLE_SRC=https://plausible.io/js/script.js # or self-hosted URL +``` + +- Lightweight (< 1 KB), no cookies, GDPR-compliant +- Runs alongside or instead of GA4 — both are opt-in +- Custom events forwarded to Plausible automatically + +### Google Search Console + +```env +VITE_GOOGLE_SITE_VERIFICATION=your-verification-code +``` + +- Injected as `` at runtime +- Enables index coverage, search performance, and Core Web Vitals reporting + +--- + +## 3. SEO Content Architecture + +### 3.1 Tool Landing Pages (32 pages) + +Each tool page (`/tools/{slug}`) renders via `ToolLandingPage` wrapper: + +1. **Helmet** — title, meta description, keywords, canonical, OG, Twitter +2. **Tool UI** — upload zone, processing, download +3. **What it does** — descriptive paragraph +4. **How to use** — ordered steps (4 items) +5. **Benefits** — bullet list (5 items) +6. **Common use cases** — bullet list (5 items) +7. **FAQ** — accordion with 3–5 Q&A pairs (generates `FAQPage` schema) +8. **Related tools** — internal link grid (4 tools) + +All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR. + +### 3.2 Static Pages (5 pages) + +| Path | Schema | Purpose | +|------|--------|---------| +| `/` | `WebSite` + `Organization` | Homepage with hero + tool grid | +| `/about` | `WebPage` | Mission, technology, security | +| `/contact` | `WebPage` | Contact form (mailto-based) | +| `/privacy` | `WebPage` | Privacy policy | +| `/terms` | `WebPage` | Terms of service | + +### 3.3 SEO Support Files + +| File | Purpose | +|------|---------| +| `sitemap.xml` | All 37 indexable URLs with priority and changefreq | +| `robots.txt` | Crawler directives + sitemap pointer | +| `llms.txt` | AI/LLM discoverability file | +| `humans.txt` | Team credits | + +--- + +## 4. Growth Playbook — Path to 500K Visits/Month + +### Phase A: Foundation (Month 1–2) — Target: 5K visits/month + +**Technical:** +- [ ] Deploy to production with real domain +- [ ] Submit sitemap to Google Search Console and Bing Webmaster Tools +- [ ] Run Lighthouse audits → fix any issues below 90 score +- [ ] Set up GA4 + Plausible dashboards +- [ ] Verify Core Web Vitals pass (LCP < 2.5s, FID < 100ms, CLS < 0.1) + +**Content:** +- [ ] Publish all 32 tool pages with full SEO content +- [ ] Ensure hreflang tags work across EN/AR/FR (add `hreflang` links if using subdomains or subdirectories) +- [ ] Add FAQ schema to all tool pages (already done) + +**Indexing:** +- [ ] Request indexing for top 10 highest-priority tool pages via Search Console +- [ ] Monitor index coverage weekly + +--- + +### Phase B: Content Marketing (Month 3–6) — Target: 30K visits/month + +**Blog / Resource Pages:** +- [ ] Create `/blog` section with 2–4 articles per week +- [ ] Target long-tail keywords per tool: + - "how to compress PDF without losing quality" + - "convert PDF to Word free online" + - "merge multiple PDFs into one" + - "كيفية دمج ملفات PDF" (Arabic equivalent) + - "comment fusionner des fichiers PDF" (French equivalent) +- [ ] Each blog post links to the relevant tool page (internal linking) +- [ ] Create comparison pages: "SaaS-PDF vs iLovePDF vs SmallPDF" + +**Keyword Research Strategy:** +- Target 200–500 keywords across three tiers: + - **Head terms** (high volume, high competition): "PDF converter", "merge PDF" — target via homepage + tool pages + - **Mid-tail** (medium volume): "compress PDF to 1MB", "PDF to Word with formatting" — target via tool pages + blog + - **Long-tail** (low volume, low competition): "how to remove watermark from PDF free", "convert scanned PDF to text" — target via blog articles + +**Multilingual Scale:** +- Every blog post published in EN, AR, and FR simultaneously +- Arabic content is underserved in the PDF tools niche — a major competitive advantage +- Target 50+ Arabic long-tail keywords with almost zero competition + +--- + +### Phase C: Authority Building (Month 6–12) — Target: 100K visits/month + +**Link Building:** +- [ ] Submit to web tool directories (Product Hunt, AlternativeTo, ToolFinder) +- [ ] Create free embeddable widgets (PDF page counter, file size estimator) +- [ ] Write guest posts on productivity and SaaS blogs +- [ ] Build a "Free PDF Tools" resource page that other sites want to link to +- [ ] Reach out to educational institutions (free tools for students = .edu backlinks) + +**Technical Improvements:** +- [ ] Implement `hreflang` for multilingual SEO (subdirectories: `/en/`, `/ar/`, `/fr/`) +- [ ] Add breadcrumb navigation to tool pages +- [ ] Create topic clusters: PDF Tools Hub → individual tool pages +- [ ] Implement internal search with search analytics + +**Social Proof:** +- [ ] Add user count ("X files processed this month") to homepage +- [ ] Collect and display testimonials +- [ ] Create YouTube tutorials for each tool (video SEO) + +--- + +### Phase D: Scale & Monetize (Month 12–18) — Target: 500K visits/month + +**Content Flywheel:** +- [ ] 100+ blog posts across 3 languages (300+ total pages) +- [ ] Programmatic SEO: auto-generate pages for format combinations + - "/convert/pdf-to-jpg", "/convert/docx-to-pdf", "/convert/png-to-webp" + - Each page targets a specific keyword with unique content +- [ ] Create glossary pages: "What is OCR?", "What is PDF/A?" +- [ ] Build an API documentation page (drives developer traffic) + +**Distribution Channels:** +- [ ] Email newsletter with PDF tips (capture leads via tool pages) +- [ ] Social media presence: Twitter/X, LinkedIn, Reddit (r/pdf, r/productivity) +- [ ] Quora/Stack Overflow answers linking back to tools +- [ ] YouTube shorts demonstrating each tool (< 60s) + +**Conversion Optimization:** +- [ ] A/B test hero copy, CTA buttons, tool card layouts +- [ ] Add "suggested next tool" after file processing +- [ ] Implement PWA for repeat visits (offline capability) + +**Performance Monitoring:** +- Key metrics to track weekly: + - Organic sessions (GA4/Plausible) + - Indexed pages (Search Console) + - Average position for target keywords + - Click-through rate (CTR) from SERPs + - Pages per session / bounce rate + - Core Web Vitals scores + - Backlink count (Ahrefs/Moz) + +--- + +## 5. Competitive Analysis + +| Competitor | Monthly Traffic | Strengths | Our Advantage | +|-----------|----------------|-----------|---------------| +| iLovePDF | ~150M | Brand recognition, wide tool set | Arabic/French i18n, free with no limits | +| SmallPDF | ~60M | UX polish, enterprise features | No signup required, fully free | +| PDF24 | ~40M | Desktop app + web tools | Lightweight, faster load, mobile-first | +| Sejda | ~10M | Advanced editing features | More tools, trilingual content | + +**Key differentiators for SaaS-PDF:** +1. **Trilingual** — EN/AR/FR from day one (Arabic market is largely unserved) +2. **No signup** — zero friction, instant file processing +3. **32 tools** — broader coverage than most competitors +4. **AI-powered tools** — OCR, Chat PDF, Summarize, Translate (unique value) +5. **Privacy-first** — files auto-deleted within 30 minutes + +--- + +## 6. Monthly SEO Checklist + +``` +□ Review Search Console for crawl errors and fix immediately +□ Check index coverage — ensure all 37+ pages are indexed +□ Review top queries — identify rising keywords to create content for +□ Publish 8–16 blog posts (2–4/week × 3 languages) +□ Build 5–10 backlinks through outreach +□ Run Lighthouse audit — maintain 90+ scores +□ Update sitemap if new pages were added +□ Monitor Core Web Vitals — fix any regressions +□ Review analytics dashboards — identify underperforming pages +□ Competitor check — new features or content gaps to exploit +``` + +--- + +## 7. Environment Variables Reference + +```env +# Google Analytics 4 (optional) +VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX + +# Plausible Analytics (optional, privacy-friendly) +VITE_PLAUSIBLE_DOMAIN=saas-pdf.com +VITE_PLAUSIBLE_SRC=https://plausible.io/js/script.js + +# Google Search Console verification (optional) +VITE_GOOGLE_SITE_VERIFICATION=your-verification-code + +# Google AdSense (optional) +VITE_ADSENSE_CLIENT_ID=ca-pub-XXXXXXXXXXXXXXXX +``` + +All integrations are **opt-in** — if the env var is empty or unset, the corresponding script is not loaded, keeping the bundle clean for development. diff --git a/frontend/.env.example b/frontend/.env.example index c983eb1..f904a6e 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,4 +1,7 @@ VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX +VITE_PLAUSIBLE_DOMAIN= +VITE_PLAUSIBLE_SRC=https://plausible.io/js/script.js +VITE_GOOGLE_SITE_VERIFICATION= VITE_ADSENSE_CLIENT_ID=ca-pub-XXXXXXXXXXXXXXXX VITE_ADSENSE_SLOT_HOME_TOP=1234567890 VITE_ADSENSE_SLOT_HOME_BOTTOM=1234567891 diff --git a/frontend/src/services/analytics.ts b/frontend/src/services/analytics.ts index 3211ea3..0256aea 100644 --- a/frontend/src/services/analytics.ts +++ b/frontend/src/services/analytics.ts @@ -4,12 +4,17 @@ declare global { interface Window { dataLayer: unknown[]; gtag?: (...args: unknown[]) => void; + plausible?: (event: string, opts?: { props?: Record }) => void; } } const GA_MEASUREMENT_ID = (import.meta.env.VITE_GA_MEASUREMENT_ID || '').trim(); +const PLAUSIBLE_DOMAIN = (import.meta.env.VITE_PLAUSIBLE_DOMAIN || '').trim(); +const PLAUSIBLE_SRC = (import.meta.env.VITE_PLAUSIBLE_SRC || 'https://plausible.io/js/script.js').trim(); let initialized = false; +// ─── Google Analytics ──────────────────────────────────────────── + function ensureGtagShim() { window.dataLayer = window.dataLayer || []; window.gtag = @@ -34,34 +39,89 @@ function loadGaScript() { document.head.appendChild(script); } -export function initAnalytics() { - if (initialized || !GA_MEASUREMENT_ID || typeof window === 'undefined') return; +// ─── Plausible Analytics ───────────────────────────────────────── + +function loadPlausibleScript() { + if (!PLAUSIBLE_DOMAIN) return; + + const existing = document.querySelector('script[data-plausible]'); + if (existing) return; + + const script = document.createElement('script'); + script.defer = true; + script.setAttribute('data-domain', PLAUSIBLE_DOMAIN); + script.setAttribute('data-plausible', ''); + script.src = PLAUSIBLE_SRC; + document.head.appendChild(script); +} + +// ─── Search Console verification ───────────────────────────────── + +function injectSearchConsoleVerification() { + const code = (import.meta.env.VITE_GOOGLE_SITE_VERIFICATION || '').trim(); + if (!code) return; + + const existing = document.querySelector('meta[name="google-site-verification"]'); + if (existing) return; + + const meta = document.createElement('meta'); + meta.name = 'google-site-verification'; + meta.content = code; + document.head.appendChild(meta); +} + +// ─── Public API ────────────────────────────────────────────────── + +export function initAnalytics() { + if (initialized || typeof window === 'undefined') return; + + // Google Analytics + if (GA_MEASUREMENT_ID) { + ensureGtagShim(); + loadGaScript(); + window.gtag?.('js', new Date()); + window.gtag?.('config', GA_MEASUREMENT_ID, { send_page_view: false }); + } + + // Plausible + loadPlausibleScript(); + + // Search Console + injectSearchConsoleVerification(); - ensureGtagShim(); - loadGaScript(); - window.gtag?.('js', new Date()); - window.gtag?.('config', GA_MEASUREMENT_ID, { send_page_view: false }); initialized = true; } export function trackPageView(path: string) { - if (!initialized || !window.gtag) return; - - window.gtag('event', 'page_view', { - page_path: path, - page_location: `${window.location.origin}${path}`, - page_title: document.title, - }); + // GA4 + if (window.gtag) { + window.gtag('event', 'page_view', { + page_path: path, + page_location: `${window.location.origin}${path}`, + page_title: document.title, + }); + } + // Plausible tracks page views automatically via its script } export function trackEvent( eventName: string, params: Record = {} ) { - if (!initialized || !window.gtag) return; - window.gtag('event', eventName, params); + // GA4 + if (window.gtag) { + window.gtag('event', eventName, params); + } + // Plausible custom event + if (window.plausible) { + const props: Record = {}; + for (const [k, v] of Object.entries(params)) { + if (v !== undefined) props[k] = String(v); + } + window.plausible(eventName, { props }); + } } export function analyticsEnabled() { - return Boolean(GA_MEASUREMENT_ID); + return Boolean(GA_MEASUREMENT_ID) || Boolean(PLAUSIBLE_DOMAIN); }