```html

Injecting Structured Data Into Concert Event Pages: A Multi-Site Schema Strategy

Over the past development session, we tackled a critical SEO gap across the JADA event portfolio: the absence of JSON-LD structured data on 12 active concert event pages. This post covers the technical approach to solving this at scale, the infrastructure decisions that shaped the solution, and the deployment pipeline we built to keep everything in sync across multiple CloudFront distributions.

The Problem: Zero Structured Data on Revenue-Critical Pages

Concert event pages are high-intent landing pages that should be visible to search engines, event aggregators, and rich snippet processors. Without structured data, these pages were losing:

  • Rich snippets in Google Search results (no event date, venue, or ticket link preview)
  • Schema validation from tools like Google's Rich Results Test
  • Event aggregator indexing (Google Events, Songkick, Bandsintown, etc.)
  • Local business context linking events back to the venue and JADA brand

A quick audit confirmed: none of the 12 active Rady Shell event subdomain pages (paulsimonradyshell.com, thewhoradyshell.com, etc.) contained any Event or LocalBusiness JSON-LD markup.

Solution Architecture: Programmatic Injection + Multi-Bucket Deployment

Rather than manually editing each HTML file, we built a reusable injection script that could be run against any event page and deployed consistently across all buckets.

Step 1: Build the Injection Script

Created /Users/cb/Documents/repos/tools/inject_structured_data.py with the following logic:

  • Parse input HTML to locate the closing </head> tag
  • Generate Event schema with event name, date, time, location, and organizer
  • Generate LocalBusiness schema linking to the Rady Shell venue and JADA booking
  • Insert both schemas as separate <script type="application/ld+json"> blocks before </head>
  • Validate output to ensure no duplicate injections or malformed JSON

The script reads metadata from each event page's title, date elements, and embedded ticket information, then constructs RFC 3339 formatted timestamps and proper schema.org structures.

Step 2: Identify and Map All Event Pages

We discovered 12 active event pages across the Rady Shell event subdomains:

  • paulsimonradyshell.com
  • thewhoradyshell.com
  • greggallupradyshell.com
  • And 9 others in the same pattern

Each subdomain has its own S3 bucket and CloudFront distribution. This is critical infrastructure to understand: a single update requires coordinating uploads and cache invalidations across multiple endpoints.

Step 3: Run Injection Across All 12 Pages

python inject_structured_data.py \
  --input-dir /path/to/event/pages \
  --output-dir /path/to/processed/pages \
  --schema-type event+localbusiness \
  --validate-json

All 12 pages were processed successfully. The script generated valid, testable JSON-LD that passes Google's Rich Results validator.

Infrastructure: S3 + CloudFront Deployment Pipeline

Finding the Right Buckets

Each event subdomain points to a dedicated S3 bucket. We identified them by:

  1. Checking Route53 DNS records for each subdomain
  2. Finding the CNAME alias to CloudFront distribution
  3. Locating the S3 bucket origin in each distribution's configuration

Example bucket naming pattern:

  • sailjada-event-paulsimon-radyshell
  • sailjada-event-thewho-radyshell
  • sailjada-event-greggallup-radyshell

Deployment: Upload and Invalidate

For each of the 12 event pages, we executed:

aws s3 cp updated-event-page.html \
  s3://sailjada-event-{artist}-radyshell/index.html \
  --cache-control "public, max-age=3600"

Then invalidated the CloudFront cache using the distribution ID (e.g., E2ABCD1234XYZ for paulsimonradyshell.com):

aws cloudfront create-invalidation \
  --distribution-id E2ABCD1234XYZ \
  --paths "/*"

Why full-path invalidation? Event pages have long cache TTLs to reduce origin load, but structured data changes need immediate visibility to search crawlers and schema validators. A full /* invalidation ensures Google's crawlers see the new markup within minutes.

Additional Updates: paulsimonradyshell.com and Beyond

We also deployed the updated Paul Simon event page to two separate buckets—paulsimonradyshell.com and a subdomain under sailjada.com—to support both dedicated and integrated marketing flows. This required coordinating uploads and cache invalidations across two separate CloudFront distributions.

Key Technical Decisions and Rationale

Why JSON-LD Over Microdata or RDFa?

JSON-LD is Google's preferred format for structured data and is easier to inject into existing HTML without modifying the DOM. It's also easier to validate, parse, and debug.

Why Two Separate Schema Objects?

Event schema alone tells search engines what is happening, but LocalBusiness schema provides where and who—critical context for local search ranking. Combining both gives search engines full semantic understanding and enables event pages to rank in multiple contexts (event search + local venue search).

Why Full CloudFront Invalidation?

Partial invalidations (e.g., /index.html only) would miss edge cases where schema is embedded across multiple page templates or dynamically rendered sections. Full invalidation is safer and faster to implement at scale.

What's Next

  • Rich Results Monitoring: Set up Google Search Console monitoring for each event subdomain to track schema validation and rich snippet impressions
  • Event Aggregator Integration: Submit event schemas to Google Events, Songkick, and other platforms; validate indexing
  • Automation: Integrate the injection script into the render_event_sites.py build pipeline so future event pages get structured data automatically
  • Testing: Add schema validation to CI/CD before deployment to prevent malformed JSON from reaching production

This was a focused infrastructure win: solving a real SEO gap