{"id":346195,"date":"2026-07-28T20:25:17","date_gmt":"2026-07-28T20:25:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/distan\/"},"modified":"2026-08-08T13:54:27","modified_gmt":"2026-08-08T13:54:27","slug":"distan","status":"publish","type":"plugin","link":"https:\/\/es-uy.wordpress.org\/plugins\/distan\/","author":23511001,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.6","stable_tag":"1.1.6","tested":"7.0.3","requires":"6.0","requires_php":"8.0","requires_plugins":null,"header_name":"Distan","header_author":"Youhei Okubo","header_description":"dist \u3067\u958b\u767a\u3059\u308b\u3001WordPress\u9759\u7684\u30b5\u30a4\u30c8\u30b8\u30a7\u30cd\u30ec\u30fc\u30bf\u30fc\u3002HTML\u7d0d\u54c1\u6848\u4ef6\u306e\u305f\u3081\u306b\u3001WordPress\u3092\u5236\u4f5c\u74b0\u5883\u3068\u3057\u3066\u4f7f\u3044\u3001\u4f59\u8a08\u306a\u3082\u306e\u3092\u542b\u307e\u306a\u3044\u9759\u7684HTML\u3092\u66f8\u304d\u51fa\u3057\u307e\u3059\u3002","assets_banners_color":"333333","last_updated":"2026-08-08 13:54:27","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/okuboyouhei\/distan","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":372,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.9.13":{"tag":"0.9.13","author":"youheiokubo","date":"2026-07-28 20:24:46"},"0.9.14":{"tag":"0.9.14","author":"youheiokubo","date":"2026-07-30 03:46:28"},"0.9.15":{"tag":"0.9.15","author":"youheiokubo","date":"2026-07-30 04:08:23"},"0.9.16":{"tag":"0.9.16","author":"youheiokubo","date":"2026-07-30 20:14:00"},"0.9.17":{"tag":"0.9.17","author":"youheiokubo","date":"2026-08-01 00:42:51"},"0.9.18":{"tag":"0.9.18","author":"youheiokubo","date":"2026-08-01 22:46:19"},"1.0.0":{"tag":"1.0.0","author":"youheiokubo","date":"2026-08-02 03:11:37"},"1.1.0":{"tag":"1.1.0","author":"youheiokubo","date":"2026-08-03 12:22:45"},"1.1.1":{"tag":"1.1.1","author":"youheiokubo","date":"2026-08-05 10:10:46"},"1.1.3":{"tag":"1.1.3","author":"youheiokubo","date":"2026-08-05 21:05:02"},"1.1.4":{"tag":"1.1.4","author":"youheiokubo","date":"2026-08-06 03:26:29"},"1.1.5":{"tag":"1.1.5","author":"youheiokubo","date":"2026-08-06 03:55:15"},"1.1.6":{"tag":"1.1.6","author":"youheiokubo","date":"2026-08-08 13:54:27"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3626410,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3626410,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3626410,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3626410,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.9.13","0.9.14","0.9.15","0.9.16","0.9.17","0.9.18","1.0.0","1.1.0","1.1.1","1.1.3","1.1.4","1.1.5","1.1.6"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[19981,141196,169165,274858,57011],"plugin_category":[59],"plugin_contributors":[266517],"plugin_business_model":[],"class_list":["post-346195","plugin","type-plugin","status-publish","hentry","plugin_tags-deploy","plugin_tags-headless","plugin_tags-jamstack","plugin_tags-static-export","plugin_tags-static-site-generator","plugin_category-utilities-and-tools","plugin_contributors-youheiokubo","plugin_committers-youheiokubo"],"banners":{"banner":"https:\/\/ps.w.org\/distan\/assets\/banner-772x250.png?rev=3626410","banner_2x":"https:\/\/ps.w.org\/distan\/assets\/banner-1544x500.png?rev=3626410","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/distan\/assets\/icon-128x128.png?rev=3626410","icon_2x":"https:\/\/ps.w.org\/distan\/assets\/icon-256x256.png?rev=3626410","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>Distan turns WordPress into a build environment for static HTML deliverables. You author in WordPress, then export clean, self-contained HTML that runs anywhere. No WordPress, no PHP, and no database are required on the production server.<\/p>\n\n<p>Most static-export plugins mirror a <em>live<\/em> WordPress site and keep it running behind the scenes. Distan is built for the opposite case: the HTML files themselves are the deliverable, and WordPress stays on the developer's side and is never published. When an update is needed, you edit in WordPress, regenerate, and hand over the difference. (We think of it as \"headloss\": the head \u2014 the static HTML \u2014 goes to production, while the body \u2014 WordPress \u2014 stays in the workshop.)<\/p>\n\n<p>This suits people who deliver <em>files<\/em>, not a CMS \u2014 for example a recruitment site placed under a subdirectory of an existing corporate site, where adding another WordPress install is not an option.<\/p>\n\n<p><strong>How Distan is different<\/strong><\/p>\n\n<ul>\n<li><strong>It does not deploy for you, and holds no credentials.<\/strong> Distan writes files and stops there. Deployment is wired through hooks (<code>distan_after_generate<\/code>, <code>distan_dispatch<\/code>), so you connect your own rsync, git push, FTP, or CI. Nothing to trust with your server keys.<\/li>\n<li><strong>It enumerates, it does not crawl.<\/strong> Distan generates the pages WordPress already knows about, rather than spidering links. The result is predictable and light \u2014 no queue, no database tables, no build daemon.<\/li>\n<li><strong>It stays small.<\/strong> No bundled framework, no vendor tree, no schema migrations. Easy to read, easy to audit, and nothing left behind when you remove it.<\/li>\n<\/ul>\n\n<p><strong>What it does<\/strong><\/p>\n\n<ul>\n<li>Generates every published page, the posts archive with pagination, category archives with pagination, and a 404 page.<\/li>\n<li>Rewrites internal links as document-relative paths, so the output can be opened directly and placed in any directory.<\/li>\n<li>Flattens WordPress paths: theme files move to <code>assets\/<\/code>, uploads move to <code>media\/<\/code>. The <code>wp-content<\/code> directory disappears from the deliverable.<\/li>\n<li>Cleans the HTML: removes the generator tag, REST API links, oEmbed, emoji scripts, speculative loading rules, and the development <code>noindex<\/code>.<\/li>\n<li>Keeps cache-busting query strings on assets, so file names stay stable and can be overwritten over FTP.<\/li>\n<li>Optional <code>sitemap.xml<\/code> and a minimal <code>robots.txt<\/code>, built from the pages actually generated, using the production URL.<\/li>\n<li>Optional Markdown export (<code>content.md<\/code>) that combines every page's main content into one file \u2014 handy for feeding a site into AI tools (Gemini Notebook \/ NotebookLM, and similar).<\/li>\n<li>Optionally bundle extra files or directories that nothing links to (<code>distan_extra_assets<\/code>) \u2014 for assets referenced only from inside a script, such as a JSON file fetched by JavaScript.<\/li>\n<li>Reports a diff after each run: which files were added, and which need to be removed from production.<\/li>\n<li>Audits links and reports any whose target was not generated.<\/li>\n<li>Downloads the result as a ZIP.<\/li>\n<\/ul>\n\n<p><strong>Requirements<\/strong><\/p>\n\n<p>The only hard requirement is loopback HTTP (the site being able to request itself). No direct SQL, no <code>exec()<\/code>, no external binaries, no external APIs. It runs on Local, MAMP, XAMPP, DDEV, Docker, or shared hosting, and works offline.<\/p>\n\n<p><strong>Not for every case<\/strong><\/p>\n\n<p>Distan is not suitable when the client updates the site themselves, when forms, search, comments, or membership are required, or for sites of only a few pages. If you need built-in deployment to many destinations, scheduled or change-based publishing, or managed static hosting, a larger tool such as Simply Static or Staatic will fit better.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin to <code>wp-content\/plugins\/<\/code>, or install the ZIP from Plugins \u2192 Add New \u2192 Upload.<\/li>\n<li>Activate it.<\/li>\n<li>Open the <strong>Distan<\/strong> menu, check the environment, set the public URL, and generate.<\/li>\n<\/ol>\n\n<p>The output is written to <code>wp-content\/uploads\/distan\/dist\/<\/code>.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20production%20server%20need%20wordpress%3F\"><h3>Does the production server need WordPress?<\/h3><\/dt>\n<dd><p>No. The output is static HTML. That is the point: there is no application to attack or maintain on the production server.<\/p><\/dd>\n<dt id=\"how%20do%20i%20deploy%20the%20result%3F\"><h3>How do I deploy the result?<\/h3><\/dt>\n<dd><p>Distan does not deploy for you and never stores server credentials. It fires <code>distan_after_generate<\/code> when a run finishes, and an optional <code>distan_dispatch<\/code> action when you press the deploy button after reviewing the output. Hook either one to your own rsync, git push, FTP, or CI step. You can also just download the ZIP.<\/p><\/dd>\n<dt id=\"do%20template%20functions%20work%3F\"><h3>Do template functions work?<\/h3><\/dt>\n<dd><p>Yes. Generation sends a normal HTTP request per page, so <code>get_header()<\/code>, <code>get_template_part()<\/code>, template hierarchy, conditional tags, and custom fields all behave exactly as they do on the live site.<\/p><\/dd>\n<dt id=\"what%20does%20not%20work%3F\"><h3>What does not work?<\/h3><\/dt>\n<dd><p>Anything that needs server-side processing: search forms, comment submission, contact forms, and membership. Values that depend on the current time (a copyright year printed with <code>date('Y')<\/code>, relative dates) are frozen at generation time.<\/p><\/dd>\n<dt id=\"a%20script%20fetches%20a%20file%20%28e.g.%20json%29%20that%20is%20not%20being%20copied.%20why%3F\"><h3>A script fetches a file (e.g. JSON) that is not being copied. Why?<\/h3><\/dt>\n<dd><p>Distan collects assets by reading the generated HTML and CSS, so a path built inside a script \u2014 <code>fetch('..\/assets\/json\/data.json')<\/code> \u2014 is invisible to it and the file is left out. List the file or directory with the <code>distan_extra_assets<\/code> filter to bundle it anyway; its path is flattened the same way as other theme assets, so the relative fetch keeps working.<\/p><\/dd>\n<dt id=\"can%20i%20export%20the%20content%20for%20ai%20tools%3F\"><h3>Can I export the content for AI tools?<\/h3><\/dt>\n<dd><p>Yes. Enable the Markdown export and Distan writes <code>content.md<\/code>, combining every page's main content into one file with production URLs \u2014 ready to hand to an AI notebook or a retrieval index. An optional <code>content.local.md<\/code> keeps development URLs.<\/p><\/dd>\n<dt id=\"can%20i%20open%20the%20generated%20files%20directly%3F\"><h3>Can I open the generated files directly?<\/h3><\/dt>\n<dd><p>Yes, with a classic theme. Block themes load their JavaScript as ES modules, which browsers refuse to load over <code>file:\/\/<\/code>. HTML, CSS, images, and links work either way; use a local server for a complete preview.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20block%20%28full%20site%20editing%29%20themes%3F\"><h3>Does it work with block (Full Site Editing) themes?<\/h3><\/dt>\n<dd><p>Yes. Classic and block themes are both supported, including the block navigation's interactivity, importmap paths, and module files.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.6<\/h4>\n\n<ul>\n<li>Added: the distan_extra_assets filter, to bundle files or directories that no page links to. Distan collects assets by inspecting the generated HTML and CSS, so a file referenced only from inside a script (for example fetch('..\/assets\/json\/data.json')) is not found and is left out. Listing a file or directory here includes it anyway, through the same pipeline as linked assets: the theme (and uploads) path is flattened the same way, so a script that fetches a relative path keeps working; the file is recorded in the report and is never removed by the cleanup; executable extensions are still refused; and anything resolving outside the WordPress root is skipped. URLs inside these files are not rewritten, so they suit data files such as JSON. The default remains reference-based \u2014 nothing extra is copied unless you list it.<\/li>\n<\/ul>\n\n<h4>1.1.5<\/h4>\n\n<ul>\n<li>Added: a guard against two people (or two browser tabs) starting a generation at the same time. Progress and output are tracked in a single shared job and written to one output directory, so overlapping runs previously corrupted each other. Starting a run while one is already in progress is now refused with a message naming who started it and when. A run that was abandoned mid-way (browser closed) is detected as stale after a couple of minutes (filter distan_job_stale_after) so it never blocks future runs.<\/li>\n<\/ul>\n\n<h4>1.1.4<\/h4>\n\n<ul>\n<li>Fixed: on top-level pages, the import map address for block-theme modules (e.g. @wordpress\/interactivity, used by the navigation block) was written as a bare relative path (wp-includes\/\u2026) with no .\/ prefix. An import map address that is a relative URL must start with \/, .\/ or ..\/, so browsers treated the bare form as a null value and every module import on the page failed (\"Failed to resolve module specifier \u2026 blocked by a null value\"). Deeper pages already got a ..\/ prefix and worked; top-level pages now get .\/ so interactive block-theme features work in relative-link mode too. Sites delivered with an absolute public URL were unaffected.<\/li>\n<\/ul>\n\n<h4>1.1.3<\/h4>\n\n<ul>\n<li>Fixed: pages with a multibyte (e.g. Japanese) slug are now written to disk under their real decoded name (\u30c6\u30b9\u30c8\/index.html) instead of a literal percent-encoded name (%E3%83%86\u2026\/index.html), while links keep the percent-encoded form. A browser or web server decodes an href before it looks for the file, so the previous literal name only matched when a URL was double-encoded: such pages opened by double-click but broke when reached through their own link (and were silently served by WordPress when the export happened to sit inside a live site). Assets already followed this rule; pages now match. The link audit decodes links before checking the filesystem so it does not report false broken links.<\/li>\n<\/ul>\n\n<h4>1.1.2<\/h4>\n\n<ul>\n<li>Changed: the Markdown export (content.md) is now streamed one page at a time instead of collecting every page's text and joining it at the end. On sites with a very large number of pages this keeps memory flat and stops the job data from growing with page count, the same way binary assets were made stream-copied in 1.1.1. The output file is byte-for-byte identical to before.<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Changed: binary assets (images, PDFs, fonts, video) are now stream-copied instead of read fully into memory, so large files no longer inflate memory during generation. Stylesheets are still read in memory because their url() references are rewritten.<\/li>\n<li>Added: files above a size threshold (10 MB by default, filter distan_large_file_threshold) are listed in the report as \"large files\". They are still copied automatically; the list is a heads-up for delivery size and CDN decisions, not a manual step.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Added: optional sitemap.xml generation. Built from the pages that were actually generated, using the production URL, in the standard format Google Search Console accepts. Author and date archives are never collected, so IDs like ?author=1 cannot leak into it.<\/li>\n<li>Added: sitemap exclusions by slug prefix (\/private\/ excludes everything under it) or substring (draft excludes any URL containing it), via a setting or the distan_sitemap_exclude filter.<\/li>\n<li>Added: optional minimal robots.txt (Allow: \/, plus a Sitemap: line when the sitemap is enabled).<\/li>\n<li>The development-URL audit now also scans sitemap.xml and robots.txt, so a leftover development host in them is reported like anywhere else.<\/li>\n<li>The diff-based cleanup now keeps sitemap.xml, robots.txt and the Markdown files, so they are not removed on the next run.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First stable release. Classic and block (FSE) themes are both supported and have been verified across the hard cases: internal links and multibyte slugs, JSON-LD and canonical\/OGP, srcset and path flattening, CSS url() and webfonts, custom post types and fields, diff-based cleanup, Markdown export, hooks, and a safe uninstall that never touches the delivered files.<\/li>\n<\/ul>\n\n<h4>0.9.18<\/h4>\n\n<ul>\n<li>Fixed: multibyte (e.g. Japanese) page slugs in internal links were corrupted (0x80-0x9F bytes turned into \"_\"), breaking the link. WordPress emits same-origin links as raw UTF-8; Distan now normalises every link path to a consistent percent-encoded form, so multibyte slugs survive intact in both the output filename and the rewritten link.<\/li>\n<li>Added: the generation report and the environment check now surface how many development-domain URLs remain in the output (e.g. in JSON-LD or canonical\/OGP tags when the Production URL is unset), so a forgotten Production URL is caught before and after delivery.<\/li>\n<li>Fixed: the link audit reported existing pages with multibyte slugs as broken links, because it looked for a decoded name while the file is written with a percent-encoded name. The audit now matches the encoded form.<\/li>\n<li>Fixed: url() references inside external CSS were copied verbatim, so theme fonts\/images went uncopied and uploads-absolute paths (url(\"\/wp-content\/uploads\/\u2026\")) leaked into delivered files. CSS url() is now resolved, flattened (theme -&gt; assets\/, uploads -&gt; media\/), its targets copied, and the reference rewritten relative to the stylesheet.<\/li>\n<\/ul>\n\n<h4>0.9.17<\/h4>\n\n<ul>\n<li>Added the <code>distan_dispatch<\/code> action hook and an optional manual \"\u30c7\u30d7\u30ed\u30a4\" (dispatch) button on the generate screen. After reviewing the generated output, pressing the button fires <code>distan_dispatch<\/code> so a project can promote the reviewed build (git push, rsync, a build webhook). Distan stores no approval state \u2014 only the time of the last dispatch, shown on screen. The button is off by default; enable it in settings.<\/li>\n<\/ul>\n\n<h4>0.9.16<\/h4>\n\n<ul>\n<li>Added the <code>distan_after_generate<\/code> action hook, fired when a generation run completes, for wiring up automatic deployment (git push, rsync, build webhooks, etc.).<\/li>\n<\/ul>\n\n<h4>0.9.15<\/h4>\n\n<ul>\n<li>Redesigned the admin screen (\"Distan Dispatch\"): a packing-slip worksheet look with monospace figures for counts and file paths, a single stamp-green accent, and improved contrast.<\/li>\n<\/ul>\n\n<h4>0.9.14<\/h4>\n\n<ul>\n<li>Added the <code>distan_url_replacements<\/code> filter for custom production URL replacements (applied across the whole output, including JSON-LD).<\/li>\n<li>Added an optional Markdown export (content.md) that combines every page's main content into one file for AI tools such as Gemini Notebook (formerly NotebookLM). URLs are rewritten to the production site URL; an optional content.local.md keeps development URLs.<\/li>\n<\/ul>\n\n<h4>0.9.13<\/h4>\n\n<ul>\n<li>Initial public release.<\/li>\n<\/ul>","raw_excerpt":"Author in WordPress, deliver static HTML \u2014 built for handing over files, not a CMS. No database, and no deploy credentials, in the deliverable.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/346195","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=346195"}],"author":[{"embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/youheiokubo"}],"wp:attachment":[{"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=346195"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=346195"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=346195"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=346195"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=346195"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/es-uy.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=346195"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}