# Canonical Tags & Tracking Parameter Preservation

**Last Updated:** 2026-01-20

## Critical: Tracking Parameters Are NOT Redirected

**IMPORTANT:** We do NOT redirect URLs with tracking parameters (UTM, partner, leadSource, etc.) because:

1. **JavaScript needs URL parameters** - The UTM tracking script (`v2/js/utm-tracking.js`) reads parameters from `window.location.search` on page load
2. **Cookies are set from URL** - Parameters are stored in cookies for 30 days for multi-page session tracking
3. **HubSpot forms use cookies** - Form submissions read tracking data from cookies, not URL
4. **Campaign attribution** - Ad campaigns, partner links, and lead sources rely on URL parameters

## How It Works

### 1. User Visits URL with Tracking Parameters

Example: `https://www.ordio.com/schichtplan?utm_source=google&utm_campaign=summer-sale`

- **Page loads normally** (no redirect)
- **JavaScript reads parameters** from `window.location.search`
- **Parameters stored in cookies** (90-day expiration)
- **Parameters also stored in localStorage** (fallback if cookies fail)
- **After 1.5 seconds:** UTM parameters cleaned from URL (prevents link sharing misattribution)
- **hsa\_\* parameters preserved** in URL (needed for Google Ads conversion tracking)
- **Canonical tag points to clean URL**: `https://www.ordio.com/schichtplan`

### 2. Google Sees Canonical Tag

- Google crawls: `/schichtplan?utm_source=google&utm_campaign=summer-sale`
- Sees canonical tag: `<link rel="canonical" href="https://www.ordio.com/schichtplan">`
- **Does NOT index** the parameterized URL
- Marks as "Alternate page with proper canonical tag" in GSC (this is EXPECTED and correct)

### 3. User Submits Form

- HubSpot form reads tracking data from cookies (set in step 1)
- Falls back to localStorage if cookies unavailable
- All UTM parameters sent to HubSpot
- All `hsa_*` parameters sent to HubSpot
- Campaign attribution preserved
- Analytics tracking works correctly

## What Gets Redirected

We ONLY redirect parameters that create duplicate content issues:

### ✅ Redirected (Non-Tracking Parameters)

1. **`search=` parameter**
   - `/tools?search=test` → `/tools` (301 redirect)
   - `/alternativen?search=test` → `/alternativen` (301 redirect)
   - **Reason:** Creates infinite URL variations, not meant to be indexed

2. **`p=monthly/yearly` parameter** (pricing page)
   - `/preise?p=monthly` → `/preise` (301 redirect)
   - `/preise?p=yearly` → `/preise` (301 redirect)
   - **Note:** If UTM parameters are also present, they are preserved
   - **Reason:** Functional parameter, but we want clean URL indexed

3. **RSS feed URLs** (legacy WordPress)
   - `/insights/author/{author}/feed/` → `/insights/` (301 redirect)
   - **Reason:** Legacy feed URLs, not tracking parameters

### ❌ NOT Redirected (Tracking Parameters)

These parameters are preserved for tracking:

- `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`
- `gclid`, `fbclid` (Google/Facebook click IDs)
- `hsa_*` (Google Ads parameters)
- `partner`, `leadSource`, `signuptype`
- `trk` (LinkedIn tracking)
- `source` (internal tracking)
- `ref` (referral tracking)

## Why "Alternate Page" Status is Expected

When Google Search Console shows "Alternate page with proper canonical tag" for URLs with tracking parameters, this is **correct behavior**:

1. ✅ Canonical tag is working correctly
2. ✅ Google is not indexing duplicate URLs
3. ✅ Tracking parameters are preserved for analytics
4. ✅ Campaign attribution continues to work

**This is NOT an error** - it means the canonicalization is working as intended.

## URL Cleanup Behavior

**IMPORTANT:** UTM parameters are intentionally cleaned from the URL after tracking to prevent incorrect attribution from link sharing.

### What Gets Cleaned

After 1.5 seconds (to ensure all tracking scripts have loaded), the following parameters are removed from the URL:

- **UTM Parameters:** `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`
- **Google Click ID:** `gclid`
- **Internal Tracking:** `leadSource`, `lead_source`, `partner`, `signuptype`

### What Gets Preserved

The following parameters remain in the URL (not cleaned):

- **Google Ads Conversion Tracking:** `hsa_*` parameters (hsa_acc, hsa_cam, hsa_grp, hsa_ad, hsa_src, hsa_tgt, hsa_kw, hsa_mt, hsa_net, hsa_ver)
- **Google Ads Additional Tracking:** `gad_source`, `gad_campaignid`, `gbraid`

**Why Preserve hsa\_\* Parameters:**

- Required for Google Ads conversion tracking
- Session-specific (don't cause misattribution when sharing)
- Google Ads best practices recommend preserving them

### How Tracking Works After Cleanup

1. **Parameters Stored in Cookies** (90-day expiration)
   - All UTM parameters stored in cookies
   - All `hsa_*` parameters stored in cookies
   - `gclid` stored in cookies
   - Also stored in `localStorage` as fallback

2. **Forms Read from Cookies**
   - Form submissions read UTM data from cookies
   - HubSpot receives complete tracking data
   - Campaign attribution preserved

3. **Cross-Page Tracking**
   - Cookies persist across page navigations
   - Forms on any page can read UTM data
   - 90-day cookie expiration ensures long-term tracking

## Testing Tracking Preservation

### Test UTM Parameters

1. Visit: `https://www.ordio.com/schichtplan?utm_source=test&utm_campaign=test&utm_debug=true`
2. Open browser console (debug mode enabled)
3. Wait 1.5 seconds for cleanup
4. Check cookies: `document.cookie` should contain `utm_source=test`
5. Verify URL cleaned: URL should not contain `utm_source` or `utm_campaign`
6. Check localStorage: `localStorage.getItem('ordio_utm_data')` should contain UTM data
7. Check canonical tag: Should point to `/schichtplan` (clean URL)

### Test hsa\_\* Parameter Preservation

1. Visit: `https://www.ordio.com/schichtbetriebe?hsa_acc=1887350035&hsa_cam=20035390960&hsa_src=g&utm_debug=true`
2. Wait 1.5 seconds for cleanup
3. Verify `hsa_*` parameters still in URL (should be preserved)
4. Check cookies: `document.cookie` should contain `hsa_acc`, `hsa_cam`, etc.

### Test HubSpot Form Submission

1. Visit URL with UTM parameters: `?utm_source=test&utm_campaign=test&utm_debug=true`
2. Wait for cookies to be set (check console)
3. Submit a form on the page
4. Check Network tab: Form submission should include UTM parameters
5. Check HubSpot contact record: Verify UTM parameters are captured
6. Verify `hsa_*` parameters are also sent to HubSpot

### Test Campaign Links

1. Create ad campaign link: `/schichtplan?utm_source=google&utm_campaign=summer&utm_debug=true`
2. Click link
3. Verify:
   - Page loads (no redirect)
   - Parameters visible in URL initially
   - Cookies set correctly (check console)
   - URL cleaned after 1.5 seconds
   - Cookies persist for form submission
   - Analytics receives tracking data

## Implementation Details

### Canonical Tag Generation

**Location:** `v2/base/head.php` (lines 60-74)

```php
// Automatically strips query parameters from canonical URL
$canonical_url = 'https://www.ordio.com' . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
```

This ensures:

- Canonical URL is always clean (no parameters)
- Page can still have parameters in actual URL
- Google sees canonical pointing to clean version

### Redirect Rules

**Location:** `.htaccess` (lines 140-165)

Only redirects:

- `search=` parameter
- `p=monthly/yearly` (with UTM preservation)
- Legacy RSS feed URLs

**Does NOT redirect:**

- Any UTM parameters
- Partner/leadSource parameters
- Any campaign tracking parameters

## Related Documentation

- [Canonical Tags Best Practices](./CANONICAL_TAGS_BEST_PRACTICES.md)
- [UTM Tracking System](../../systems/shared-components/UTM_TRACKING_SYSTEM.md)
- [Canonical Tags Fix Summary](./CANONICAL_TAGS_FIX_SUMMARY.md)
