3.5 KiB
pSEO Static Prerender Guide
How it works
The pseoPrerendererPlugin Vite plugin runs as part of vite build (in closeBundle). After the SPA bundle is assembled, the plugin:
- Fetches
GET /www/sitemap.xmlfrom the quinn.api to discover all pSEO routes - Fetches
GET /www/destinationsto find which slugs haverelationship === 'tour-confirmed' - For each remaining route, fetches the specific page data and writes
dist/_/<route>/index.htmlwith the correct<title>,<meta>,<link rel="canonical">, and<script type="application/ld+json">tags injected into the HTML template - Emits
dist/prerender-manifest.jsonlisting all routes written, skipped, and excluded
Nginx serves the prerendered index.html for known routes via try_files $uri/index.html $uri /index.html. React hydrates on the client and takes over from there.
Routes covered
| Pattern | Source |
|---|---|
/_/escorts/in-{slug} |
destinations table, all non-tour-confirmed rows |
/_/trans-escorts/in-{slug} |
same destinations |
/_/escorts/{region} |
regions table |
/_/trans-escorts/{region} |
same regions |
/_/what-is/{slug} |
hobby_terms table |
Excluded routes
Tour-confirmed destinations are excluded at build time. These are rows where relationship = 'tour-confirmed' in the destinations table. Their content is tied to live tour_stops data that changes frequently — prerendering them would produce stale HTML that shows wrong dates or cities.
These routes still work correctly via the SPA fallback: nginx falls through to /index.html, React hydrates, and the client fetches live data.
To see which slugs were excluded in the last build:
cat deployments/@domains/quinn.www/root/dist/prerender-manifest.json | jq '.tourConfirmedExcluded'
Environment variables
| Variable | Default | Purpose |
|---|---|---|
QUINN_API_URL |
http://localhost:3030 |
Base URL of quinn.api — must be reachable at build time |
Set this in the build environment when deploying (e.g. in deploy.sh before bun run build):
export QUINN_API_URL=http://localhost:3030 # on VPS, api runs on :3030
cd deployments/@domains/quinn.www/root && bun run build
Rebuilding
The prerender runs automatically on every bun run build. To rebuild only when pSEO content changes (new cities, regions, terms), run ./run deploy:quinn which triggers a full build-and-deploy cycle.
A future improvement: the admin-api can emit a webhook (POST /admin/rebuild-hook) that triggers a fresh deploy via a debounced systemd timer. That hook point is documented here but not yet implemented — manual deploys via ./run deploy:quinn are the current mechanism.
Verification
After a build, confirm JSON-LD is present in the raw HTML:
# Local (after build)
grep -l 'application/ld+json' deployments/@domains/quinn.www/root/dist/_/escorts/in-*/index.html | head -5
# Check one file
grep 'application/ld+json' deployments/@domains/quinn.www/root/dist/_/escorts/in-san-francisco/index.html
# On prod (curl without JS execution)
curl -s https://transquinnftw.com/_/escorts/in-san-francisco | grep 'application/ld+json'
Plugin source
deployments/@domains/quinn.www/root/plugins/vite-plugin-pseo-prerender.ts
Nginx config
The /_/ location block in deployments/@domains/quinn.www/nginx/prod.conf uses:
try_files $uri/index.html $uri /index.html;
This serves dist/_/escorts/in-{slug}/index.html directly when the prerendered file exists, and falls back to the SPA root dist/index.html for any route that was skipped.