How do I convert my WordPress site to a static site with AI?

v1.0 Updated 2026-04-24 Source crystopa-forge-sop-wp-to-static-conversion.md
Direct answer

Convert a WordPress site to a hand-coded static site in nine phases: import the WP export into a local install, audit the rendered HTML in fonts → colors → images → layout order, copy original assets only, hand-code semantic HTML/CSS/JS replacing each plugin with a static equivalent, layer in schema and llms.txt, test locally, push to GitHub, deploy to Vercel or Nginx, then cut over DNS. The full spec is below — copy it whole and paste it into Claude as your session context.

The Spec

What this is

A nine-phase workflow for converting any WordPress site — Avada, Elementor, Divi, custom theme — into hand-coded static HTML/CSS/JS, deployed on Vercel or Nginx. Designed to be pasted into an AI coding agent (Claude, Cursor, etc.) as session context. The agent then executes the phases against your source site.

Prerequisites

  • WordPress site accessible — either live or via a .wpress export
  • Local by Flywheel installed
  • GitHub CLI (gh) authenticated
  • Vercel CLI (vercel) authenticated, or SSH access to a static-hosting droplet
  • An AI coding agent with file-system tools (Claude Code, Cursor, etc.)

Phase 1 — Source Extraction

If the source is on a hosted backup

  1. Get the .wpress export from All-in-One WP Migration (or equivalent backup)
  2. Create a new Local site, name it {sitename}-old
  3. Bump upload limits in conf/php/php.ini.hbs: upload_max_filesize = 4096M and post_max_size = 4096M
  4. Restart the site in Local
  5. Import the .wpress file via WP admin → All-in-One WP Migration

If the source is already in Local or live

Skip the import. Work from the existing install.

Phase 2 — Content & Design Audit

Lock styles before building

Audit in this exact order: fonts → colors → images → layout. Lock each into a markdown doc before moving to the next. Fonts are the highest-risk to get wrong, so they get locked first. Layout audit comes last because it depends on the locked styles.

Save audit docs to {project}/_audit/:

  • 01-FONTS.md — every font family, weight, size, line-height, letter-spacing per element (h1–h6, body, nav, buttons). Include a CSS variable mapping table.
  • 02-COLORS.md — full palette with hex values + where each color is used. Edge-case colors get noted.
  • 03-IMAGES.md — full inventory of copied originals, mapped to which pages they appear on.
  • 04-LAYOUT.md — per-page section order, container widths, breakpoints, schema for header/footer that repeats.
  • _audit/source/ — raw rendered HTML pulled via curl from the OLD site, kept as reference during build.

View the rendered site

Curl the rendered HTML for every page (do not mirror with wget) and save to _audit/source/:

for page in $(curl -s http://sitename-old.local/page-sitemap.xml | grep -oE '<loc>[^<]+</loc>' | sed 's|<[^>]*>||g'); do
  slug=$(echo "$page" | sed 's|http://sitename-old.local/||; s|/$||; s|/|_|g')
  curl -s "$page" > "_audit/source/${slug:-home}.html"
done

Extract from rendered HTML (View Source, not DevTools inspect)

  1. Sitemap — pull from /page-sitemap.xml (Yoast / sitemap plugin)
  2. Fonts — for Avada/Fusion sites, typography tokens live in wp-content/uploads/fusion-styles/{hash}.min.css. Grep for --awb-typography{1-4}-* and --h{1-6}_typography-* variables. For other themes, check Google Fonts <link> and @font-face.
  3. Colors — Avada palette is --awb-color1 through --awb-color8 in fusion-styles CSS. For non-Avada themes, pull CSS custom properties + inline style="..." attributes.
  4. Nav structure — exact menu items and link targets
  5. Content per page — headings, body text, CTAs, form fields. Extract verbatim — line-for-line is the standard.
  6. Images — note which images are used on which pages. Full-size originals are in wp-content/uploads/ (skip the -{width}x{height} thumbnails).
  7. Business info — address, phone, email, social URLs, GA tracking ID, Google Ads ID
  8. Third-party integrations — forms (Gravity Forms, Contact Form 7, Avada Fusion Forms), analytics, cookie consent, review badges
  9. SEO — title tags, meta descriptions, OG images, canonical URLs

Do NOT attempt

  • Wget/mirror approach — generates too much WP junk (query string pages, feed XMLs, admin bar markup, plugin assets). Waste of time to clean up.
  • Reverse-engineering page builder PHP (Avada Fusion Builder, Elementor, Divi) — the rendered HTML is deeply nested and class-heavy. Not worth salvaging.
  • Server-side scraping of dynamically-injected content (Termageddon legal pages, etc.) — those scripts run in the browser. The text won't be in curl output. Build stub pages and ask for content.

Phase 3 — Asset Collection

  1. Create the project directory: {site-name}/
  2. Create subdirectories: images/, css/, js/
  3. Copy full-size images from the WP install:
    wp-content/uploads/{year}/{month}/{filename}.jpg  (originals only)
    wp-content/uploads/revslider/                     (slider media if applicable)
  4. Copy logos, favicons, badge images
  5. Copy any video files (.mp4)
  6. Do NOT copy: plugin assets, theme assets, WP core files, CSS/JS from plugins

Phase 4 — Fresh Build

Structure

{site-name}/
  index.html
  css/style.css
  js/main.js
  images/
  {page-name}/index.html    (one per subpage)

Standards

  • No inline styles — all styling in css/style.css
  • No text characters for UI elements — use inline SVGs for arrows, close buttons, icons
  • SVG buttons: padding: 0; display: grid; place-items: center; on the button, display: block; on the SVG
  • CSS custom properties for colors, fonts, max-widths
  • Google Fonts via <link> with preconnect
  • Semantic HTML<header>, <nav>, <main>, <section>, <footer>, <address>
  • Responsive — mobile-first or desktop-first with breakpoints at 1024px, 768px, 480px
  • No frameworks — pure HTML/CSS/JS
  • Lazy load images below the fold (loading="lazy")
  • Animations — IntersectionObserver for scroll reveals, CSS keyframes for ambient effects

Rebuild features, don't replicate plugins

WP PluginStatic Replacement
Revolution SliderCSS/JS image slider with Ken Burns
Strong TestimonialsCSS/JS fade rotator
Gravity Forms / Avada Fusion FormsHTML <form> → POST to /api/send (Vercel function + Resend)
Justified Image GridCSS grid/masonry gallery
Yoast SEOManual <title>, <meta>, OG tags, JSON-LD
Termageddon (cookie consent + legal injection)Drop the script. Build static legal page stubs and ask for replacement copy.

Form pipeline — Resend + anti-spam

Files needed:

  • package.json{ "private": true, "engines": { "node": "20.x" }, "dependencies": { "resend": "^4.0.0" } }
  • vercel.json — CORS headers restricting /api/(.*) to the production origin
  • api/send.js — handler with origin/honeypot/timestamp/email-format/gibberish checks, then resend.emails.send({...})
  • js/forms.js — injects honeypot + base64 timestamp on page load, validates required fields, POSTs JSON, handles success/error UI

Required env var: RESEND_API_KEY (set via vercel env add RESEND_API_KEY production, paste value via stdin to keep it out of terminal history).

Preserve from original

  • Google Analytics tracking code (gtag.js + gtag('config', 'G-XXXXXXX'))
  • Google Ads conversion code (gtag config AW-XXXXXX + per-element click handlers)
  • All testimonial content (verbatim)
  • All business info (address, phone, social)
  • Badge links (WeddingWire, The Knot, etc.)
  • Legal pages content — except when source is Termageddon (script-injected, not in HTML)

Phase 4b — GEO / Search Visibility

Apply between build and deploy. Bare minimum for a small local-business site:

Schema (JSON-LD)

Add <script type="application/ld+json"> with a @graph array to every page's <head>:

EntityWhereNotes
Organization + LocalBusiness + (vertical type)Every pageUnified via @id="https://{domain}/#organization"
Person (founder)Home + AboutNested under Organization via @id reference. Include credentials/jobTitle.
WebSiteHomeIdentifies the site itself.
WebPageEvery pagePer-page entity referencing Organization via @id.
ServiceEach service pageprovider is @id reference, not a flat copy.
FAQPagePages with Q&A contentReuse existing on-page text — do NOT invent answers.
BreadcrumbListEvery non-home pagePosition-numbered list from Home → current page.

Sitewide files (project root)

  • llms.txt — short AI-assistant index (~30 lines): identity, services, beliefs, service area, founder, key URLs, contact
  • llms-full.txt — detailed profile
  • sitemap.xml — every public page with priority + changefreq
  • robots.txt — allow all, disallow /api/ and /_audit/, link to sitemap

Per-page metadata

  • <link rel="canonical" href="https://domain/path/" />
  • OG tags (og:type, og:url, og:title, og:description, og:image)
  • <meta name="description">
  • "Last updated: {date}" in footer (AEO freshness signal)

Content rules

  • Image alt text — entity + service + location, e.g. "Roof repair services in Austin, TX by Acme Roofing"
  • Inline cross-links — visibly distinguishable from body text (color OR underline)
  • Related-services block at the bottom of each capability page
  • Anchor phrases — pick 3–5 phrases that repeat naturally across pages

Phase 5 — Local Testing

  1. Serve locally: python -m http.server 8080 from the project root
  2. Check every page at http://localhost:8080
  3. Test responsive at 1024px, 768px, 480px
  4. Verify all images load
  5. Verify all internal links work
  6. Test slider/rotator auto-advance and manual controls
  7. Test mobile nav toggle

Phase 6 — Git & GitHub

  1. git init in the project directory
  2. git add -A && git commit -m "Initial build: {Site Name} static site"
  3. Create private repo: gh repo create {org}/client--{site-name} --private --source=. --push
  4. Naming convention: client--{site-name} (double dash separator)

Phase 7 — Deploy

Option A — Vercel (preferred for sites with forms)

  1. npm install locally to generate package-lock.json
  2. Link the project: vercel link --yes --scope={team} --project={project-name}
  3. Set env vars via stdin: printf "{value}" | vercel env add RESEND_API_KEY production
  4. Deploy: vercel deploy --prod --yes
  5. Verify: curl -s -o /dev/null -w "%{http_code}" https://{project-name}.vercel.app/
  6. Domain attach (after DNS): vercel domains add {domain}

Option B — Nginx static (legacy / pure static)

  1. Drop project files into /srv/{site-name}/ on the server
  2. Add Nginx vhost
  3. Run certbot for SSL
  4. Deploy via SCP/rsync
  5. Verify health check passes

Phase 8 — DNS Cutover

  1. Update A record to new host IP at the domain registrar
  2. If TTL is high, lower to 300s 24 hours before cutover
  3. Wait for propagation
  4. Verify site loads on new IP with SSL
  5. Monitor for 24 hours

Phase 8b — Form Pipeline Test

If the project has a form, do a real end-to-end test before considering deploy complete:

  1. Temporarily change the to: address in api/send.js to your inbox
  2. Redeploy: vercel --prod --yes
  3. POST a test submission with a valid base64 timestamp:
    TS=$(echo -n $(($(date +%s) - 5)) | base64)
    curl -s -X POST https://{domain}/api/send 
      -H "Content-Type: application/json" 
      -H "Origin: https://{domain}" 
      -H "Referer: https://{domain}/" 
      -d '{"name":"...","email":"...","leit_cf_ts":"'$TS'", ...}'
    Response should be {"ok":true}.
  4. Confirm receipt
  5. Revert to: to the client's email and redeploy

Phase 9 — Cleanup

  • Confirm site is live and stable
  • Remove site from old host
  • Delete Local WP install ({sitename}-old) if no longer needed
  • Update infrastructure registry

Conditions

When this works

  • You have access to the WP install or a .wpress export
  • The site is content + media + forms + analytics — no logged-in user features
  • Target hosting is Vercel (forms) or Nginx static (pure static)
  • Page-builder source is fine — Avada, Elementor, Divi, custom theme all convert the same way

When it doesn't

  • Active WooCommerce checkout with stock/inventory — keep WordPress, don't convert
  • User accounts, login, member-only content — keep WordPress
  • Server-side search or filtering you don't want to rebuild client-side
  • Content injected by external scripts (Termageddon-style) — text won't be in curl output; you must source the copy separately

Outcome

Output Hand-coded HTML/CSS/JS site
Typical timing 1–4 hours
Hosting cost $0 static, ~$5/mo with forms
Lighthouse 95+ across the board

The result is a portable site you can move to any host on earth. No proprietary lock-in, no plugin update treadmill, no DOM bloat from page builders. The same content loads in under a second instead of three to five.

Specs provided as-is. chadworks isn't responsible for how you use these prompts or any effects they may have on your code, content, infrastructure, or business. Review and test before applying.

chadworks — Chad Last updated 2026-04-24