FastAPI + nginx SSI sidecar for injecting fresh Schema.org JSON-LD into nginx-served sites without redeploys.
Project description
SEO Sidecar
Inject up-to-date Schema.org JSON-LD into your nginx-served sites with zero crons, zero site file changes, always fresh. A FastAPI sidecar + nginx SSI integration that lets an upstream AI/automation push schemas to your hosting machine in real time.
Why this exists
If you host static or server-rendered HTML through nginx and want fresh, dynamic Schema.org JSON-LD in every page's <head> without redeploying the site every time a schema changes, you have three bad options:
- Edit the HTML files every time → site redeployment per schema change
- Cron job that rebuilds + redeploys → stale between cron runs, slow propagation
- Server-side rendering → couples schema generation to your rendering tier
SEO Sidecar is the fourth option. An upstream process (your AI, your CMS, your trend analyzer, whatever) pushes JSON-LD to a tiny FastAPI service on your nginx host. nginx pulls the schema in via SSI on every page load. Schema updates take effect instantly with no site redeployment, no cron lag, no rendering coupling.
Built in production for ThatDevPro's network of nine client websites.
Architecture
<UPSTREAM> (your AI publishing source)
│
│ Push over Tailscale (event-driven)
│ POST /update/{site} with JSON-LD
│
▼
┌──────────────────────────────────────────┐
│ <SIDECAR_HOST> (your nginx host) │
│ │
│ seo-sidecar (localhost:9090) │
│ ├── SQLite: /var/lib/seo-sidecar/ │
│ ├── Receives pushes from upstream │
│ ├── Serves schemas to nginx │
│ └── Keeps history + versioning │
│ │
│ nginx │
│ ├── SSI include on every page load │
│ ├── Fetches from localhost:9090 │
│ ├── 5min proxy_cache (configurable) │
│ └── 1s timeout (pages never stall) │
│ │
│ 9 hosted sites │
│ └── Each gets dynamic JSON-LD in <head> │
└──────────────────────────────────────────┘
How It Works
- Your upstream service decides a schema needs updating (trend shift, new FAQ, competitor gap)
- Upstream pushes the new JSON-LD to
http://<SIDECAR_HOST>:9090/update/{site}over Tailscale - Sidecar stores it in SQLite with version history
- nginx SSI fetches from
localhost:9090/schema/{site}on every HTML page load - Schema appears in the
<head>of the site — search engines and AI crawlers see it immediately
No cron jobs. No file writes. No site deployments. Upstream controls schema state remotely.
Files
| File | Where It Goes | Purpose |
|---|---|---|
sidecar.py |
/opt/seo-sidecar/ on the sidecar host |
FastAPI service — receives pushes, serves to nginx |
nginx-seo-sidecar.conf |
/etc/nginx/conf.d/ on the sidecar host |
Upstream + hostname map |
seo-inject.conf |
/etc/nginx/snippets/ on the sidecar host |
Drop-in include for server blocks |
megamind_push.py |
upstream nodes | CLI push client |
seo_client.go |
Go push client | Go push client for integration |
seo-sidecar.service |
/etc/systemd/system/ on the sidecar host |
systemd unit |
deploy-sidecar.sh |
Run once on the sidecar host | Full automated deployment |
Deploy on the sidecar host
# Copy files to your sidecar host
scp -r seo-sidecar/ <YOUR_SERVER>:/tmp/seo-sidecar/
# SSH in and deploy
ssh <YOUR_SERVER>
cd /tmp/seo-sidecar
sudo bash deploy-sidecar.sh
Then add one line to each nginx server block:
include /etc/nginx/snippets/seo-inject.conf;
Reload nginx:
nginx -t && systemctl reload nginx
Push from your upstream
# Push single site
python megamind_push.py push thatdeveloperguy
# Push all 9 sites
python megamind_push.py push-all
# Bulk push (single HTTP request)
python megamind_push.py push-bulk
# Check what's live
python megamind_push.py status
# View version history
python megamind_push.py history thatdeveloperguy
Or from Go:
client := seo.NewClient("http://<SIDECAR_HOST>:9090", "your-token")
client.Push("thatdeveloperguy", jsonldString)
API Endpoints
Push (MEGAMIND → Sidecar)
POST /update/{site}— Push schema for one sitePOST /update-bulk— Push schemas for multiple sitesDELETE /schema/{site}— Remove a schema
Serve (nginx → Sidecar)
GET /schema/{site}— Returns<script type="application/ld+json">HTML
Monitor
GET /status— All sites, versions, freshnessGET /history/{site}— Version history
Config
Set in /etc/systemd/system/seo-sidecar.service:
| Env Var | Default | Description |
|---|---|---|
SEO_DB_PATH |
/var/lib/seo-sidecar/schemas.db |
SQLite database location |
SEO_AUTH_TOKEN |
CHANGE_ME_BEFORE_DEPLOY |
Bearer token for push auth |
SEO_HOST |
127.0.0.1 |
Listen address (keep localhost) |
SEO_PORT |
9090 |
Listen port |
Adding New Sites
- Add the hostname mapping in
nginx-seo-sidecar.conf(themapblock) - Add
include /etc/nginx/snippets/seo-inject.conf;to the new site's server block - Push a schema:
python megamind_push.py push newsite
That's it. No other config changes.
Companion tools
- aio-surfaces — MIT-licensed Python toolkit that generates the four AI-citation surfaces (llms.txt, aeo.json, entity.json, brand.json) from a single site config. Pairs naturally with this — generate schemas with
aio-surfaces, push them with this sidecar. - llms.txt generator — free Hugging Face Space that drafts a spec-compliant
llms.txtfrom any homepage URL.
License
MIT © 2026 Joseph W. Anady (ThatDevPro). See LICENSE.
Built and used in production by ThatDevPro — SDVOSB-certified veteran-owned web + AI engineering studio. Cassville, Missouri.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file seo_sidecar-0.2.0.tar.gz.
File metadata
- Download URL: seo_sidecar-0.2.0.tar.gz
- Upload date:
- Size: 8.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da7309a9770010491616768c1d8cd379bedfb4efdcbb6101b4ad61120d4e7985
|
|
| MD5 |
cc774f2611c50ba06fabb2d3d6f794b5
|
|
| BLAKE2b-256 |
52ffc51df7bd00f93b59859dc55268a26bb4ace304954f47ce0871b73f8cac67
|
File details
Details for the file seo_sidecar-0.2.0-py3-none-any.whl.
File metadata
- Download URL: seo_sidecar-0.2.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
deedeb57a604b2898c680535cfbccb806084cccf3bd53cd1c6425f24b18528b3
|
|
| MD5 |
22d171450015db5398236cb2d98c5f51
|
|
| BLAKE2b-256 |
e3ac0f742eb08ed55723e33830e8803efa30b2f5972bd568f4a06eb4c849ea4a
|