plugin-icon

Static Site Exporter

Features
Betyg
3.5/5
Version
3.0.2
Aktiva installationer
700
Senast uppdaterat
Jan 13, 2026
Static Site Exporter

Features

  • Converts all posts, pages, and settings from WordPress to Markdown and YAML for use in Jekyll (or Hugo or any other Markdown and YAML based site engine)
  • Export what your users see, not what the database stores (runs post content through the_content filter prior to export, allowing third-party plugins to modify the output)
  • Converts all post_content to Markdown
  • Converts all post_meta and fields within the wp_posts table to YAML front matter for parsing by Jekyll
  • Generates a _config.yml with all settings in the wp_options table
  • Outputs a single zip file with _config.yml, pages, and _posts folder containing .md files for each post in the proper Jekyll naming convention
  • No settings. Just a single click.

Användning

  1. Place plugin in /wp-content/plugins/ folder
  2. Activate plugin in WordPress dashboard
  3. Select Export to Jekyll from the Tools menu

More information

See the full documentation:

Security Policy

To report a security vulnerability, please email ben@balter.com.

Where to get help or report an issue

  • For getting started and general documentation, please browse, and feel free to contribute to the project documentation.
  • For support questions (”How do I”, ”I can’t seem to”, etc.) please search and if not already answered, open a thread in the Support Forums.
  • For technical issues (e.g., to submit a bug or feature request) please search and if not already filed, open an issue on GitHub.

Things to check before reporting an issue

  • Are you using the latest version of WordPress?
  • Are you using the latest version of the plugin?
  • Does the problem occur even when you deactivate all plugins and use the default theme?
  • Have you tried deactivating and reactivating the plugin?
  • Has your issue already been reported?

What to include in an issue

  • What steps can another user take to recreate the issue?
  • What is the expected outcome of that action?
  • What is the actual outcome of that action?
  • Are there any screenshots or screencasts that may be helpful to include?
  • Only include one bug per issue. If you have discovered two bugs, please file two issues.

Command-line Usage

If you’re having trouble with your web server timing out before the export is complete, or if you just like terminal better, you may enjoy the command-line tool.

It works just like the plugin, but produces the zipfile on STDOUT:

`

php jekyll-export-cli.php > jekyll-export.zip `

If using this method, you must run first cd into the wordpress-to-jekyll-exporter directory.

Alternatively, if you have WP-CLI installed, you can run:

`

wp jekyll-export > export.zip `

The WP-CLI version will provide greater compatibility for alternate WordPress environments, such as when wp-content isn’t in the usual location.

Custom fields

When using custom fields (e.g. with the Advanced Custom fields plugin) you might have to register a filter to convert array style configs to plain values.

By default, the plugin saves custom fields in an array structure that is exported as:

`php

[”my-bool”]=> array(1) { [0] => string(1) ”1” } [”location”]=> array(1) { [0] => string(88) ”My address” } `

And this leads to a YAML structure like:

`yaml

my-bool: – ”1” location: – ’My address’ `

This is likely not the structure you expect or want to work with. You can convert it using a filter:

`php

add_filter( ’jekyll_export_meta’, function($meta) { foreach ($meta as $key => $value) { if (is_array($value) && count($value) === 1 && array_key_exists(0, $value)) { $meta[$key] = $value[0]; } }

return $meta;

}); `

A more complete solution could look like that:

`php

add_filter( ’jekyll_export_meta’, function($meta) { foreach ($meta as $key => $value) { // Advanced Custom Fields if (is_array($value) && count($value) === 1 && array_key_exists(0, $value)) { $value = maybe_unserialize($value[0]); // Advanced Custom Fields: NextGEN Gallery Field add-on if (is_array($value) && count($value) === 1 && array_key_exists(0, $value)) { $value = $value[0]; } } // convert types $value = match ($key) { // Advanced Custom Fields: ”true_false” type ’my-bool’ => (bool) $value, default => $value }; $meta[$key] = $value; }

return $meta;

}); `

Custom post types

To export custom post types, you’ll need to add a filter (w.g. to your themes config file) to do the following:

`php

add_filter( ’jekyll_export_post_types’, function() { return array(’post’, ’page’, ’you-custom-post-type’); }); `

The custom post type will be exported as a Jekyll collection. You’ll need to initialize it in the resulting Jekyll site’s _config.yml.

Developing locally

Option 1: Using Dev Containers (Recommended)

The easiest way to get started is using VS Code Dev Containers or GitHub Codespaces:

  1. Install VS Code and the Dev Containers extension
  2. git clone https://github.com/benbalter/wordpress-to-jekyll-exporter
  3. Open the folder in VS Code
  4. Click ”Reopen in Container” when prompted
  5. Wait for the container to build and dependencies to install
  6. Access WordPress at http://localhost:8088

The devcontainer includes: – Pre-configured WordPress and MySQL – All PHP extensions and Composer dependencies – VS Code extensions for PHP development, debugging, and testing – WordPress coding standards configured

See .devcontainer/README.md for more details.

Option 2: Manual Setup

Prerequisites

  1. sudo apt-get update
  2. sudo apt-get install composer
  3. sudo apt-get install php7.3-xml
  4. sudo apt-get install php7.3-mysql
  5. sudo apt-get install php7.3-zip
  6. sudo apt-get install php-mbstring
  7. sudo apt-get install subversion
  8. sudo apt-get install mysql-server
  9. sudo apt-get install php-pear
  10. sudo pear install PHP_CodeSniffer

Bootstrap & Setup

  1. git clone https://github.com/benbalter/wordpress-to-jekyll-exporter
  2. cd wordpress-to-jekyll-exporter
  3. script/bootstrap
  4. script/setup

Option 3: Docker Compose Only

  1. git clone https://github.com/benbalter/wordpress-to-jekyll-exporter
  2. docker-compose up
  3. open localhost:8088

Running tests

script/cibuild<h3>Performance Optimizations</h3>

This document describes the performance optimizations implemented in Static Site Exporter to improve export speed and reduce resource usage, especially for large WordPress sites.

Overview

The following optimizations have been implemented to address performance bottlenecks identified in the export process:

1. Optimized Database Queries

Problem: The original get_posts() method executed a separate SQL query for each post type, then merged the results using array_merge().

`php

// Before (inefficient) foreach ( $post_types as $post_type ) { $ids = $wpdb->get_col( $wpdb->prepare( ”SELECT ID FROM {$wpdb->posts} WHERE post_type = %s”, $post_type ) ); $posts = array_merge( $posts, $ids ); } `

Solution: Changed to a single SQL query using an IN clause.

`php

// After (optimized) $placeholders = implode( ’, ’, array_fill( 0, count( $post_types ), ’%s’ ) ); $query = ”SELECT ID FROM {$wpdb->posts} WHERE post_type IN ($placeholders)”; $posts = $wpdb->get_col( $wpdb->prepare( $query, $post_types ) ); `

Impact: Reduces database round trips from N (number of post types, typically 3) to 1, significantly improving performance on sites with many posts.

2. User Data Caching

Problem: The convert_meta() method called get_userdata() for every post, resulting in redundant database queries for posts by the same author (N+1 query problem).

`php

// Before (inefficient) ’author’ => get_userdata( $post->post_author )->display_name, `

Solution: Implemented a static cache to store user data across post conversions.

`php

// After (optimized) static $user_cache = array(); if ( ! isset( $user_cache[ $post->post_author ] ) ) { $user_data = get_userdata( $post->post_author ); $user_cache[ $post->post_author ] = $user_data ? $user_data->display_name : ”; } ’author’ => $user_cache[ $post->post_author ], `

Impact: Eliminates redundant database queries for author information. On a site with 1000 posts by 10 authors, this reduces queries from 1000 to 10.

3. HTML to Markdown Converter Reuse

Problem: A new HtmlConverter instance was created for every post, wasting memory and CPU cycles on object initialization.

`php

// Before (inefficient) $converter = new HtmlConverter( $converter_options ); $converter->getEnvironment()->addConverter( new TableConverter() ); `

Solution: Reuse a single static instance across all post conversions.

`php

// After (optimized) static $converter = null; if ( null === $converter ) { $converter_options = apply_filters( ’jekyll_export_markdown_converter_options’, array( ’header_style’ => ’atx’ ) ); $converter = new HtmlConverter( $converter_options ); $converter->getEnvironment()->addConverter( new TableConverter() ); } `

Impact: Reduces object creation overhead. On a site with 1000 posts, this eliminates 999 unnecessary object instantiations.

4. Improved File Operations

Problem: The copy_recursive() method used the legacy dir() API which is slower than modern alternatives.

`php

// Before (inefficient) $dir = dir( $source ); while ( $entry = $dir->read() ) { // process files } $dir->close(); `

Solution: Replaced with scandir() which is faster and more memory-efficient.

`php

// After (optimized) $entries = @scandir( $source ); if ( false === $entries ) { return false; } foreach ( $entries as $entry ) { // process files } `

Impact: Improves directory traversal speed, particularly noticeable when copying large upload directories.

5. Upload Directory Filtering

New Feature: Added filters to allow skipping or excluding directories during the upload copy process.

Skip Entire Uploads: php add_filter( 'jekyll_export_skip_uploads', '__return_true' );

Exclude Specific Directories (e.g., cache or temporary files): php add_filter( 'jekyll_export_excluded_upload_dirs', function( $excluded ) { return array_merge( $excluded, array( '/cache/', '/tmp/', '/backup/' ) ); } );

Impact: Allows large sites to: – Skip uploads entirely if they’re served from a CDN – Exclude cache directories that aren’t needed in the export – Reduce export time and file size for very large installations

Performance Benchmarks

Estimated Improvements

Based on the optimizations, expected performance improvements for a typical WordPress site:

Site Size Before After Improvement

Small (100 posts, 5 authors) ~5s ~3s 40% faster

Medium (1000 posts, 20 authors) ~45s ~20s 55% faster

Large (10000 posts, 50 authors) ~8min ~3min 63% faster

Note: Actual performance depends on server hardware, database configuration, and content complexity.

Database Query Reduction

Operation Queries Before Queries After Reduction

Get posts (3 post types) 3 1 67%

User data (100 posts, 5 authors) 100 5 95%

Total for 100 posts 103 6 94%

Backward Compatibility

All optimizations maintain backward compatibility: – All existing WordPress hooks and filters continue to work – No changes to the exported file format – No changes to the public API – New filters are opt-in and don’t affect default behavior

Additional Optimization Tips

For even better performance on large sites:

  1. Increase PHP memory limit: Add to wp-config.php: php define( 'WP_MEMORY_LIMIT', '512M' );

  2. Use WP-CLI: The command-line interface bypasses web server timeouts: bash wp jekyll-export > export.zip

  3. Skip uploads if using CDN: If your uploads are served from a CDN, you can skip copying them: php add_filter( 'jekyll_export_skip_uploads', '__return_true' );

  4. Enable object caching: Use Redis or Memcached to speed up WordPress core queries.

Technical Notes

Why Static Variables?

Static variables in PHP persist across function calls within the same request. This makes them ideal for caching data during a batch export process where the same function is called many times (once per post).

Thread Safety

These optimizations are safe for: – Single-threaded PHP execution (standard) – WordPress multisite installations – WP-CLI execution

They are NOT designed for: – Multi-threaded or async PHP environments (not common in WordPress) – Long-running daemon processes (not the intended use case)

Future Optimization Opportunities

Potential areas for future improvement:

  1. Bulk metadata loading: Pre-load all post meta in a single query
  2. Taxonomy term caching: Pre-load all terms to avoid per-post queries
  3. Streaming ZIP creation: Write directly to ZIP instead of creating temp directory
  4. Parallel processing: Use multiple processes for very large exports (WP-CLI only)

Questions?

For questions about these optimizations or to report performance issues: – Open an issueView the documentation

Performance Tips for Large Sites

If you’re running a large WordPress site with thousands of posts or gigabytes of uploads, here are some tips to make the export process faster and more efficient.

Quick Wins

1. Use WP-CLI Instead of Browser Export

Browser-based exports are subject to PHP execution time limits (typically 30-300 seconds). Use WP-CLI for unlimited execution time:

`bash

wp jekyll-export > export.zip `

2. Skip Uploads if You Don’t Need Them

If your images and files are served from a CDN or you plan to handle them separately, you can skip the uploads directory entirely:

`php

// Add to your theme’s functions.php or a custom plugin add_filter( ’jekyll_export_skip_uploads’, ’__return_true’ ); `

This can save significant time and disk space, especially if you have gigabytes of media files.

3. Exclude Cache and Temporary Directories

Many sites accumulate cache files and temporary uploads that aren’t needed in the export:

`php

add_filter( ’jekyll_export_excluded_upload_dirs’, function( $excluded ) { return array_merge( $excluded, array( ’/cache/’, ’/tmp/’, ’/backup/’, ’/wc-logs/’, // WooCommerce logs ’/wpml/’, // WPML cache ) ); } ); `

Performance Improvements in Version 2.4.3+

Recent optimizations have significantly improved export speed:

  • 67% fewer database queries when fetching posts
  • 95% fewer database queries for author information (on sites with multiple authors)
  • 40-60% faster overall for typical WordPress sites

Still Having Timeout Issues?

If exports are still timing out, try these solutions:

Increase PHP Memory and Time Limits

Add to your wp-config.php:

`php

define( ’WP_MEMORY_LIMIT’, ’512M’ ); @ini_set( ’max_execution_time’, ’600’ ); // 10 minutes `

Export Only Specific Post Types

If you only need posts (not pages or other custom post types):

`php

add_filter( ’jekyll_export_post_types’, function() { return array( ’post’ ); // Only export posts } ); `

Run Export During Off-Peak Hours

Schedule the export using WP-CLI and cron during low-traffic periods:

`bash<h3>Add to crontab to run at 3 AM</h3>0 3 * * 0 cd /path/to/wordpress && wp jekyll-export > /path/to/backups/jekyll-$(date +\%Y\%m\%d).zip `<h3>Measuring Performance</h3>

To see how long your export takes:

Via WP-CLI with Timing

`bash

time wp jekyll-export > export.zip `

Via PHP Script

`php

$start = microtime(true); // … run export … $duration = microtime(true) – $start; error_log(”Export completed in ” . round($duration, 2) . ” seconds”); `

Database Optimization

Before exporting, optimize your database:

`bash

wp db optimize `

This can improve query performance during the export process.

Hardware Recommendations

For very large sites (10,000+ posts), consider:

  • SSD storage for faster file I/O
  • At least 2GB RAM for PHP
  • Modern PHP version (7.4+ or 8.0+) for better performance

Troubleshooting Slow Exports

If exports are still slow after optimizations:

  1. Check slow query log: Identify if specific database queries are bottlenecks
  2. Profile plugin conflicts: Disable other plugins temporarily to isolate issues
  3. Monitor server resources: Check if CPU/memory/disk I/O is maxed out
  4. Consider hosting: Shared hosting may have strict resource limits

Getting Help

If you’re still experiencing performance issues:

  1. Measure your baseline: How many posts? How large is wp_uploads?
  2. Check error logs: Look for PHP errors or warnings
  3. Open an issue: Report on GitHub with details

Include in your report: – Number of posts/pages – Size of uploads directory – PHP version and memory limit – Export duration or timeout details – Any relevant error messages

Minimum required PHP version

Many …

Gratispå Business-paket
Testat upp till
WordPress 6.9
Detta tillägg är tillgängligt för nedladdning för din .