```html

Injecting Structured Data Into 12 Concert Event Pages: Automating Schema.org JSON-LD Deployment Across Multi-Site Infrastructure

What Was Done

Over a single development session, we identified that zero structured data existed across 12 active concert event pages spanning 6 CloudFront-distributed subdomains. We built an automated injection pipeline that:

  • Wrote a Python script (inject_structured_data.py) to parse HTML and inject Event + LocalBusiness JSON-LD schemas
  • Applied the script to all active event pages across the Rady Shell event subdomain network
  • Deployed updated pages to S3 buckets for each event subdomain
  • Invalidated CloudFront caches to ensure immediate propagation
  • Established a repeatable pattern for future event pages

This work was part of a larger CMO initiative to close the visibility gap across JADA's multi-site presence: 3,676 email contacts, 157 reviews, and a beautiful brand footprint are generating minimal inbound traffic due to zero structured data, Google Business Profile misconfiguration, and dormant social channels.

Technical Details: The Structured Data Injection Script

The core script, /Users/cb/Documents/repos/tools/inject_structured_data.py, was designed to be idempotent and non-destructive. It performs three operations:

1. Parse HTML and detect existing structured data (skip if present)
2. Build Event + LocalBusiness JSON-LD payloads from page metadata
3. Inject into <head> before the closing tag

Why this approach? Concert pages live in multiple code repositories with different build systems. Rather than modifying 6 separate build pipelines or templates, we built a post-deployment injection layer. This is a common pattern when retrofitting SEO infrastructure into existing multi-site deployments.

The script targets two schema types:

  • Event schema: Captures concert name, date, time, location, performer, and ticket URL. Google uses this for rich event cards in search results and knowledge panels.
  • LocalBusiness schema: Reinforces venue location (The Rady Shell at Jacobs Park), phone, address, and business hours. Helps Google associate events with the physical location.

The injection point is always </head>, placed after any existing meta tags but before the closing head tag. This ensures:

  • Google's parser encounters structured data before rendering the page
  • No conflicts with existing analytics or tracking scripts
  • Validates cleanly against Schema.org specification

Infrastructure: S3 Buckets and CloudFront Distribution Map

The Rady Shell event subdomain network spans 6 independent S3+CloudFront deployments, each with its own bucket and distribution ID. The pattern is:

Subdomain: [event-name].queenofsandiego.com
S3 Bucket: [event-name].queenofsandiego.com
CloudFront Distribution: E2[...] (varies by event)

We identified the following active event subdomains requiring updates:

  • paulsimonradyshell.queenofsandiego.com
  • bobdylanradyshell.queenofsandiego.com
  • fleetmacradyshell.queenofsandiego.com
  • steely-dan-rady-shell.queenofsandiego.com
  • Plus 2 additional event subdomains

Each subdomain bucket contains a static site with an index.html serving as the primary event page. The injection script targeted these index files plus any secondary pages (ticketing, venue details, artist bios).

Why S3 + CloudFront for event pages? These are static, high-traffic pages that need:

  • Geographic distribution: CloudFront edge locations serve cached pages from 200+ global POPs, reducing latency for ticket-buyers searching from anywhere
  • Cost efficiency: S3 + CloudFront is ~90% cheaper than EC2-based hosting for static content
  • Cache control: CloudFront's 24-hour default TTL ensures schema updates propagate predictably
  • SEO stability: Static content = zero runtime errors, zero downtime, perfect crawlability

Deployment and Cache Invalidation

After running the injection script locally and validating output, we synced updated files using the AWS CLI pattern:

aws s3 sync ./updated-pages/ s3://[event-name].queenofsandiego.com/ --delete

The --delete flag ensures orphaned files are removed; since we're only updating index files, this is safe.

Immediately after each sync, we invalidated the CloudFront cache:

aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/*"

Why full-path invalidation? We could have invalidated only /index.html, but concert pages often reference secondary assets (social images, structured data in secondary pages). A full cache invalidation costs the same as a partial one (CloudFront charges per invalidation request, not per path) and eliminates edge-case stale-cache issues.

Invalidation status was monitored using:

aws cloudfront get-invalidation --distribution-id [DIST_ID] --id [INVALIDATION_ID]

CloudFront invalidations typically propagate globally within 2–5 minutes. We waited for all 6 distributions to report Completed status before declaring the deployment done.

Key Decisions and Trade-offs

Decision 1: Why not modify source templates? The event pages are built from 6 different source repositories, some using static site generators, others hand-authored HTML. Coordinating template changes across 6 repos introduces merge-conflict risk and slows future event deployments. The injection-after-build approach is slower for initial setup but faster for operations.

Decision 2: Why Event + LocalBusiness schemas, not Organization? Google's search algorithm has matured significantly for Event schema. A concert page with Event + LocalBusiness signals:

  • What is being sold (Event)
  • Where it's happening (LocalBusiness venue details)
  • Who's running the venue (optional Organization)

This triple-signal improves click-through rate on Google search and enables rich snippets in the Google Events portal.

Decision 3: JSON-LD vs. Microdata? We chose JSON-LD because:

  • It's Google's preferred format (stated in their structured data docs)
  • It doesn't pollute HTML class/attribute syntax
  • It's easier to inject post-build without parsing/modifying the DOM
  • It's more maintainable in version control (pure JSON vs. scattered attributes)

What's Next

This structured data injection establishes a repeatable pattern for future event pages. The next priorities in the CMO