{"id":11009,"date":"2026-05-24T19:29:47","date_gmt":"2026-05-24T11:29:47","guid":{"rendered":"https:\/\/aeosub.com\/staticforge-for-cloudflare-pages\/"},"modified":"2026-05-24T19:29:47","modified_gmt":"2026-05-24T11:29:47","slug":"staticforge-for-cloudflare-pages","status":"publish","type":"post","link":"https:\/\/aeosub.com\/zh\/staticforge-for-cloudflare-pages\/","title":{"rendered":"StaticForge for Cloudflare Pages"},"content":{"rendered":"<div class=\"post-img\" style=\"margin-bottom: 25px\"><img decoding=\"async\" src=\"https:\/\/ps.w.org\/staticforge-for-cloudflare-pages\/assets\/banner-1544x500.png?rev=3546094\" alt=\"StaticForge for Cloudflare Pages\" style=\"width: 100%;height: auto;border-radius: 12px\"><\/div>\n<div class=\"plugin-info-card\" style=\"border: 1px solid #e2e8f0;border-radius: 10px;overflow: hidden;margin-bottom: 30px;font-family: -apple-system, system-ui, sans-serif;background: #fff\">\n<table style=\"width: 100%;border-collapse: collapse;font-size: 14px\">\n<tr style=\"border-bottom: 1px solid #edf2f7\">\n<th style=\"padding: 15px 20px;text-align: left;width: 35%;color: #4a5568;background: #fcfcfc\"><img decoding=\"async\" src=\"https:\/\/ps.w.org\/staticforge-for-cloudflare-pages\/assets\/icon-256x256.png?rev=3546100\" style=\"width: 40px;height: 40px;border-radius: 6px;margin-right: 12px;vertical-align: middle\"> Developer<\/th>\n<td style=\"padding: 15px 20px;font-weight: 500\"><a href=\"https:\/\/profiles.wordpress.org\/gunjanjaswal\/\" target=\"_blank\" rel=\"noopener\">Gunjan Jaswal<\/a><\/td>\n<\/tr>\n<tr style=\"border-bottom: 1px solid #edf2f7\">\n<th style=\"padding: 12px 20px;text-align: left;color: #4a5568;background: #fcfcfc\">Version<\/th>\n<td style=\"padding: 12px 20px\">v1.1.0<\/td>\n<\/tr>\n<tr style=\"border-bottom: 1px solid #edf2f7\">\n<th style=\"padding: 12px 20px;text-align: left;color: #4a5568;background: #fcfcfc\">Active Installs<\/th>\n<td style=\"padding: 12px 20px\">0 +<\/td>\n<\/tr>\n<tr style=\"border-bottom: 1px solid #edf2f7\">\n<th style=\"padding: 12px 20px;text-align: left;color: #4a5568;background: #fcfcfc\">Requires \/ Tested<\/th>\n<td style=\"padding: 12px 20px\">5.8 \/ 7.0<\/td>\n<\/tr>\n<tr style=\"border-bottom: 1px solid #edf2f7\">\n<th style=\"padding: 12px 20px;text-align: left;color: #4a5568;background: #fcfcfc\">Last Updated<\/th>\n<td style=\"padding: 12px 20px\">2026-05-24 10:55am GMT<\/td>\n<\/tr>\n<tr>\n<th style=\"padding: 12px 20px;text-align: left;color: #4a5568;background: #fcfcfc\">Official Link<\/th>\n<td style=\"padding: 12px 20px\"><a href=\"https:\/\/wordpress.org\/plugins\/staticforge-for-cloudflare-pages\/\" target=\"_blank\" style=\"text-decoration: none;color: #4a3bca;font-weight: bold\" rel=\"noopener\">View Details \u2192<\/a><\/td>\n<\/tr>\n<\/table><\/div>\n<div class=\"plugin-short-desc\" style=\"margin: 20px 0;padding: 12px 15px;background: #f9fafb;border-left: 4px solid #4a3bca;border-radius: 6px;font-style: italic;color: #4a5568\">Auto-export WordPress as static HTML to Cloudflare Pages on publish \u2014 with SEO meta, JSON-LD schemas, sitemaps, and robots.txt.<\/div>\n<p><strong>StaticForge for Cloudflare Pages<\/strong> turns your WordPress site into a static site that lives on Cloudflare Pages, automatically. On every publish or update of any selected post type, the plugin renders your whole site to static HTML, injects a complete SEO metadata baseline (when no other SEO plugin is present), inlines all linked CSS so pages are self-contained, mirrors your sitemap structure, ships an editable <code>robots.txt<\/code>, and pushes everything to Cloudflare Pages via the Direct Upload API.<\/p>\n<p>The WordPress install (your &#8220;dashboard&#8221;) becomes the editor only. Public visitors hit the static deployment on Cloudflare&#8217;s edge \u2014 fast, free, and resilient.<\/p>\n<h4>Key features<\/h4>\n<ul>\n<li><strong>Whole-site export<\/strong> \u2014 homepage, posts, pages, custom post types, taxonomy archives, author archives.<\/li>\n<li><strong>Theme-independent<\/strong> \u2014 works with any theme. Renders pages exactly as a real visitor would see them.<\/li>\n<li><strong>Inlined CSS<\/strong> \u2014 all <code>&lt;link rel=\"stylesheet\"&gt;<\/code> tags are fetched and embedded as <code>&lt;style&gt;<\/code> blocks. Each deployed page is fully self-contained.<\/li>\n<li><strong>Featured image LCP boost<\/strong> \u2014 auto-adds <code>fetchpriority=\"high\"<\/code>, <code>loading=\"eager\"<\/code>, <code>decoding=\"async\"<\/code> to the post&#8217;s featured image so the browser prioritises it as the LCP candidate. Improves Core Web Vitals on every theme that uses <code>the_post_thumbnail()<\/code> or <code>get_the_post_thumbnail()<\/code>.<\/li>\n<li><strong>Built-in SEO metadata injection<\/strong> \u2014 when no other SEO plugin is detected, automatically emits a full baseline:\n<ul>\n<li><code>&lt;meta description&gt;<\/code> \u2014 smart fallback chain (excerpt <span aria-hidden=\"true\" class=\"wp-exclude-emoji\">\u2192<\/span> trimmed content <span aria-hidden=\"true\" class=\"wp-exclude-emoji\">\u2192<\/span> user bio <span aria-hidden=\"true\" class=\"wp-exclude-emoji\">\u2192<\/span> term description <span aria-hidden=\"true\" class=\"wp-exclude-emoji\">\u2192<\/span> site tagline).<\/li>\n<li><code>&lt;meta robots&gt;<\/code> with <code>index, follow, max-image-preview:large<\/code> and friends.<\/li>\n<li><code>&lt;link rel=\"canonical\"&gt;<\/code>.<\/li>\n<li><strong>Open Graph<\/strong>: <code>og:type<\/code>, <code>og:title<\/code>, <code>og:description<\/code>, <code>og:url<\/code>, <code>og:site_name<\/code>, <code>og:locale<\/code>, <code>og:image<\/code> with dimensions and alt; <code>article:published_time<\/code>, <code>article:modified_time<\/code>, <code>article:author<\/code>, <code>article:section<\/code>, <code>article:tag<\/code> on posts; <code>profile:first_name<\/code>, <code>profile:last_name<\/code>, <code>profile:username<\/code> on author pages.<\/li>\n<li><strong>Twitter Card<\/strong> \u2014 <code>summary_large_image<\/code> when an image is available, otherwise <code>summary<\/code>; title, description, image, creator.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Rich JSON-LD schemas<\/strong> \u2014 auto-emitted in <code>&lt;head&gt;<\/code>:\n<ul>\n<li><code>WebSite<\/code> + <code>SearchAction<\/code>, <code>Organization<\/code> on every page.<\/li>\n<li><code>Article<\/code> with linked <code>author<\/code> Person, <code>publisher<\/code> Organization, image, dates, articleSection, keywords on single posts.<\/li>\n<li><code>WebPage<\/code> with <code>primaryImageOfPage<\/code> on pages and custom post types.<\/li>\n<li><strong><code>Person<\/code> + <code>ProfilePage<\/code> schema on author archives<\/strong> \u2014 display name, URL, bio, avatar <code>ImageObject<\/code> (256\u00d7256), <code>sameAs<\/code> social links pulled from <code>user_url<\/code> and Twitter\/Facebook\/LinkedIn\/Instagram\/YouTube\/GitHub user meta.<\/li>\n<li><code>CollectionPage<\/code> for taxonomy and term archives.<\/li>\n<li><code>BreadcrumbList<\/code> on all singulars and archives.<\/li>\n<li><strong>Auto-detected <code>FAQPage<\/code><\/strong> \u2014 extracts Q\/A pairs from Yoast \/ Rank Math \/ SEOPress FAQ blocks, OR native HTML5 <code>&lt;details&gt;&lt;summary&gt;<\/code> markup.<\/li>\n<li><strong>Auto-detected <code>HowTo<\/code><\/strong> \u2014 extracts steps from Yoast \/ Rank Math HowTo blocks, OR posts whose title starts with &#8220;How to&#8221; + has an ordered list with 3+ items.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Two-tier dedup safety<\/strong> \u2014 auto-disables to avoid duplicates:\n<ul>\n<li><strong>General SEO plugins<\/strong> (skip ALL our injection): Yoast, Rank Math, All in One SEO Pack (v4+ &amp; legacy), SEOPress, The SEO Framework, Slim SEO, Squirrly, SmartCrawl, WP Meta SEO.<\/li>\n<li><strong>Schema-only plugins<\/strong> (skip ONLY our JSON-LD; meta + og still emit): Schema &amp; Structured Data for WP &amp; AMP (saswp by Magazine3), Schema Pro by Brainstorm Force, WPSSO Core, Schema (by Hesham), Schema App, and Magazine3 Schema variants.<\/li>\n<li>Override via setting or filters (<code>sforge_seo_competing_plugin<\/code>, <code>sforge_schema_competing_plugin<\/code>).<\/li>\n<\/ul>\n<\/li>\n<li><strong>Sitemap mirroring + fallback generation<\/strong> \u2014 discovers <code>\/sitemap.xml<\/code>, <code>\/sitemap_index.xml<\/code>, <code>\/wp-sitemap.xml<\/code>, follows index files, fetches child sitemaps, handles CDATA-wrapped <code>&lt;loc&gt;<\/code> entries, rewrites origin URLs to your live domain (including protocol-relative <code>\/\/host<\/code> variants), and strips <code>&lt;?xml-stylesheet ... ?&gt;<\/code> directives so the dashboard host doesn&#8217;t leak into browser-rendered sitemap views. Bundles them all in the deploy. <strong>When the origin exposes no sitemap<\/strong> (no SEO plugin, WP core sitemap disabled, sub-directory install with non-standard paths, etc.), the plugin builds a standards-compliant <code>&lt;urlset&gt;<\/code> <code>sitemap.xml<\/code> itself from the crawled URL list \u2014 with <code>&lt;lastmod&gt;<\/code> resolved from <code>get_post_modified_time()<\/code>, <code>&lt;changefreq&gt;weekly&lt;\/changefreq&gt;<\/code>, and <code>&lt;priority&gt;<\/code> (1.0 home \/ 0.7 elsewhere). Live site always ships a sitemap.<\/li>\n<li><strong>Granular sitemap generator settings<\/strong> \u2014 when the fallback runs, you control exactly what gets listed: per-public-post-type checkboxes, include\/exclude homepage, taxonomy archives, author archives, and an option to split the output into a <code>&lt;sitemapindex&gt;<\/code> referencing per-type sub-sitemaps (<code>sitemap-post.xml<\/code>, <code>sitemap-page.xml<\/code>, <code>sitemap-authors.xml<\/code>, <code>sitemap-taxonomy-category.xml<\/code>, etc.) for cleaner Search Console submission. Independent of Export Scope. Filter <code>sforge_sitemap_groups<\/code> to mutate the URL list.<\/li>\n<li><strong>Editable robots.txt for the live site with auto-managed Sitemap: line<\/strong> \u2014 leave blank to auto-generate, or paste your own <code>Allow:<\/code> \/ <code>Disallow:<\/code> rules. Any <code>Sitemap:<\/code> directive you type is stripped and replaced with the URL of the actually-deployed sitemap (<code>sitemap.xml<\/code> \/ <code>sitemap_index.xml<\/code> \/ <code>wp-sitemap.xml<\/code> \/ etc.) so robots.txt never points at a dead URL. Independent of the dashboard&#8217;s own robots.txt.<\/li>\n<li><strong>Dashboard auto-noindex on activation (social-aware)<\/strong> \u2014 when the plugin activates it locks the WordPress install out of search engines (so editors only ever appear via the static deployment). Social\/messaging\/preview scrapers (Facebook, LinkedIn, Twitter\/X, Pinterest, WhatsApp, Slack, Discord, Telegram, Applebot, Reddit, Tumblr, Mastodon, Bluesky, iframely, Embedly) are explicitly allowed <code>\/wp-content\/uploads\/<\/code> so og:image previews and oEmbed thumbnails still resolve when a post is shared. Four enforcement layers, all bypassed when the plugin&#8217;s own renderer fetches a page (detected via <code>X-SFORGE-Export<\/code> header), and additionally bypassed for social-scraper user agents and <code>\/wp-content\/uploads\/<\/code> requests:\n<ol>\n<li>Physical <code>robots.txt<\/code> at webroot with <code>Disallow: \/<\/code> (any existing file is backed up to <code>robots.txt.sforge-backup<\/code> and restored on deactivation).<\/li>\n<li><code>robots_txt<\/code> WordPress filter for the dynamic fallback.<\/li>\n<li><code>wp_robots<\/code> filter adding <code>noindex,nofollow<\/code> to the meta robots tag.<\/li>\n<li><code>send_headers<\/code> action emitting <code>X-Robots-Tag: noindex, nofollow, noarchive, nosnippet<\/code> HTTP header on every response.<br \/>\nToggle via the <strong>Block dashboard from search engines<\/strong> setting (default on); flipping the toggle applies\/restores the physical robots.txt instantly.<\/li>\n<\/ol>\n<\/li>\n<li><strong>Defensive noindex stripping<\/strong> \u2014 removes <code>noindex<\/code> \/ <code>nofollow<\/code> \/ <code>noarchive<\/code> directives from rendered HTML before deploy, so your live site stays indexable even if the source dashboard is locked down.<\/li>\n<li><strong>Auto-deploy on publish\/update<\/strong> \u2014 debounced (default 120s) so rapid edit clusters collapse into one deploy.<\/li>\n<li><strong>Cloudflare Pages Direct Upload<\/strong> \u2014 no Git integration required. Uses the official content-addressable upload API: only changed assets are re-uploaded across deploys.<\/li>\n<li><strong>Live progress UI<\/strong> \u2014 activity log auto-refreshes every 4 seconds with batch-by-batch upload progress, render percentages, and a status pill (Idle \/ Queued \/ Working).<\/li>\n<li><strong>Setup Guide built in<\/strong> \u2014 full walk-through for Cloudflare Pages project creation, API token setup, and plugin configuration, all inside WP admin.<\/li>\n<\/ul>\n<h4>How it works<\/h4>\n<ol>\n<li>On publish\/update, plugin queues a full-site rebuild via <code>wp_schedule_single_event<\/code>.<\/li>\n<li>Crawler builds URL list (homepage + all published posts\/pages of selected types + taxonomy term archives + author archives).<\/li>\n<li>SEO injector hooks into <code>wp_head<\/code> and emits meta + JSON-LD for the rendering page (skipped if another SEO plugin is active).<\/li>\n<li>Renderer fetches each URL via <code>wp_remote_get<\/code>, inlines CSS, rewrites origin URLs to your live domain, strips defensive noindex meta and admin-bar artefacts.<\/li>\n<li>SEO module discovers and mirrors <code>\/sitemap.xml<\/code>, <code>\/sitemap_index.xml<\/code>, <code>\/wp-sitemap.xml<\/code> and any child sitemaps; if none found, auto-generates <code>sitemap.xml<\/code> from the crawled URL list. Emits the configured <code>robots.txt<\/code>.<\/li>\n<li>Deployer hashes each file, asks Cloudflare which assets are new, uploads only the new ones in batches (100 files \/ 25 MiB each), then creates a deployment via multipart\/form-data.<\/li>\n<li>Result: a new Cloudflare Pages deployment URL, logged with a clickable link.<\/li>\n<\/ol>\n<h4>What is NOT bundled<\/h4>\n<p>By default, files under <code>\/wp-content\/uploads\/<\/code>, theme assets, plugin assets, and fonts under <code>\/wp-content\/<\/code> are kept pointing at your WordPress origin so they keep working without re-uploading multi-gigabyte media folders. Make sure your origin is reachable over HTTPS (proxy through Cloudflare if your origin&#8217;s SSL cert is fragile).<\/p>\n<p>If your origin can&#8217;t be reached from Cloudflare (shared hosting firewall, IP allow-list, no proxy option), enable <strong>Export Scope <span aria-hidden=\"true\" class=\"wp-exclude-emoji\">\u2192<\/span> Bundle <code>\/wp-content\/uploads\/<\/code> into deploy<\/strong>. The plugin then fetches every uploads URL referenced in the rendered HTML and ships those files inside the CF Pages deploy itself \u2014 no origin dependency at runtime. Theme\/plugin assets still load from origin. See the <strong>Bundle <code>\/wp-content\/uploads\/<\/code> (recommended for shared hosting)<\/strong> section below.<\/p>\n<h4>Why this plugin<\/h4>\n<ul>\n<li>Simpler scope \u2014 Cloudflare Pages only, Direct Upload only.<\/li>\n<li>Built-in setup walkthrough, no docs hunting.<\/li>\n<li>Live progress UI with granular per-batch logging.<\/li>\n<li>Free tier compatible \u2014 no build minutes consumed.<\/li>\n<li>SEO baseline included \u2014 no extra plugin needed for tags + JSON-LD.<\/li>\n<li>No external dependencies, no SaaS, no premium tier.<\/li>\n<\/ul>\n<h3>External services<\/h3>\n<p>This plugin connects to the <strong>Cloudflare Pages API<\/strong> (<code>https:\/\/api.cloudflare.com\/client\/v4<\/code>) to deploy your exported static site. This is required core functionality \u2014 without it the plugin cannot upload your site to Cloudflare.<\/p>\n<p><strong>What the service is and what it is used for:<\/strong><br \/>\nCloudflare Pages is a static site hosting platform operated by Cloudflare, Inc. The plugin uses the Cloudflare Pages Direct Upload API to publish your statically rendered WordPress site to a Cloudflare Pages project that you create and own.<\/p>\n<p><strong>What data is sent, and when:<\/strong><br \/>\nThe plugin contacts the Cloudflare Pages API on these occasions:<\/p>\n<ul>\n<li>When you click <strong>Test Connection<\/strong> in the plugin settings. Sent: your Cloudflare API token (in the <code>Authorization<\/code> header), your Account ID, and your Pages project slug. Used to verify the project exists and the token has access.<\/li>\n<li>When you click <strong>Rebuild + Deploy Now<\/strong>, or after any post\/page\/CPT publish\/update if <strong>Auto-deploy<\/strong> is enabled (debounced). The plugin: (1) requests a short-lived upload JWT from <code>\/pages\/projects\/{project}\/upload-token<\/code>; (2) sends a list of SHA-256 hashes of the files in the export to <code>\/pages\/assets\/check-missing<\/code> to find which files Cloudflare does not already have; (3) uploads only the missing assets (HTML, CSS, JS, images, sitemap.xml, robots.txt) to <code>\/pages\/assets\/upload<\/code>; (4) POSTs a final deployment manifest + branch name to <code>\/pages\/projects\/{project}\/deployments<\/code>. All requests include your API token in the <code>Authorization: Bearer<\/code> header.<\/li>\n<\/ul>\n<p><strong>What is NOT sent:<\/strong> the plugin never sends WordPress database credentials, user passwords, post drafts, private content, settings beyond the four Cloudflare credentials, or any analytics\/telemetry beacons. Only the rendered public HTML\/CSS\/JS\/asset files that already make up your site are uploaded \u2014 the same content visitors would see.<\/p>\n<p><strong>Cloudflare Pages service links:<\/strong><\/p>\n<ul>\n<li>Cloudflare Pages product page: <a href=\"https:\/\/pages.cloudflare.com\/\" rel=\"nofollow ugc noopener\" target=\"_blank\">https:\/\/pages.cloudflare.com\/<\/a><\/li>\n<li>Cloudflare API documentation: <a href=\"https:\/\/developers.cloudflare.com\/api\/\" rel=\"nofollow ugc noopener\" target=\"_blank\">https:\/\/developers.cloudflare.com\/api\/<\/a><\/li>\n<li>Cloudflare Terms of Service: <a href=\"https:\/\/www.cloudflare.com\/website-terms\/\" rel=\"nofollow ugc noopener\" target=\"_blank\">https:\/\/www.cloudflare.com\/website-terms\/<\/a><\/li>\n<li>Cloudflare Self-Service Subscription Agreement (covers Workers &amp; Pages): <a href=\"https:\/\/www.cloudflare.com\/terms\/\" rel=\"nofollow ugc noopener\" target=\"_blank\">https:\/\/www.cloudflare.com\/terms\/<\/a><\/li>\n<li>Cloudflare Privacy Policy: <a href=\"https:\/\/www.cloudflare.com\/privacypolicy\/\" rel=\"nofollow ugc noopener\" target=\"_blank\">https:\/\/www.cloudflare.com\/privacypolicy\/<\/a><\/li>\n<\/ul>\n<p>You retain full ownership and control of your Cloudflare account, Pages project, API token, and deployed content. To stop using the service, revoke the API token in your Cloudflare dashboard and deactivate the plugin.<\/p>\n<h3>About<\/h3>\n<p>Built by <a href=\"https:\/\/www.gunjanjaswal.me\" rel=\"nofollow ugc noopener\" target=\"_blank\">Gunjan Jaswal<\/a>. Bug reports, feedback: hello@gunjanjaswal.me.<\/p>","protected":false},"excerpt":{"rendered":"<p>Auto-export WordPress as static HTML to Cloudflare Pages on publish \u2014 with SEO meta, JSON-LD schemas, sitemaps, and robots.txt.<\/p>","protected":false},"author":1,"featured_media":9317,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[252],"tags":[1734,1922,554,1249,1923,555],"class_list":["post-11009","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-trends","tag-cloudflare","tag-deploy","tag-seo","tag-sitemap","tag-static-site","tag-wordpress-plugin"],"_links":{"self":[{"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/posts\/11009","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/comments?post=11009"}],"version-history":[{"count":0,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/posts\/11009\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/media\/9317"}],"wp:attachment":[{"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/media?parent=11009"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/categories?post=11009"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/aeosub.com\/zh\/wp-json\/wp\/v2\/tags?post=11009"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}