<title>Florida Dispensary Deals & Dispensary Discounts</title>
<link rel="canonical" href="https://flcannabisdeals.org/"/>


Welcome To FLCannabisDeals

All Things Florida Dispensary Deals & Discounts


meta name="description" content="Posting All The Florida Dispensary Deals and Dispensary Discounts Helping Patients Find The Best Dispensary Deals Near You..."


Okay, so the competitor (flcannabisdeals.org) has:
- Title: "Florida Dispensary Deals & Dispensary Discounts"
- H1: "All Things Florida Dispensary Deals & Discounts"
- Description: "Posting All The Florida Dispensary Deals and Dispensary Discounts Helping Patients Find The Best Dispensary Deals Near You..."

Their title and h1 are MORE focused than ours. They're targeting "Florida Dispensary Deals" which is more specific than our "Florida Dispensary Deals Today".

Also, I notice the curl output for cannadealsfl.com/deals returned JSON deal data at the beginning - that's strange. It seems like the page is outputting JSON in the HTML? Let me check the actual rendered HTML more carefully.

Actually, looking at the output, I see it's returning JSON deal data first, then HTML. That's weird - it's like the page is rendering the API response as text? This could be a bug.

Let me focus on what I know:
1. All pages have duplicate titles/descriptions (keyword cannibalization)
2. Target keyword "cannabis deals florida" is NOT in titles
3. Competitor has more focused titles
4. No canonical URLs visible in HTML

I should now start implementing fixes. Let me create the generateMetadata functions for the key pages.

The competitor has cleaner titles than us. Now let me start fixing. I'll implement the metadata fixes on dev:


import { Suspense } from 'react'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { NewDealsHero } from '@/components/sections/new-deals-hero'
import { FeaturedDeals } from '@/components/sections/featured-deals'
import { DispensarySpotlight } from '@/components/sections/dispensary-spotlight'
import NewsletterCTABanner from '@/components/newsletter/newsletter-cta-banner'
import { RecentDeals } from '@/components/sections/recent-deals'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { SettingSunLogo } from '@/components/effects/setting-sun-logo'
import { getDeals, getDispensaries } from '@/lib/database'

export const dynamic = 'force-dynamic' // Force dynamic rendering to avoid build-time database calls
export const revalidate = 60 // Revalidate every 60 seconds

export default async function HomePage() {
  // Fetch data for the homepage - with limits to prevent timeouts
  let recentDeals = []
  let dispensaries = []

  try {
    // Get timestamp for 7 days ago (extended from 24 hours to ensure deals are visible)
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()

    const results = await Promise.all([
      // Get deals from last 7 days
      getDeals({ created_after: sevenDaysAgo }, { limit: 200 }),
      getDispensaries({ limit: 60 })
    ])
    recentDeals = results[0].deals
    dispensaries = results[1].dispensaries
  } catch (error) {
    console.error('Failed to fetch homepage data:', error)
    // Continue with empty arrays - page will still render
  }

  return (
    <>
      <SEOStructuredData />

        {/* Setting sun effect - logo fades as you scroll */}
        <SettingSunLogo />
        <PageLayout>

          {/* Logo Header */}

          {/* New Deals Hero - Shows top 4 deals from last hour */}
          <Suspense fallback={
}>
            <NewDealsHero deals={recentDeals} />
          </Suspense>

          {/* Latest Deals Section - Shows most recent deals in chronological order */}

                  <svg className="w-6 h-6 text-emerald-600 dark:text-emerald-400 animate-pulse" fill="currentColor" viewBox="0 0 20 20">
                    <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />
                  </svg>

                    Latest Deals

                  <svg className="w-6 h-6 text-emerald-600 dark:text-emerald-400 animate-pulse" fill="currentColor" viewBox="0 0 20 20" style={{ animationDelay: '0.5s' }}>
                    <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />
                  </svg>

                  Fresh deals as they arrive

              <Suspense fallback={
}>
                <RecentDeals deals={recentDeals} />
              </Suspense>

          {/* Combined Deals Section */}

                  Recent Deals

                  All deals from the past week.

              <Suspense fallback={
}>
                <FeaturedDeals deals={recentDeals} />
              </Suspense>

              {/* View Archive Button */}

                  <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                    <path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" />
                  </svg>
                  View Deal Archive (24-72hrs)
                  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7l5 5m0 0l-5 5m5-5H6" />
                  </svg>

          {/* Dispensary Spotlight */}

                  Dispensary Spotlight

                  Discover top-rated dispensaries across Florida and their exclusive offers.

              <Suspense fallback={
}>
                <DispensarySpotlight dispensaries={dispensaries.slice(0, 6)} />
              </Suspense>

          {/* Newsletter Signup - Compelling CTA */}

              <Suspense fallback={
}>
                <NewsletterCTABanner variant="inline" dismissable={false} />
              </Suspense>

        </PageLayout>

    </>
  )
}


The homepage doesn't have generateMetadata. I need to add it. Let me create the fix on dev.

Let me add `generateMetadata` to the homepage on dev:

exec

ssh carl "cd ~/cannadealsfl-dev && cat app/page.tsx | head -20"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
import { Suspense } from 'react'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { NewDealsHero } from '@/components/sections/new-deals-hero'
import { FeaturedDeals } from '@/components/sections/featured-deals'
import { DispensarySpotlight } from '@/components/sections/dispensary-spotlight'
import NewsletterCTABanner from '@/components/newsletter/newsletter-cta-banner'
import { RecentDeals } from '@/components/sections/recent-deals'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { SettingSunLogo } from '@/components/effects/setting-sun-logo'
import { getDeals, getDispensaries } from '@/lib/database'

export const dynamic = 'force-dynamic' // Force dynamic rendering to avoid build-time database calls
export const revalidate = 60 // Revalidate every 60 seconds

export default async function HomePage() {
  // Fetch data for the homepage - with limits to prevent timeouts
  let recentDeals = []
  let dispensaries = []


I need to add generateMetadata to the homepage. Let me create the fix and push it to dev.

ssh carl "cd ~/cannadealsfl-dev && cat > /tmp/homepage_metadata.ts << 'EOF'
import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts',
    description: 'Find the best cannabis deals in Florida. Daily updates on medical marijuana discounts, dispensary coupons, and weed deals from top Florida dispensaries like Trulieve, MUV, and Curaleaf.',
    keywords: [
      'cannabis deals florida',
      'florida weed deals',
      'mmj deals florida',
      'dispensary coupons florida',
      'medical marijuana discounts',
      'florida dispensary deals',
      'weed discounts florida'
    ],
    openGraph: {
      title: 'Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts',
      description: 'Find the best cannabis deals in Florida. Daily updates on medical marijuana discounts, dispensary coupons, and weed deals.',
      url: 'https://cannadealsfl.com',
      images: [
        {
          url: '/og-image.jpg',
          width: 1200,
          height: 630,
          alt: 'CannaDealsFL - Florida Cannabis Deals',
        },
      ],
    },
    twitter: {
      card: 'summary_large_image',
      title: 'Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts',
      description: 'Find the best cannabis deals in Florida. Daily updates on medical marijuana discounts and dispensary coupons.',
      images: ['/og-image.jpg'],
    },
    alternates: {
      canonical: 'https://cannadealsfl.com',
    },
  }
}
EOF
echo '✅ Created metadata template'"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Created metadata template


Now I need to insert this into the homepage. Let me use sed to insert the generateMetadata function after the imports.

ssh carl "cd ~/cannadealsfl-dev && cp app/page.tsx app/page.tsx.bak && sed -i '/export const dynamic/a\\nimport type { Metadata } from '\\''next'\\''\\n\\nexport async function generateMetadata(): Promise<Metadata> {\\n  return {\\n    title: '\\''Cannabis Deals Florida | Florida Weed Deals \\& MMJ Discounts'\\''\\,\\n    description: '\\''Find the best cannabis deals in Florida. Daily updates on medical marijuana discounts, dispensary coupons, and weed deals from top Florida dispensaries like Trulieve, MUV, and Curaleaf.'\\''\\,\\n    alternates: {\\n      canonical: '\\''https://cannadealsfl.com'\\''\\,\\n    },\\n  }\\n}\\n' app/page.tsx && echo '✅ Added generateMetadata to homepage'"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Added generateMetadata to homepage


Good, I added the generateMetadata function to the homepage. Now let me do the same for the deals page and the dispensary pages.

Now the deals page:

ssh carl "cd ~/cannadealsfl-dev && cat app/deals/page.tsx | head -25"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
import { Suspense } from 'react'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealsPageWithTabs } from '@/components/pages/deals-page-with-tabs'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDeals, getDispensaries, getProductCategories, getProductSubcategories, getDealTypes } from '@/lib/database'

export const dynamic = 'force-dynamic'
export const revalidate = 60 // Revalidate every minute

export default async function DealsPage() {
  // Fetch data for the deals page
  let activeDeals = []
  let activeTotal = 0
  let archivedDeals = []
  let archivedTotal = 0
  let featuredDeals = []
  let dispensaries = []
  let categories = []
  let subcategories = []
  let dealTypes = []

  try {
    // Calculate time ranges
    const now = new Date()


Let me add generateMetadata to the deals page as well.

ssh carl "cd ~/cannadealsfl-dev && sed -i '/export const dynamic/i \\
import type { Metadata } from '\\''next'\\''\\n\\
export async function generateMetadata(): Promise<Metadata> {\\n\\
  return {\\n\\
    title: '\\''Cannabis Deals Florida Today | Weed Deals \\& MMJ Discounts'\\''\\,\\n\\
    description: '\\''Browse all cannabis deals in Florida today. Find medical marijuana discounts, dispensary coupons, and the best weed deals from 20+ Florida dispensaries. Updated hourly.'\\''\\,\\n\\
    keywords: [\\n\\
      '\\''cannabis deals florida'\\''\\,\\n\\
      '\\''weed deals florida'\\''\\,\\n\\
      '\\''mmj deals'\\''\\,\\n\\
      '\\''dispensary coupons'\\''\\,\\n\\
      '\\''florida dispensary deals'\\''\\,\\n\\
      '\\''medical marijuana discounts'\\''\\,\\n\\
      '\\''cheap weed florida'\\''\\n\\
    ]\\,\\n\\
    openGraph: {\\n\\
      title: '\\''Cannabis Deals Florida Today | Weed Deals \\& MMJ Discounts'\\''\\,\\n\\
      description: '\\''Browse all cannabis deals in Florida today. Find medical marijuana discounts and dispensary coupons.'\\''\\,\\n\\
      url: '\\''https://cannadealsfl.com/deals'\\''\\,\\n\\
    }\\,\\n\\
    twitter: {\\n\\
      card: '\\''summary_large_image'\\''\\,\\n\\
      title: '\\''Cannabis Deals Florida Today | Weed Deals \\& MMJ Discounts'\\''\\,\\n\\
      description: '\\''Browse all cannabis deals in Florida today.'\\''\\,\\n\\
    }\\,\\n\\
    alternates: {\\n\\
      canonical: '\\''https://cannadealsfl.com/deals'\\''\\,\\n\\
    }\\,\\n\\
  }\\n\\
}\\n\\
' app/deals/page.tsx && echo '✅ Added generateMetadata to deals page'"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Added generateMetadata to deals page


Good, now let me add generateMetadata to the dispensary pages. This is the most important one since it's causing the cannibalization issue.

Now the critical one - dispensary pages:

ssh carl "cd ~/cannadealsfl-dev && cat app/dispensaries/\\[slug\\]/page.tsx | head -60"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'

interface DispensaryPageProps {
  params: {
    slug: string
  }
}

export default async function DispensaryPage({ params }: DispensaryPageProps) {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    notFound()
  }

  // Get deals for this dispensary
  const { deals } = await getDeals({ dispensary_id: dispensary.id }, { limit: 50 })

  return (
    <>
      <SEOStructuredData />
      <PageLayout showLogoBar={false}>

          {/* Dispensary Header */}

            {/* Mobile Layout */}

              {/* Logo and Title */}

                {dispensary.logo_url ? (
                  <img
                    src={dispensary.logo_url}
                    alt={`${dispensary.name} logo`}
                    className="w-16 h-16 object-contain rounded-lg bg-white p-2 border shadow-sm flex-shrink-0"
                  />
                ) : (

                    {dispensary.name.charAt(0)}

                )}

                  {dispensary.name}

              {/* Description - Structured Display */}
              {dispensary.description && (

                  {dispensary.description.split('•').map((item, index) => {
                    const trimmed = item.trim()


I see - the dispensary pages don't have generateMetadata. I need to add it. Let me create a proper generateMetadata function that will generate unique titles for each dispensary.

ssh carl "cd ~/cannadealsfl-dev && sed -i '/interface DispensaryPageProps/i \\
import type { Metadata } from '\\''next'\\''\\n\\
\\n\\
export async function generateMetadata({ params }: DispensaryPageProps): Promise<Metadata> {\\n\\
  const dispensary = await getDispensaryBySlug(params.slug)\\n\\
  \\n\\
  if (!dispensary) {\\n\\
    return { title: '\\''Dispensary Not Found'\\'' }\\n\\
  }\\n\\
  \\n\\
  const title = `${dispensary.name} Deals Florida | Cannabis Coupons \\& Discounts`\\n\\
  const description = `Find the latest ${dispensary.name} deals and cannabis discounts in Florida. Browse ${dispensary.name} coupons, promo codes, and medical marijuana offers.`\\n\\
  \\n\\
  return {\\n\\
    title,\\n\\
    description,\\n\\
    keywords: [\\n\\
      `${dispensary.name.toLowerCase()} deals florida`,\\n\\
      `${dispensary.name.toLowerCase()} coupons`,\\n\\
      `${dispensary.name.toLowerCase()} dispensary`,\\n\\
      'cannabis deals florida',\\n\\
      'dispensary discounts florida'\\n\\
    ]\\,\\n\\
    openGraph: {\\n\\
      title,\\n\\
      description,\\n\\
      url: `https://cannadealsfl.com/dispensaries/${params.slug}`\\,\\n\\
    }\\,\\n\\
    alternates: {\\n\\
      canonical: `https://cannadealsfl.com/dispensaries/${params.slug}`\\,\\n\\
    }\\,\\n\\
  }\\n\\
}\\n\\
' app/dispensaries/\\[slug\\]/page.tsx && echo '✅ Added generateMetadata to dispensary pages'"


/bin/bash: line 1: ${dispensary.name}: bad substitution
/bin/bash: line 1: Cannabis: command not found
/bin/bash: line 1: ${dispensary.name}: bad substitution
/bin/bash: line 1: ${dispensary.name.toLowerCase()}: bad substitution
/bin/bash: line 1: ${dispensary.name.toLowerCase()}: bad substitution
/bin/bash: line 1: ${dispensary.name.toLowerCase()}: bad substitution
/bin/bash: line 1: https://cannadealsfl.com/dispensaries/${params.slug}: bad substitution
/bin/bash: line 1: https://cannadealsfl.com/dispensaries/${params.slug}: bad substitution
Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
sed: can't read deals: No such file or directory
sed: can't read florida,\n\
      dispensary: No such file or directory
sed: can't read discounts: No such file or directory
sed: can't read florida\n\
    ]\,\n\
    openGraph: {\n\
      title,\n\
      description,\n\
      url: \,\n\
    }\,\n\
    alternates: {\n\
      canonical: \,\n\
    }\,\n\
  }\n\
}\n\
: No such file or directory

(Command exited with code 2)


Bash is interpreting the template literals. I need to write this to a file first and then insert it properly. Let me use a different approach.

write

import type { Metadata } from 'next'

export async function generateMetadata({ params }: DispensaryPageProps): Promise<Metadata> {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    return { title: 'Dispensary Not Found' }
  }

  const title = `${dispensary.name} Deals Florida | Cannabis Coupons & Discounts`
  const description = `Find the latest ${dispensary.name} deals and cannabis discounts in Florida. Browse ${dispensary.name} coupons, promo codes, and medical marijuana offers.`

  return {
    title,
    description,
    keywords: [
      `${dispensary.name.toLowerCase()} deals florida`,
      `${dispensary.name.toLowerCase()} coupons`,
      `${dispensary.name.toLowerCase()} dispensary`,
      'cannabis deals florida',
      'dispensary discounts florida'
    ],
    openGraph: {
      title,
      description,
      url: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
    alternates: {
      canonical: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
  }
}


Successfully wrote 1043 bytes to /home/crogers2287/.openclaw/agents/cannadeals-marketing/tmp/dispensary-metadata.ts


Let me just read the current dispensary page and create a complete new version with the metadata added.

exec

ssh carl "cd ~/cannadealsfl-dev && cat app/dispensaries/\\[slug\\]/page.tsx" > /tmp/dispensary-page-current.tsx 2>/dev/null && head -30 /tmp/dispensary-page-current.tsx


import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'

import type { Metadata } from 'next'

export async function generateMetadata({ params }: DispensaryPageProps): Promise<Metadata> {

  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {

    return { title: 'Dispensary Not Found' }

  }

  const title =


Looks like the file got partially corrupted. Let me restore it and try again with a cleaner approach.

ssh carl "cd ~/cannadealsfl-dev && git checkout app/dispensaries/\\[slug\\]/page.tsx && echo '✅ Restored dispensary page'"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
Updated 1 path from the index
✅ Restored dispensary page


Let me use a more reliable method - write the entire file locally and then copy it over.


import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'

interface DispensaryPageProps {
  params: {
    slug: string
  }
}

export default async function DispensaryPage({ params }: DispensaryPageProps) {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    notFound()
  }

  // Get deals for this dispensary
  const { deals } = await getDeals({ dispensary_id: dispensary.id }, { limit: 50 })

  return (
    <>
      <SEOStructuredData />
      <PageLayout showLogoBar={false}>

          {/* Dispensary Header */}

            {/* Mobile Layout */}

              {/* Logo and Title */}

                {dispensary.logo_url ? (
                  <img
                    src={dispensary.logo_url}
                    alt={`${dispensary.name} logo`}
                    className="w-16 h-16 object-contain rounded-lg bg-white p-2 border shadow-sm flex-shrink-0"
                  />
                ) : (

                    {dispensary.name.charAt(0)}

                )}

                  {dispensary.name}

              {/* Description - Structured Display */}
              {dispensary.description && (

                  {dispensary.description.split('•').map((item, index) => {
                    const trimmed = item.trim()
                    if (!trimmed) return null

                    // Parse discount items
                    const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                    if (match) {
                      const [, label, value] = match
                      return (

                            {label}

                            {value}

                      )
                    }

                    return (

                        {trimmed}

                    )
                  })}

              )}

              {/* Website Button */}
              {dispensary.website_url && (
                <Button asChild variant="default" className="w-full" size="lg">
                  <Link
                    href={dispensary.website_url}
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    <ExternalLink className="h-4 w-4 mr-2" />
                    Visit Website
                  </Link>
                </Button>
              )}

            {/* Desktop Layout */}

                {dispensary.logo_url ? (
                  <img
                    src={dispensary.logo_url}
                    alt={`${dispensary.name} logo`}
                    className="w-24 h-24 object-contain rounded-lg bg-white p-2 border shadow-sm"
                  />
                ) : (

                    {dispensary.name.charAt(0)}

                )}

                    {dispensary.name}

                  {dispensary.description && (

                      {dispensary.description.split('•').map((item, index) => {
                        const trimmed = item.trim()
                        if (!trimmed) return null

                        const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                        if (match) {
                          const [, label, value] = match
                          return (

                                {label}:

                                {value}

                          )
                        }

                        return (

                            {trimmed}

                        )
                      })}

                  )}

                {dispensary.website_url && (
                  <Button asChild variant="outline">
                    <Link
                      href={dispensary.website_url}
                      target="_blank"
                      rel="noopener noreferrer"
                    >
                      <ExternalLink className="h-4 w-4 mr-2" />
                      Visit Website
                    </Link>
                  </Button>
                )}

          {/* Deals Section */}

                  Current Deals

                  {deals.length} active {deals.length === 1 ? 'deal' : 'deals'} from {dispensary.name}

              <Badge variant="secondary" className="text-sm md:text-lg px-3 py-1 md:px-4 md:py-2 flex-shrink-0">
                {deals.length}
              </Badge>

            {deals.length > 0 ? (
              <DealGrid
                deals={deals}
                totalDeals={deals.length}
                currentPage={1}
                isLoading={false}
                hasMore={false}
              />
            ) : (

No Active Deals

                  {dispensary.name} doesn't have any active deals right now.

                <Button asChild>
                  <Link href="/deals">Browse All Deals</Link>
                </Button>

            )}

          {/* About Section - Hidden on mobile since info shown in header */}
          {dispensary.description && (

                About {dispensary.name}

                {dispensary.description.split('•').map((item, index) => {
                  const trimmed = item.trim()
                  if (!trimmed) return null

                  const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                  if (match) {
                    const [, label, value] = match
                    return (

                          {label}:

                          {value}

                    )
                  }

                  return (

                      {trimmed}

                  )
                })}

          )}

      </PageLayout>
    </>
  )
}


Now I have the full file. Let me create a version with generateMetadata added and push it to dev.

edit

import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'

interface DispensaryPageProps {
  params: {
    slug: string
  }
}

export default async function DispensaryPage({ params }: DispensaryPageProps) {

import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'
import type { Metadata } from 'next'

interface DispensaryPageProps {
  params: {
    slug: string
  }
}

// Generate unique metadata for each dispensary page
export async function generateMetadata({ params }: DispensaryPageProps): Promise<Metadata> {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    return { title: 'Dispensary Not Found' }
  }

  const title = `${dispensary.name} Deals Florida | Cannabis Coupons & Discounts`
  const description = `Find the latest ${dispensary.name} deals and cannabis discounts in Florida. Browse ${dispensary.name} coupons, promo codes, and medical marijuana offers.`

  return {
    title,
    description,
    keywords: [
      `${dispensary.name.toLowerCase()} deals florida`,
      `${dispensary.name.toLowerCase()} coupons`,
      `${dispensary.name.toLowerCase()} dispensary`,
      'cannabis deals florida',
      'dispensary discounts florida'
    ],
    openGraph: {
      title,
      description,
      url: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
    alternates: {
      canonical: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
  }
}

export default async function DispensaryPage({ params }: DispensaryPageProps) {


File not found: /home/crogers2287/cannadealsfl/app/dispensaries/[slug]/page.tsx


The file doesn't exist on this machine - I'm editing on fred but the repo is on carl. I need to do this differently. Let me write a patch file and apply it on carl.

write

import { notFound } from 'next/navigation'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { DealGrid } from '@/components/deals/deal-grid'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { getDispensaryBySlug, getDeals } from '@/lib/database'
import { ExternalLink } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import Link from 'next/link'
import type { Metadata } from 'next'

interface DispensaryPageProps {
  params: {
    slug: string
  }
}

// Generate unique metadata for each dispensary page
export async function generateMetadata({ params }: DispensaryPageProps): Promise<Metadata> {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    return { title: 'Dispensary Not Found' }
  }

  const title = `${dispensary.name} Deals Florida | Cannabis Coupons & Discounts`
  const description = `Find the latest ${dispensary.name} deals and cannabis discounts in Florida. Browse ${dispensary.name} coupons, promo codes, and medical marijuana offers.`

  return {
    title,
    description,
    keywords: [
      `${dispensary.name.toLowerCase()} deals florida`,
      `${dispensary.name.toLowerCase()} coupons`,
      `${dispensary.name.toLowerCase()} dispensary`,
      'cannabis deals florida',
      'dispensary discounts florida'
    ],
    openGraph: {
      title,
      description,
      url: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
    alternates: {
      canonical: `https://cannadealsfl.com/dispensaries/${params.slug}`,
    },
  }
}

export default async function DispensaryPage({ params }: DispensaryPageProps) {
  const dispensary = await getDispensaryBySlug(params.slug)

  if (!dispensary) {
    notFound()
  }

  // Get deals for this dispensary
  const { deals } = await getDeals({ dispensary_id: dispensary.id }, { limit: 50 })

  return (
    <>
      <SEOStructuredData />
      <PageLayout showLogoBar={false}>

          {/* Dispensary Header */}

            {/* Mobile Layout */}

              {/* Logo and Title */}

                {dispensary.logo_url ? (
                  <img
                    src={dispensary.logo_url}
                    alt={`${dispensary.name} logo`}
                    className="w-16 h-16 object-contain rounded-lg bg-white p-2 border shadow-sm flex-shrink-0"
                  />
                ) : (

                    {dispensary.name.charAt(0)}

                )}

                  {dispensary.name}

              {/* Description - Structured Display */}
              {dispensary.description && (

                  {dispensary.description.split('•').map((item, index) => {
                    const trimmed = item.trim()
                    if (!trimmed) return null

                    // Parse discount items
                    const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                    if (match) {
                      const [, label, value] = match
                      return (

                            {label}

                            {value}

                      )
                    }

                    return (

                        {trimmed}

                    )
                  })}

              )}

              {/* Website Button */}
              {dispensary.website_url && (
                <Button asChild variant="default" className="w-full" size="lg">
                  <Link
                    href={dispensary.website_url}
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    <ExternalLink className="h-4 w-4 mr-2" />
                    Visit Website
                  </Link>
                </Button>
              )}

            {/* Desktop Layout */}

                {dispensary.logo_url ? (
                  <img
                    src={dispensary.logo_url}
                    alt={`${dispensary.name} logo`}
                    className="w-24 h-24 object-contain rounded-lg bg-white p-2 border shadow-sm"
                  />
                ) : (

                    {dispensary.name.charAt(0)}

                )}

                    {dispensary.name}

                  {dispensary.description && (

                      {dispensary.description.split('•').map((item, index) => {
                        const trimmed = item.trim()
                        if (!trimmed) return null

                        const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                        if (match) {
                          const [, label, value] = match
                          return (

                                {label}:

                                {value}

                          )
                        }

                        return (

                            {trimmed}

                        )
                      })}

                  )}

                {dispensary.website_url && (
                  <Button asChild variant="outline">
                    <Link
                      href={dispensary.website_url}
                      target="_blank"
                      rel="noopener noreferrer"
                    >
                      <ExternalLink className="h-4 w-4 mr-2" />
                      Visit Website
                    </Link>
                  </Button>
                )}

          {/* Deals Section */}

                  Current Deals

                  {deals.length} active {deals.length === 1 ? 'deal' : 'deals'} from {dispensary.name}

              <Badge variant="secondary" className="text-sm md:text-lg px-3 py-1 md:px-4 md:py-2 flex-shrink-0">
                {deals.length}
              </Badge>

            {deals.length > 0 ? (
              <DealGrid
                deals={deals}
                totalDeals={deals.length}
                currentPage={1}
                isLoading={false}
                hasMore={false}
              />
            ) : (

No Active Deals

                  {dispensary.name} doesn't have any active deals right now.

                <Button asChild>
                  <Link href="/deals">Browse All Deals</Link>
                </Button>

            )}

          {/* About Section - Hidden on mobile since info shown in header */}
          {dispensary.description && (

                About {dispensary.name}

                {dispensary.description.split('•').map((item, index) => {
                  const trimmed = item.trim()
                  if (!trimmed) return null

                  const match = trimmed.match(/^([^:]+):\s*(.+)$/)
                  if (match) {
                    const [, label, value] = match
                    return (

                          {label}:

                          {value}

                    )
                  }

                  return (

                      {trimmed}

                  )
                })}

          )}

      </PageLayout>
    </>
  )
}


Successfully wrote 11299 bytes to /home/crogers2287/.openclaw/agents/cannadeals-marketing/tmp/dispensary-page-new.tsx


Now I need to copy this to carl and then build on dev. Let me do that.

exec

scp ~/.openclaw/agents/cannadeals-marketing/tmp/dispensary-page-new.tsx carl:~/cannadealsfl-dev/app/dispensaries/\\[slug\\]/page.tsx && echo '✅ Uploaded dispensary page with metadata'


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Uploaded dispensary page with metadata


Now let me verify the homepage and deals page also have the correct metadata, then build and test on dev.

ssh carl "cd ~/cannadealsfl-dev && head -40 app/page.tsx"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
import { Suspense } from 'react'
import { PageLayout } from '@/components/layout/page-layout'
import { Footer } from '@/components/layout/footer'
import { NewDealsHero } from '@/components/sections/new-deals-hero'
import { FeaturedDeals } from '@/components/sections/featured-deals'
import { DispensarySpotlight } from '@/components/sections/dispensary-spotlight'
import NewsletterCTABanner from '@/components/newsletter/newsletter-cta-banner'
import { RecentDeals } from '@/components/sections/recent-deals'
import { SEOStructuredData } from '@/components/seo/structured-data'
import { SettingSunLogo } from '@/components/effects/setting-sun-logo'
import { getDeals, getDispensaries } from '@/lib/database'

export const dynamic = 'force-dynamic' // Force dynamic rendering to avoid build-time database calls
nimport type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts',
    description: 'Find the best cannabis deals in Florida. Daily updates on medical marijuana discounts, dispensary coupons, and weed deals from top Florida dispensaries like Trulieve, MUV, and Curaleaf.',
    alternates: {
      canonical: 'https://cannadealsfl.com',
    },
  }
}

export const revalidate = 60 // Revalidate every 60 seconds

export default async function HomePage() {
  // Fetch data for the homepage - with limits to prevent timeouts
  let recentDeals = []
  let dispensaries = []

  try {
    // Get timestamp for 7 days ago (extended from 24 hours to ensure deals are visible)
    const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()

    const results = await Promise.all([
      // Get deals from last 7 days
      getDeals({ created_after: sevenDaysAgo }, { limit: 200 }),
      getDispensaries({ limit: 60 })


There's a small issue - the sed command added a stray 'n' at the beginning of the line. Let me fix that.

ssh carl "cd ~/cannadealsfl-dev && sed -i 's/^nimport/import/' app/page.tsx && echo '✅ Fixed typo in homepage'"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Fixed typo in homepage


ssh carl "cd ~/cannadealsfl-dev && npm run build 2>&1 | tail -50"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.

> cannadealsfl@0.1.0 build
> next build

  ▲ Next.js 14.2.33
  - Environments: .env.local
  - Experiments (use with caution):
    · instrumentationHook

   Creating an optimized production build ...

> Build error occurred
NormalizeError: Requested and resolved page mismatch: /dispensaries//[slug/]/page /dispensaries/[slug/]/page
    at normalizePagePath (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/shared/lib/page-path/normalize-page-path.js:20:19)
    at /home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/entries.js:398:73
    at Array.map (<anonymous>)
    at createEntrypoints (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/entries.js:544:57)
    at /home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/webpack-build/impl.js:104:126
    at Span.traceAsyncFn (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/trace/trace.js:154:26)
    at webpackBuildImpl (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/webpack-build/impl.js:104:78)
    at webpackBuild (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/webpack-build/index.js:155:16)
    at /home/crogers2287/cannadealsfl-dev/node_modules/next/dist/build/index.js:1006:148
    at async Span.traceAsyncFn (/home/crogers2287/cannadealsfl-dev/node_modules/next/dist/trace/trace.js:154:20)


The scp command messed up the path. The `[slug]` directory was interpreted by bash. I need to escape it properly.

ssh carl "cd ~/cannadealsfl-dev && ls -la app/dispensaries/"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
total 24
drwxr-xr-x  4 crogers2287 crogers2287 4096 Feb 28 16:41 .
drwxr-xr-x 37 crogers2287 crogers2287 4096 Mar  8 06:15 ..
drwxr-xr-x  2 crogers2287 crogers2287 4096 Mar  8 06:14 [slug]
drwxr-xr-x  2 crogers2287 crogers2287 4096 Mar  8 06:15 \[slug\]
-rw-r--r--  1 crogers2287 crogers2287 4743 Feb 28 16:41 page.tsx


There are now two directories - `[slug]` and `\[slug\]`. I need to clean this up and restore the correct structure.

ssh carl "cd ~/cannadealsfl-dev && rm -rf 'app/dispensaries/[slug]' 'app/dispensaries/\\[slug\\]' && mkdir -p 'app/dispensaries/[slug]'"

scp ~/.openclaw/agents/cannadeals-marketing/tmp/dispensary-page-new.tsx 'carl:~/cannadealsfl-dev/app/dispensaries/[slug]/page.tsx' && echo '✅ Uploaded to correct path'


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
✅ Uploaded to correct path


Good. Now let me verify the structure is correct and build again.

ssh carl "cd ~/cannadealsfl-dev && ls -la app/dispensaries/ && npm run build 2>&1 | tail -40"


Command still running (session lucky-mist, pid 3585967). Use process (list/poll/log/write/kill/clear/remove) for follow-up.

Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
total 20
drwxr-xr-x  3 crogers2287 crogers2287 4096 Mar  8 06:16 .
drwxr-xr-x 37 crogers2287 crogers2287 4096 Mar  8 06:15 ..
drwxr-xr-x  2 crogers2287 crogers2287 4096 Mar  8 06:16 [slug]
-rw-r--r--  1 crogers2287 crogers2287 4743 Feb 28 16:41 page.tsx


The build is still running. Let me poll it.


total 20
drwxr-xr-x  3 crogers2287 crogers2287 4096 Mar  8 06:16 .
drwxr-xr-x 37 crogers2287 crogers2287 4096 Mar  8 06:15 ..
drwxr-xr-x  2 crogers2287 crogers2287 4096 Mar  8 06:16 [slug]
-rw-r--r--  1 crogers2287 crogers2287 4743 Feb 28 16:41 page.tsx
├ ƒ /dispensaries                                    202 B           377 kB
├ ƒ /dispensaries/[slug]                             201 B           377 kB
├ ○ /dispensary-deals-near-me                        145 B           377 kB
├ ○ /florida-dispensary-deals                        145 B           377 kB
├ ○ /florida-dispensary-discounts                    145 B           377 kB
├ ƒ /guides/best-dispensary-in-florida-for-flower    144 B           377 kB
├ ƒ /guides/florida-dispensary-first-time-discounts  144 B           377 kB
├ ○ /how-to-get-florida-mmj-card                     145 B           377 kB
├ ○ /jacksonville-dispensary-deals                   146 B           377 kB
├ ● /locations/[city]                                144 B           377 kB
├   ├ /locations/miami
├   ├ /locations/orlando
├   ├ /locations/tampa
├   └ [+9 more paths]
├ ○ /miami-dispensary-deals                          144 B           377 kB
├ ○ /mmj-deals-florida                               145 B           377 kB
├ ○ /newsletter                                      2.99 kB         380 kB
├ ○ /orlando-dispensary-deals                        145 B           377 kB
├ ○ /pricing                                         3.65 kB         381 kB
├ ○ /privacy                                         143 B           377 kB
├ ○ /robots.txt                                      0 B                0 B
├ ○ /search                                          2.65 kB         380 kB
├ ○ /sitemap.xml                                     0 B                0 B
├ ○ /tampa-dispensary-deals                          145 B           377 kB
├ ○ /terms                                           144 B           377 kB
├ ○ /test                                            136 B           339 kB
├ ○ /test-mobile-nav                                 144 B           377 kB
├ ○ /tools/thc-calculator                            181 B           377 kB
└ ○ /weed-deals-florida                              145 B           377 kB
+ First Load JS shared by all                        312 kB
  └ chunks/vendors-4b1bba3aea59ca74.js               310 kB
  └ other shared chunks (total)                      1.97 kB

ƒ Middleware                                         27.3 kB

○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses getStaticProps)
ƒ  (Dynamic)  server-rendered on demand
Warning: Permanently added 'carl' (ED25519) to the list of known hosts.

Process exited with code 0.

ssh carl

Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
total 20
drwxr-xr-x  3 crogers2287 crogers2287 4096 Mar  8 06:16 .
drwxr-xr-x 37 crogers2287 crogers2287 4096 Mar  8 06:15 ..
drwxr-xr-x  2 crogers2287 crogers2287 4096 Mar  8 06:16 [slug]
-rw-r--r--  1 crogers2287 crogers2287 4743 Feb 28 16:41 page.tsx
├ ƒ /dispensaries                                    202 B           377 kB
├ ƒ /dispensaries/[slug]                             201 B           377 kB
├ ○ /dispensary-deals-near-me                        145 B           377 kB
├ ○ /florida-dispensary-deals                        145 B           377 kB
├ ○ /florida-dispensary-discounts                    145 B           377 kB
├ ƒ /guides/best-dispensary-in-florida-for-flower    144 B           377 kB
├ ƒ /guides/florida-dispensary-first-time-discounts  144 B           377 kB
├ ○ /how-to-get-florida-mmj-card                     145 B           377 kB
├ ○ /jacksonville-dispensary-deals                   146 B           377 kB
├ ● /locations/[city]                                144 B           377 kB
├   ├ /locations/miami
├   ├ /locations/orlando
├   ├ /locations/tampa
├   └ [+9 more paths]
├ ○ /miami-dispensary-deals                          144 B           377 kB
├ ○ /mmj-deals-florida                               145 B           377 kB
├ ○ /newsletter                                      2.99 kB         380 kB
├ ○ /orlando-dispensary-deals                        145 B           377 kB
├ ○ /pricing                                         3.65 kB         381 kB
├ ○ /privacy                                         143 B           377 kB
├ ○ /robots.txt                                      0 B                0 B
├ ○ /search                                          2.65 kB         380 kB
├ ○ /sitemap.xml                                     0 B                0 B
├ ○ /tampa-dispensary-deals                          145 B           377 kB
├ ○ /terms                                           144 B           377 kB
├ ○ /test                                            136 B           339 kB
├ ○ /test-mobile-nav                                 144 B           377 kB
├ ○ /tools/thc-calculator                            181 B           377 kB
└ ○ /weed-deals-florida                              145 B           377 kB
+ First Load JS shared by all                        312 kB
  └ chunks/vendors-4b1bba3aea59ca74.js               310 kB
  └ other shared chunks (total)                      1.97 kB

ƒ Middleware                                         27.3 kB

○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses getStaticProps)
ƒ  (Dynamic)  server-rendered on demand


Build succeeded. Now let me restart the dev server and verify the metadata changes are working.

exec

ssh carl "pm2 restart cannadeals-dev && sleep 3 && pm2 logs cannadeals-dev --lines 5 --nostream"


Warning: Permanently added 'carl' (ED25519) to the list of known hosts.
Use --update-env to update environment variables
[PM2] Applying action restartProcessId on app [cannadeals-dev](ids: [ 6 ])
[PM2] [cannadeals-dev](6) ✓
┌────┬─────────────────────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name                            │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼─────────────────────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 6  │ cannadeals-dev                  │ default     │ N/A     │ fork    │ 1175196  │ 0s     │ 21   │ online    │ 0%       │ 24.9mb   │ crogers… │ disabled │
│ 5  │ cannadeals-news-blog            │ default     │ N/A     │ fork    │ 0        │ 0      │ 0    │ stopped   │ 0%       │ 0b       │ crogers… │ disabled │
│ 2  │ cannadeals-newsletter-daily     │ default     │ N/A     │ fork    │ 0        │ 0      │ 0    │ stopped   │ 0%       │ 0b       │ crogers… │ disabled │
│ 3  │ cannadeals-newsletter-weekly    │ default     │ N/A     │ fork    │ 0        │ 0      │ 0    │ stopped   │ 0%       │ 0b       │ crogers… │ disabled │
│ 4  │ cannadeals-scraper              │ default     │ N/A     │ fork    │ 0        │ 0      │ 0    │ stopped   │ 0%       │ 0b       │ crogers… │ disabled │
│ 1  │ cannadeals-web                  │ default     │ N/A     │ fork    │ 442816   │ 15h    │ 111  │ online    │ 0%       │ 63.4mb   │ crogers… │ disabled │
└────┴─────────────────────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
[TAILING] Tailing last 5 lines for [cannadeals-dev] process (change the value with --lines option)
/home/crogers2287/.pm2/logs/cannadeals-dev-error-6.log last 5 lines:
6|cannadea |   details: 'The result contains 0 rows',
6|cannadea |   hint: null,
6|cannadea |   message: 'JSON object requested, multiple (or no) rows returned'
6|cannadea | }
6|cannadea |  ⚠ The "images.domains" configuration is deprecated. Please use "images.remotePatterns" configuration instead.

/home/crogers2287/.pm2/logs/cannadeals-dev-out-6.log last 5 lines:
6|cannadea |
6|cannadea |  ✓ Starting...
6|cannadea | [Instrumentation] Scraper scheduler auto-start is DISABLED (prevented infinite loop)
6|cannadea | [Instrumentation] To start scheduler, use admin dashboard or PM2 cron
6|cannadea |  ✓ Ready in 803ms


Dev is running. Now let me verify the new metadata is being served correctly.

curl -s https://dev.cannadealsfl.com/ | grep -oE '<title>[^<]+</title>'

curl -s https://dev.cannadealsfl.com/deals | grep -oE '<title>[^<]+</title>'

curl -s https://dev.cannadealsfl.com/dispensaries/cannabist | grep -oE '<title>[^<]+</title>'


<title>Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts</title>


<title>Cannabis Deals Florida Today | Weed Deals & MMJ Discounts | CannaDealsFL</title>


<title>Cannabist Deals Florida | Cannabis Coupons & Discounts | CannaDealsFL</title>


PERFECT! Now each page has a UNIQUE title:

- Homepage: "Cannabis Deals Florida | Florida Weed Deals & MMJ Discounts"
- Deals page: "Cannabis Deals Florida Today | Weed Deals & MMJ Discounts | CannaDealsFL"
- Cannabist page: "Cannabist Deals Florida | Cannabis Coupons & Discounts | CannaDealsFL"

This is a HUGE improvement. The keyword cannibalization is fixed. Now I need to:

1. Tell Chad it's ready on dev
2. Wait for approval
3. Deploy to prod

Let me also check what else I might have missed in the SEO audit. I should think outside the box:

1. **Canonical URLs** - I added them to the metadata, but let me verify they're in the HTML
2. **Internal linking** - Are we linking to the right pages with good anchor text?
3. **Competitor analysis** - What else is flcannabisdeals.org doing?
4. **Page speed** - Could be a factor
5. **Schema markup** - Is it correct?
6. **H1 tags** - Do they match the page focus?
7. **Content length** - Are our pages too thin?
8. **Backlinks** - Do we have any?

Let me check canonicals and a few other things.

**FIXED.** Each page now has a UNIQUE title:

| Page | Old Title | New Title |
|------|-----------|-----------|
| Homepage | "Florida Dispensary Deals Today" | "Cannabis Deals Florida \| Florida Weed Deals & MMJ Discounts" |
| Deals | "Florida Dispensary Deals Today" | "Cannabis Deals Florida Today \| Weed Deals & MMJ Discounts" |
| Cannabist | "Florida Dispensary Deals Today" | "Cannabist Deals Florida \| Cannabis Coupons & Discounts" |

The keyword **"cannabis deals florida"** is now in titles where it matters.

**Ready on dev:** https://dev.cannadealsfl.com

Let me also check a few more SEO things while I'm at it:

curl -s https://dev.cannadealsfl.com/deals | grep -i 'canonical\|<h1' | head -5

curl -s https://dev.cannadealsfl.com/ | grep -i 'canonical\|<h1' | head -5

curl -s https://dev.cannadealsfl.com/dispensaries/cannabist | grep -i 'canonical\|<h1' | head -5