Injecting Structured Data into Concert Event Pages and Deploying via CloudFront
During this development session, we identified a critical SEO gap: 12 active concert event pages across multiple subdomains were missing JSON-LD structured data markup. This meant search engines couldn't understand event details, dates, venues, or pricing—losing significant opportunities for rich snippets and improved organic visibility. Here's how we diagnosed the problem, built an automated injection system, and deployed the fixes across our multi-site infrastructure.
The Problem: Missing Event Schema
Our event pages lived across several subdomains under the queenofsandiego.com domain structure:
paulsimonradyshell.com(Paul Simon concert)sailjada.queenofsandiego.com/ranch-and-coast.html(Ranch & Coast event)- Multiple other event subdomains in
/Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/
A quick audit using our event page renderer script confirmed: zero pages had Event or LocalBusiness schema markup. The pages were rendering beautifully from HTML templates, but they were invisible to Google's structured data parser. This meant:
- No rich event snippets in search results
- No date/time/price information extraction for Knowledge Graph
- No enhanced appearance in Google's event carousel
- Missed opportunities for better CTR and booking funnel visibility
Solution Architecture: Automated Structured Data Injection
Rather than manually editing 12+ HTML files, we built a reusable injection script that could be applied to all event pages and integrated into future deployment pipelines.
The Injection Script
Created at: /Users/cb/Documents/repos/tools/inject_structured_data.py
This Python utility performs the following operations:
- Scans target HTML files for the
<head>tag - Extracts event metadata from page title, content, and filename conventions
- Generates Event + LocalBusiness JSON-LD blocks matching schema.org specs
- Inserts structured data before the closing
</head>tag - Validates placement to ensure no duplicate injection on re-runs
The script processes pages in batch mode, targeting specific directories and file patterns. Rather than hardcoding page-specific data, it extracts common fields from the HTML structure itself—event name from the page title, dates from visible content markers, and venue information from footer or content blocks.
Why This Approach?
We chose injection over template refactoring for several reasons:
- Speed: Event pages already exist and render correctly; no need to rebuild templates
- Non-invasive: Adds structured data without touching the visual HTML structure
- Reusable: The same script can run on new event pages as they're created
- Rollback-safe: Structured data blocks are isolated and can be removed without breaking layout
- Pipeline-ready: Can be integrated into the build process for
render_event_sites.py
Deployment Infrastructure: S3 + CloudFront
Our event pages are hosted on static S3 buckets behind CloudFront distributions for caching and performance:
S3 Bucket Locations
Updated pages were synced to the following buckets:
paulsimonradyshell.com-bucket(Paul Simon concert pages)sailjada.queenofsandiego.com-bucket(JADA branded events)- Event-specific buckets for each Rady Shell concert subdomain
Sync command pattern (with appropriate bucket names and credentials handled via AWS CLI configuration):
aws s3 sync /path/to/updated/pages/ s3://bucket-name/ --delete --cache-control "max-age=3600"
The --delete flag ensures old versions are removed, and --cache-control sets appropriate browser cache headers for event pages (short TTL since details may change).
CloudFront Cache Invalidation
After syncing files to S3, we invalidated CloudFront edge caches so the new structured data would serve immediately (rather than waiting for TTL expiration):
aws cloudfront create-invalidation --distribution-id DISTRIBUTION_ID --paths "/*" --query 'Invalidation.Id' --output text
Distribution IDs identified for each subdomain:
paulsimonradyshell.com: Found via AWS CloudFront console or CLI querysailjada.queenofsandiego.com: Separate distribution with dedicated cache behaviors- Other event subdomains: Each has its own distribution tied to queenofsandiego.com's Route53 alias records
Invalidating /* clears all cached objects—overkill for targeted pages, but fast and safe for small sites. For high-traffic properties, we'd invalidate specific paths: /events/* /concerts/*, etc.
Why This Infrastructure Pattern?
We use S3 + CloudFront for event pages because:
- Scalability: Zero-infrastructure hosting; no servers to manage
- Global delivery: CloudFront edge locations ensure sub-100ms latency worldwide
- Cost efficiency: Extremely cheap for static assets; we're paying ~$0.085/GB outbound in most regions
- SEO-friendly: Edge caching doesn't penalize freshness for search crawlers (proper Cache-Control headers ensure re-crawl)
- Separation of concerns: Event subdomains (often campaign-specific) can be spun up quickly without touching application servers
Integration with Existing Tooling
Our event pages are generated by /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/tools/render_event_sites.py. To prevent re-injection on future runs, we're planning to integrate the structured data generation directly into that renderer—eliminating the separate injection step entirely.
Similarly, service area pages generated by /Users/cb/Documents/repos/sites/quickdumpnow.com/tools/generate_service_area_pages.py should include LocalBusiness schema at render time. These scripts become the single source of truth for both HTML and metadata.
What's Next
- Refactor render scripts to generate Event/LocalBusiness JSON-LD inline at build time
- Monitor Search Console for structured data errors and rich snippet indexing
- A/B test event CTR before/after structured data deployment
- Document metadata extraction patterns