HomePortfolioExtensionsScriptsAbout
WordPress pluginv2.15.0

StaticPush

Static site generator for WordPress. Export to HTML and push to GitHub or Cloudflare Pages automatically, with search, feeds, sitemaps, R2 media and a private admin.

This guide covers installing, connecting and running StaticPush by Quintaphon (version 2.15.0), a free static site generator for WordPress. Use it for the first setup and as a reference for every settings page.

Tip

The plugin's menu in WordPress is called Static Push. Every settings page mentioned below is inside it.

Overview

StaticPush exports your WordPress site to plain HTML files and publishes them. You keep writing in WordPress; visitors get a fast static copy that has no PHP, database or login page to attack.

  • Destinations: a GitHub repository (Cloudflare Pages or any Git-based host builds from it), Cloudflare Pages Direct Upload, or a local folder with ZIP download.
  • Automatic pushes: publishing, updating or deleting content pushes only the affected pages. Site-wide changes rebuild everything.
  • Static features: search, related posts, feeds, sitemaps, redirects, llms.txt for AI tools, the theme's 404 page and forms that post to an external service.
  • Extras: media offload to Cloudflare R2, HTML clean-up, and Private WordPress mode for a hidden admin site.

Requirements

ItemNeeded
WordPress5.8 or newer, with pretty permalinks (not "Plain")
PHP7.4 or newer, with the curl, json, mbstring and zip extensions
Loopback requestsThe server must be able to request its own pages. Health Check tests this.
DestinationA GitHub account, a Cloudflare account (for Cloudflare Pages), or neither for ZIP export

Install & connect

  1. Install: download staticpush.zip, then in WordPress go to Plugins > Add New Plugin > Upload Plugin, choose the file, click Install Now and activate it. (StaticPush is not in the WordPress.org directory yet. Once it is, you can search for StaticPush there instead.)
  2. Connect GitHub: open Static Push > Destination, choose GitHub and click Connect GitHub. A short code appears. Open github.com/login/device, enter the code and approve. The page updates by itself.
  3. Choose a repository: pick one from the list, or click New to create a private repository. Use a repository only for the static site; files the export doesn't produce are removed on push (except .github/, README, LICENSE, CNAME and wrangler configuration).
  4. Set your public address: open Export, choose Full address under Link rewriting and enter your public site address, for example https://www.example.com. Save.
  5. First push: click Push > Full site. The Dashboard shows live progress.
StaticPush Destination screen with the Connect GitHub button
Destination: choose GitHub, Cloudflare Pages or Local / ZIP, then connect GitHub.

Keys in wp-config.php

Instead of storing keys in the database you can define them in wp-config.php:

define( 'CFSP_GITHUB_TOKEN', 'github_pat_...' );  // fine-grained token for one repository
define( 'CFSP_API_TOKEN', '...' );                // Cloudflare Pages Direct Upload
define( 'CFSP_ACCOUNT_ID', '...' );
define( 'CFSP_R2_ACCESS_KEY', '...' );            // Media Offload
define( 'CFSP_R2_SECRET_KEY', '...' );
define( 'CFSP_BASIC_AUTH', 'user:password' );     // if WordPress is behind HTTP basic auth

Cloudflare Pages setup

  1. In Cloudflare go to Workers & Pages > Create > Pages > Connect to Git and pick the repository.
  2. Framework preset: None. Build command: empty. Output directory: / (or the folder you set under Destination).
  3. Add your domain under Custom domains.

Static Push writes _redirects and _headers for Cloudflare Pages: your own redirects, rules from the Redirection plugin, feed and sitemap redirects, and content types for feeds and stylesheets.

Recommended layout

Move WordPress to its own hostname (for example wp.example.com) and point your public domain at Cloudflare Pages. Then turn on Private WordPress.

Export settings

  • Link rewriting: Full address (best for SEO: canonical tags, sitemaps and social previews stay complete) or Root-relative. Rewrite every occurrence also rewrites addresses inside inline CSS, scripts and JSON.
  • Page discovery: use the WordPress database to list the home page, posts and pages (every page of posts split with page breaks), custom post types, categories and tags, authors, pagination, archives, feeds, sitemaps, robots.txt and ads.txt, and the 404 page; optionally follow links between pages.
  • Feeds and 404 page: save feeds as feed.xml with a browser stylesheet; the theme's real 404 page is exported and marked noindex.
  • Sitemaps: add a browser stylesheet to every sitemap, and redirect /sitemap.xml to the sitemap index.
  • Extra and excluded: extra pages, additional files and folders (paths inside the WordPress folder, full server paths or site addresses), and text that excludes an address.
  • SEO and redirect plugins (Static Push > Plugins): sitemaps from Yoast SEO, Rank Math, All in One SEO and SEOPress are exported and redirected, and rules from the Redirection plugin are written to _redirects.
StaticPush Export screen with link rewriting and page discovery
Export: link rewriting and page discovery.

Pushing

Push optionWhat it does
Pending changesExports and pushes only what changed since the last push.
Specific pagesExports and pushes the addresses you list. Add homepage and Add 404 page are one click.
Full siteExports every page again, then pushes. Use it after theme changes or settings that affect every page.
Current filesPushes the export folder as it is, without exporting.

Only changed files are sent to GitHub, so even a full push usually results in a small commit. A push that would delete most of the repository is refused as a safety measure.

Automation & scheduled posts

  • Automatic pushes run after publishing, updating, unpublishing or deleting content, comments, term changes and site-wide changes (menus, widgets, Customizer, theme). A short delay groups quick edits into one push.
  • Scheduled posts: WordPress publishes them with WP-Cron, which only runs when someone visits WordPress. Keep Publish missed scheduled posts on, and have a service such as cron-job.org call the wp-cron.php address shown on the Automation page every minute.
  • Notifications: optional emails when a push fails or succeeds.

Private WordPress

When WordPress has its own hostname, Stealth > Private WordPress hides it: visitors who aren't logged in see the login page (or a plain "Not found" page), every response is marked X-Robots-Tag: noindex, nofollow, and bots get a robots.txt that blocks everything. Static Push itself is let through with a secret key, so exports work exactly as before and the static site stays fully indexable.

Before turning it on

Set the static site address under Export to your public domain first. Files the web server serves directly (images, a robots.txt file on disk) don't pass through WordPress; add a Cloudflare Transform Rule that sets X-Robots-Tag: noindex, nofollow for the WordPress hostname to cover them too. Protect the login page with a login limiter or two-factor login.

Media Offload (R2)

  1. In Cloudflare, open R2, create a bucket and an R2 API token with Object Read & Write for it.
  2. In Media Offload, turn it on, enter the account ID, bucket and keys, and click Test connection.
  3. Set Public media URL to the domain you'll connect to the bucket, for example https://cdn.example.com. The default key prefix wp-content/uploads keeps today's paths.
  4. Click Sync media library now. Uploads run in the background and can be paused and resumed.
  5. Connect the domain to the bucket (R2 > bucket > Settings > Custom Domains), then run a full push.

Skip files named keeps image sizes you don't need out of the bucket (for example *-150x150.jpg); exported pages then use the original image instead, so nothing breaks. Remove skipped files deletes already uploaded files that match the rules. Private folders such as backups and protected downloads are never uploaded. With automatic upload on, new media goes to R2 as soon as you add it.

StaticPush Media Offload screen with the R2 connection settings
Media Offload: R2 connection settings.

A search index (cfsp-search.json) and a small script are added to the static site. The script takes over your theme's search forms and ?s= links and shows results in an instant overlay, with fuzzy, typo-tolerant matching by Fuse.js (bundled and self-hosted). Choose the content types to index, fuzziness, minimum characters and the number of results. Run a full push after changing search settings.

StaticPush Site Search settings
Site Search: content types, excerpts and fuzzy matching.

AI & llms.txt

Turn on AI & LLMs > Publish llms.txt to add a plain Markdown file at /llms.txt (format from llmstxt.org). It tells AI tools such as ChatGPT, Claude and Perplexity what your site is about, with your categories, pages and latest posts, using your static site's addresses. It is rebuilt at the end of every push.

Turning it off removes the llms.txt StaticPush published. A file of your own listed under Export > Additional files is never touched while the option is off.

StaticPush AI and LLMs screen
AI & LLMs: publish llms.txt.
  • Related Posts are worked out at export time from shared tags or categories and written into each post, in the Jetpack Related Posts design, a simple grid or your own template.
  • Forms: remove comment forms, or send POST forms to an external form service such as Formspree or Getform.

HTML Clean-up & Stealth

HTML Clean-up can add a <main> landmark for accessibility when your theme has none, and removes markup a static site doesn't need: HTML comments, sourceURL markers, block editor and emoji styles and scripts, Dashicons, admin-bar CSS, Yoast schema, speculation rules and preconnect hints you don't use. It can add preconnect and dns-prefetch hints for your media domain, minify HTML, and apply your own removal rules (#element-id, /regex/ or exact text).

Custom removal rules

Each line is a separate rule. Don't paste multi-line blocks: a line such as </script> on its own is ignored for safety, but paste whole blocks as one /regex/s rule or remove them by #id.

Stealth removes the generator tag and WordPress API links, and can strip ?ver= query strings (best left off, because browsers use them to fetch updated CSS and JavaScript).

Feeds, sitemaps & 404

  • Feeds are saved as feed.xml (category and tag feeds too), every link to a feed is updated, and old feed addresses redirect to the new ones.
  • Feeds and sitemaps get browser stylesheets (rss-style.xsl, main-sitemap.xsl); paste your own XSL to replace the built-in ones.
  • The theme's own 404 template is exported as 404.html and marked noindex; Cloudflare Pages serves it for missing addresses. Re-export it any time with Push > Specific pages > Add 404 page.

Performance

Setting (Advanced)DefaultNotes
Pages exported at the same time4Use 1 on small shared hosting, up to 8 on a dedicated server.
Fetch pages directly from this serverOnSkips the round trip through the internet and any proxy. Falls back to the normal route automatically if it doesn't work on your host.
Server address127.0.0.1Enter the server's IP if the web server only listens on it.
Batch length / fetch timeout20 s / 30 sLower values suit hosts that stop long requests.

Health Check

Health Check tests permalinks, PHP, memory and disk space, loopback requests, the background worker, direct fetch, WP-Cron, scheduled posts, Private WordPress, and the connection to your destination and R2 bucket. Results are kept for an hour; Reset Diagnostics runs every check again. It also shows the IP address your server uses on the internet, which you need if WordPress sits behind a login wall such as Cloudflare Access.

StaticPush Health Check results
Health Check: passed, warning and failed checks at a glance.

WP-CLI & hooks

wp static-push export [--no-deploy]
wp static-push deploy
wp static-push status
wp static-push cancel

Filters: cfsp_full_urls, cfsp_post_urls, cfsp_exclude_url, cfsp_rewrite_content, cfsp_clean_html, cfsp_fetch_args, cfsp_post_types, cfsp_github_client_id, cfsp_github_protected, cfsp_github_allow_mass_delete.
Actions: cfsp_job_done, cfsp_job_failed, cfsp_settings_updated.

Your data

  • Deleting the plugin keeps settings, the GitHub connection, media records and exported files, so reinstalling picks up where you left off. Turn on Tools > Delete all data when the plugin is deleted first if you want everything removed.
  • Your static site, GitHub repository and R2 bucket are never touched when the plugin is removed.
  • Tools also exports, imports and resets settings (tokens are never included in exports), downloads the site as a ZIP and clears the export folder.
  • External services are only contacted for features you set up. The plugin's readme lists each one.
  • Exports bypass page caches with a query parameter and a no-cache header, so caching plugins don't need to be turned off.

Troubleshooting

ProblemWhat to do
"Plugin could not be activated because it triggered a fatal error"Another copy of the plugin is active (for example an older version in a different folder). Deactivate it first. Remove old folders with a file manager rather than the Delete link to keep your settings.
"Refusing to delete N files from the repository"The export folder is almost empty compared with the repository, usually after it was cleared. Run Push > Full site to rebuild it.
GitHub API HTTP 401: Bad credentialsThe GitHub connection was revoked. Open Destination, click Disconnect and then Connect GitHub again, and use Push > Current files.
The push stops with a login-page errorA login wall such as Cloudflare Access is in front of WordPress. Add the server's IP (shown in Health Check) to a bypass rule, or use Private WordPress instead.
Images are broken on the static siteCheck Media Offload > Public media URL and that the media domain is connected to the R2 bucket.
Theme changes don't appearTheme file edits don't trigger automatic pushes. Use Push > Specific pages for the pages you changed, or Full site.
Page 2, 3 and on of a long post show old contentVersions before 2.15.0 only re-exported the first page of a post split with page breaks. Update to 2.15.0 and run one Push > Full site.
Admin screens look unstyledA stylesheet failed to load. The plugin retries once by itself; otherwise press Ctrl+F5.

Changelog

  • 2.15.0: when a post split into pages with the Page Break block was updated, automatic and "Pending changes" pushes re-exported only its first page. All pages of the post are now exported. Run one Full site push after updating.
  • 2.14.9: first public release.

Need help?

Questions about setup, hosting or a feature request? Contact us on our support page. You can also download the latest version (v2.15.0) here.