{"id":305672,"date":"2026-05-10T20:33:17","date_gmt":"2026-05-10T20:33:17","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/html-page-publisher\/"},"modified":"2026-08-28T18:23:27","modified_gmt":"2026-08-28T18:23:27","slug":"html-page-publisher","status":"publish","type":"plugin","link":"https:\/\/zgh.wordpress.org\/plugins\/html-page-publisher\/","author":23084196,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.5.0","stable_tag":"1.5.0","tested":"7.1","requires":"5.9","requires_php":"7.4","requires_plugins":null,"header_name":"HTML Page Publisher","header_author":"Hossein Karami","header_description":"Upload standalone HTML files (including AI-generated pages from Claude Design, ChatGPT, Gemini, v0, Bolt) and publish them as landing pages at a configurable URL. Optional subdomain routing.","assets_banners_color":"7c7baa","last_updated":"2026-08-28 18:23:27","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/buymeacoffee.com\/hosseinkarami?utm_source=wordpress.org&utm_medium=plugin-page&utm_campaign=donate&utm_content=readme-donate","header_plugin_uri":"https:\/\/github.com\/HosseinKarami\/html-page-publisher","header_author_uri":"https:\/\/hosseinkarami.com","rating":0,"author_block_rating":0,"active_installs":200,"downloads":1070,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"hosseinkarami","date":"2026-05-10 20:32:45","revision":3528013},"1.1.0":{"tag":"1.1.0","author":"hosseinkarami","date":"2026-05-10 23:03:01","revision":3528082},"1.2.0":{"tag":"1.2.0","author":"hosseinkarami","date":"2026-05-17 14:55:25","revision":3534624},"1.2.1":{"tag":"1.2.1","author":"hosseinkarami","date":"2026-05-21 15:46:08","revision":3542297},"1.5.0":{"tag":"1.5.0","author":"hosseinkarami","date":"2026-08-28 18:23:27","revision":3670802}},"upgrade_notice":{"1.5.0":"<p>Your pages keep their URLs. Coming from 1.2.x: replacing an existing page now needs an explicit checkbox, links into the uploads folder redirect to the page URL, and pages gain a canonical link (Settings). Adds ZIP bundles, drafts, custom paths, analytics snippets, sitemap and a REST API.<\/p>","1.4.0":"<p>ZIP bundles, drafts with preview links, custom paths and front-page mapping, global analytics snippets, sitemap\/noindex, rename and duplicate. Existing pages keep their URLs.<\/p>","1.3.0":"<p>Storage folders are now protected from direct access, pages get real caching headers and a canonical URL. Existing pages keep their URLs. One behaviour change: re-uploading to an existing slug now requires ticking a Replace checkbox instead of overwriting silently.<\/p>","1.2.0":"<p>Edit published pages and manage their images right in the dashboard, with version history and one-click restore. Fully backward-compatible.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3528012,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3528012,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3528012,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3528012,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3528012,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250},"banner.svg":{"filename":"banner.svg","revision":3528012,"resolution":false,"location":"assets","locale":false}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0","1.2.0","1.2.1","1.5.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3670802,"resolution":"1","location":"assets","locale":"","width":1440,"height":865},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3670802,"resolution":"2","location":"assets","locale":"","width":1440,"height":980},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3670802,"resolution":"3","location":"assets","locale":"","width":1440,"height":625},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3670802,"resolution":"4","location":"assets","locale":"","width":1440,"height":1360},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3670802,"resolution":"5","location":"assets","locale":"","width":1440,"height":484}},"screenshots":{"1":"Upload a new page or ZIP bundle and see everything you have published, with each page's title, status and public URL.","2":"Edit a published page in the dashboard with the native WordPress code editor.","3":"Files &amp; Assets: every file of an imported bundle \u2014 images, CSS, JS, fonts \u2014 with replace and delete in place.","4":"Settings: URL prefix, optional subdomain, global snippets, and a live check that storage protection is active.","5":"Page settings: draft\/published, custom path or front page, noindex, rename and duplicate."}},"plugin_section":[],"plugin_tags":[2353,278118,246,808,251091],"plugin_category":[],"plugin_contributors":[236828],"plugin_business_model":[],"class_list":["post-305672","plugin","type-plugin","status-publish","hentry","plugin_tags-ai","plugin_tags-claude-design","plugin_tags-html","plugin_tags-landing-page","plugin_tags-static-html","plugin_contributors-hosseinkarami","plugin_committers-hosseinkarami"],"banners":{"banner":"https:\/\/ps.w.org\/html-page-publisher\/assets\/banner-772x250.png?rev=3528012","banner_2x":"https:\/\/ps.w.org\/html-page-publisher\/assets\/banner-1544x500.png?rev=3528012","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/html-page-publisher\/assets\/icon.svg?rev=3528012","icon":"https:\/\/ps.w.org\/html-page-publisher\/assets\/icon.svg?rev=3528012","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/html-page-publisher\/assets\/screenshot-1.png?rev=3670802","caption":"Upload a new page or ZIP bundle and see everything you have published, with each page's title, status and public URL."},{"src":"https:\/\/ps.w.org\/html-page-publisher\/assets\/screenshot-2.png?rev=3670802","caption":"Edit a published page in the dashboard with the native WordPress code editor."},{"src":"https:\/\/ps.w.org\/html-page-publisher\/assets\/screenshot-3.png?rev=3670802","caption":"Files &amp; Assets: every file of an imported bundle \u2014 images, CSS, JS, fonts \u2014 with replace and delete in place."},{"src":"https:\/\/ps.w.org\/html-page-publisher\/assets\/screenshot-4.png?rev=3670802","caption":"Settings: URL prefix, optional subdomain, global snippets, and a live check that storage protection is active."},{"src":"https:\/\/ps.w.org\/html-page-publisher\/assets\/screenshot-5.png?rev=3670802","caption":"Page settings: draft\/published, custom path or front page, noindex, rename and duplicate."}],"raw_content":"<!--section=description-->\n<p>HTML Page Publisher lets you drop a standalone HTML file into WordPress and have it served as a landing page at a clean URL such as <code>https:\/\/example.com\/pages\/your-slug\/<\/code>. No theme changes, no FTP, no page builder.<\/p>\n\n<p>It is built for the \"I made this page with an AI tool, now how do I get it onto my site?\" moment. It includes purpose-built cleanup for <strong>Claude Design<\/strong> exports, and works with any self-contained HTML page \u2014 from ChatGPT, Gemini, or written by hand. (Tools that export React or Vite projects, such as v0 and Bolt, first need to be built or exported to static HTML.)<\/p>\n\n<p><strong>Key features<\/strong><\/p>\n\n<ul>\n<li><strong>Upload and publish<\/strong>: one HTML file plus its images \u2014 or a whole ZIP bundle (index.html + css\/js\/images\/fonts) \u2014 and you get a public URL<\/li>\n<li><strong>Drafts and previews<\/strong>: save a page as a draft, share a preview link that works without logging in, publish when ready<\/li>\n<li><strong>Custom paths and front page<\/strong>: serve a page at <code>\/promo\/<\/code> instead of <code>\/pages\/promo\/<\/code>, or make it the site's front page<\/li>\n<li><strong>Global snippets<\/strong>: add GA4\/GTM\/pixel, fonts or a consent banner to every page from Settings (pages can opt out)<\/li>\n<li><strong>SEO<\/strong>: pages are listed in the WordPress XML sitemap, can be marked noindex, and get a canonical link<\/li>\n<li><strong>Rename and duplicate<\/strong>: change a slug (the old URL redirects) or copy a page as a new draft<\/li>\n<li><strong>REST API and WP-CLI<\/strong>: publish from scripts, CI, or an AI agent \u2014 <code>POST \/wp-json\/htmlpp\/v1\/pages<\/code> with an application password, or <code>wp htmlpp publish<\/code>. A Claude Code skill is included in the <a href=\"https:\/\/github.com\/HosseinKarami\/html-page-publisher\">GitHub repository<\/a>.<\/li>\n<li><strong>Built-in HTML editor<\/strong>: edit a published page in the dashboard with the native WordPress code editor (syntax highlighting)<\/li>\n<li><strong>Version history<\/strong>: every save keeps the previous version; restore any earlier version with one click (restoring is itself undoable)<\/li>\n<li><strong>File management<\/strong>: add, replace, or delete a page's images, CSS, JS, fonts and other files in place \u2014 Replace keeps the original filename so existing references keep working<\/li>\n<li><strong>Clean Claude Design exports<\/strong>: strips the export-time runtime wrappers Claude Design adds so the published page is pure static HTML; add rules for other tools with the <code>htmlpp_sanitizer_rules<\/code> filter<\/li>\n<li><strong>Configurable URL prefix<\/strong>: default <code>\/pages\/your-slug\/<\/code>, change to anything you like (e.g. <code>\/resources\/<\/code>, <code>\/guides\/<\/code>)<\/li>\n<li><strong>Optional subdomain routing<\/strong>: point <code>sales.example.com<\/code> at your site and pages appear at <code>sales.example.com\/your-slug\/<\/code><\/li>\n<li><strong>Works on subdirectory installs<\/strong> (e.g. <code>example.com\/blog\/pages\/your-slug\/<\/code>)<\/li>\n<li><strong>Cache-friendly<\/strong>: ETag and Last-Modified headers with conditional (304) and HEAD support, so browsers and CDNs revalidate cheaply while edits still show immediately<\/li>\n<li><strong>Protected storage<\/strong>: pages are only served at their public URL; direct access to the uploads folder and to version-history snapshots is blocked, and Settings shows whether your web server honours the protection<\/li>\n<li><strong>Safe by default<\/strong>: nonce-protected forms, capability checks, path-traversal guards, strict file-extension filtering, and no silent overwrites of live pages<\/li>\n<li><strong>Extensible<\/strong>: actions and filters for add-ons (<code>htmlpp_loaded<\/code>, <code>htmlpp_page_published<\/code>, <code>htmlpp_before_serve<\/code>, <code>htmlpp_cache_max_age<\/code>, <code>htmlpp_allowed_asset_mimes<\/code>, and more)<\/li>\n<\/ul>\n\n<p><strong>Use cases<\/strong><\/p>\n\n<ul>\n<li>Publishing landing pages generated with Claude Design<\/li>\n<li>Publishing any AI-generated or hand-written static HTML page<\/li>\n<li>Sales collateral (rate cards, one-pagers, proposals)<\/li>\n<li>Campaign landing pages and microsites<\/li>\n<li>Rapidly publishing static HTML without touching the theme or FTP<\/li>\n<\/ul>\n\n<p>HTML Page Publisher is an independent project. It is not affiliated with, endorsed by, or sponsored by Anthropic, OpenAI, Google, Vercel, or StackBlitz. Claude, ChatGPT, Gemini, v0 and Bolt are trademarks of their respective owners.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>html-page-publisher<\/code> folder to <code>\/wp-content\/plugins\/<\/code> (or install from Plugins \u2192 Add New).<\/li>\n<li>Activate the plugin from the Plugins screen.<\/li>\n<li>Go to <strong>HTML Pages<\/strong> in the admin sidebar to upload your first page.<\/li>\n<li>(Optional) Visit <strong>HTML Pages \u2192 Settings<\/strong> to change the URL prefix, configure a subdomain, or check storage protection.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"can%20i%20upload%20a%20whole%20folder%20%28css%2C%20js%2C%20images%29%20at%20once%3F\"><h3>Can I upload a whole folder (CSS, JS, images) at once?<\/h3><\/dt>\n<dd><p>Yes. Zip the exported folder \u2014 <code>index.html<\/code> at the top level (or inside a single folder), assets in subfolders \u2014 and upload the <code>.zip<\/code> instead of an <code>.html<\/code> file. Files keep their relative paths, so <code>css\/site.css<\/code> and <code>img\/hero.png<\/code> references work unchanged. Executable files (<code>.php<\/code> etc.), anything outside the allowed types, and any file containing PHP code are skipped and reported. ZIP import needs PHP's ZipArchive extension, which nearly every host provides; if yours does not, upload the HTML and its files separately.<\/p><\/dd>\n<dt id=\"how%20do%20drafts%20and%20previews%20work%3F\"><h3>How do drafts and previews work?<\/h3><\/dt>\n<dd><p>Tick <strong>Save as draft<\/strong> when uploading a new page, or switch a page to <strong>Draft<\/strong> in its Page Settings. Drafts return a 404 to visitors and search engines; administrators can view them, and the <strong>Preview<\/strong> button gives you a link with a secret token you can share with anyone (use <strong>Reset preview link<\/strong> to revoke it). Publish from Page Settings when you are happy.<\/p><\/dd>\n<dt id=\"can%20i%20serve%20a%20page%20at%20the%20site%20root%20or%20a%20custom%20path%3F\"><h3>Can I serve a page at the site root or a custom path?<\/h3><\/dt>\n<dd><p>Yes. In a page's settings, set <strong>Custom path<\/strong> to <code>promo<\/code> to serve it at <code>https:\/\/example.com\/promo\/<\/code>, or to <code>\/<\/code> to make it the front page. If you later change the path, the old one redirects to the page. Paths that belong to existing WordPress pages or posts, core routes, category\/tag\/author bases and post-type archives are refused; other rewrite rules (e.g. from other plugins) are not checked, so choose a path you know is free. The front page keeps working for WordPress's own query-string routes (search, previews, REST, feeds).<\/p><\/dd>\n<dt id=\"how%20do%20i%20add%20google%20analytics%2C%20gtm%20or%20a%20pixel%20to%20every%20page%3F\"><h3>How do I add Google Analytics, GTM or a pixel to every page?<\/h3><\/dt>\n<dd><p><strong>HTML Pages \u2192 Settings \u2192 Global snippets<\/strong>. Paste the tags into the head or footer snippet; they are inserted into every published page when it is served (so re-uploading a page never loses them). Pages can opt out individually.<\/p><\/dd>\n<dt id=\"are%20pages%20in%20my%20xml%20sitemap%3F\"><h3>Are pages in my XML sitemap?<\/h3><\/dt>\n<dd><p>Published pages that are not marked noindex are listed at <code>\/wp-sitemap-htmlpp-1.xml<\/code>, which is linked from WordPress's built-in sitemap index. SEO plugins such as Yoast or Rank Math replace the core sitemap with their own; in that case use the <code>htmlpp_sitemap_entries<\/code> filter (or <code>HTMLPP_Sitemap::entries()<\/code>) to feed their sitemap.<\/p><\/dd>\n<dt id=\"can%20i%20publish%20from%20a%20script%2C%20ci%2C%20or%20claude%20code%3F\"><h3>Can I publish from a script, CI, or Claude Code?<\/h3><\/dt>\n<dd><p>Yes. The plugin exposes a REST API at <code>\/wp-json\/htmlpp\/v1<\/code> (list, create\/replace from an HTML file or ZIP, update HTML or settings, rename, duplicate, files, versions, preview links). Authenticate with an application password (Users \u2192 Profile \u2192 Application Passwords) for an administrator:<\/p>\n\n<pre><code>curl -u \"admin:xxxx xxxx xxxx xxxx\" -F slug=spring-promo -F file=@index.html https:\/\/example.com\/wp-json\/htmlpp\/v1\/pages\n<\/code><\/pre>\n\n<p>With shell access, <code>wp htmlpp publish spring-promo .\/site.zip --overwrite<\/code> does the same. The <a href=\"https:\/\/github.com\/HosseinKarami\/html-page-publisher\">GitHub repository<\/a> ships a Claude Code skill (<code>skills\/publish-to-wordpress<\/code>) so Claude can publish a page it just generated.<\/p><\/dd>\n<dt id=\"which%20ai%20tools%20does%20it%20work%20with%3F\"><h3>Which AI tools does it work with?<\/h3><\/dt>\n<dd><p>Any tool that gives you a single, self-contained HTML file. Claude Design's \"Export as standalone HTML\" is supported directly, including automatic removal of the runtime wrappers it injects. Pages from ChatGPT, Gemini, or written by hand work as-is. Tools such as v0 and Bolt export React\/Vite projects rather than static HTML, so you need to build or export those to plain HTML first.<\/p><\/dd>\n<dt id=\"where%20are%20uploaded%20pages%20stored%3F\"><h3>Where are uploaded pages stored?<\/h3><\/dt>\n<dd><p>In <code>wp-content\/uploads\/html-page-publisher\/&lt;slug&gt;\/<\/code>. Each page has its own directory containing an <code>index.html<\/code> and an <code>assets\/<\/code> folder for images. Version-history snapshots are kept separately in <code>wp-content\/uploads\/html-page-publisher-backups\/&lt;slug&gt;\/<\/code>. Both folders contain an <code>.htaccess<\/code> that denies direct access, so pages are only reachable at their public URL.<\/p><\/dd>\n<dt id=\"i%20use%20nginx.%20is%20my%20storage%20folder%20protected%3F\"><h3>I use nginx. Is my storage folder protected?<\/h3><\/dt>\n<dd><p>nginx ignores <code>.htaccess<\/code>. Pages still work at their public URL, but the raw files could also be fetched from the uploads folder. <strong>HTML Pages \u2192 Settings<\/strong> shows whether direct access is blocked and gives you a ready-made <code>location<\/code> block to add to your nginx server configuration. The same applies to Apache or LiteSpeed configured with <code>AllowOverride None<\/code>.<\/p><\/dd>\n<dt id=\"how%20do%20i%20edit%20a%20page%20or%20change%20its%20files%20after%20publishing%3F\"><h3>How do I edit a page or change its files after publishing?<\/h3><\/dt>\n<dd><p>Open <strong>HTML Pages<\/strong>, click <strong>Edit<\/strong> on a page. You get the native WordPress code editor for the HTML, a Version History panel to restore earlier saves, and a Files &amp; Assets panel to add, replace, or delete the page's images, CSS, JS, fonts and other files without re-uploading the HTML. Use <strong>Replace<\/strong> (rather than uploading a new file) to swap a file while keeping the same name, so existing references in your HTML keep working.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20i%20upload%20a%20slug%20that%20already%20exists%3F\"><h3>What happens if I upload a slug that already exists?<\/h3><\/dt>\n<dd><p>The upload is refused with a message explaining why, unless you tick <strong>Replace the existing page<\/strong>. With the box ticked the new HTML replaces the live page and the previous version is saved to the page's version history. Automated workflows can allow overwrites with the <code>htmlpp_allow_overwrite<\/code> filter.<\/p><\/dd>\n<dt id=\"can%20i%20lock%20down%20editing%3F\"><h3>Can I lock down editing?<\/h3><\/dt>\n<dd><p>Yes. The standard <code>DISALLOW_FILE_EDIT<\/code> (or <code>DISALLOW_FILE_MODS<\/code>) constant in <code>wp-config.php<\/code> disables in-dashboard HTML editing, file changes, renaming and replacing existing pages, the same way it disables WordPress's core file editor. Uploading new pages, duplicating and deleting remain available.<\/p><\/dd>\n<dt id=\"how%20do%20i%20use%20the%20subdomain%20feature%3F\"><h3>How do I use the subdomain feature?<\/h3><\/dt>\n<dd><p>Two steps:<\/p>\n\n<ol>\n<li><strong>DNS<\/strong>: Add an A or CNAME record pointing your subdomain (e.g. <code>sales.yourdomain.com<\/code>) at the same server as your WordPress site.<\/li>\n<li><strong>Web server<\/strong>: Configure your hosting to serve that subdomain from the same WordPress install. On shared hosting with cPanel this is typically an \"addon domain\" or \"subdomain\" option. On managed hosts, you may need to request it from support.<\/li>\n<\/ol>\n\n<p>Then enter the hostname in <strong>HTML Pages \u2192 Settings \u2192 Subdomain<\/strong>. Pages will be served at <code>https:\/\/sales.yourdomain.com\/your-slug\/<\/code> in addition to the regular prefix URL.<\/p><\/dd>\n<dt id=\"is%20it%20safe%20to%20upload%20arbitrary%20html%3F\"><h3>Is it safe to upload arbitrary HTML?<\/h3><\/dt>\n<dd><p>Only users with the <code>manage_options<\/code> capability (administrators by default) can upload pages. Uploaded HTML is served as-is, including any <code>&lt;script&gt;<\/code> tags it contains, so only upload HTML you trust.<\/p><\/dd>\n<dt id=\"will%20uninstalling%20delete%20my%20pages%3F\"><h3>Will uninstalling delete my pages?<\/h3><\/dt>\n<dd><p>No. Uninstalling removes the plugin's settings but leaves the uploaded pages in <code>wp-content\/uploads\/html-page-publisher\/<\/code> (and their version-history snapshots in <code>wp-content\/uploads\/html-page-publisher-backups\/<\/code>) intact, together with their metadata (draft status, custom paths, redirects) so a reinstall does not publish former drafts. Delete those folders and the <code>htmlpp_pages<\/code>, <code>htmlpp_redirects<\/code> and <code>htmlpp_path_redirects<\/code> options manually if you want everything gone.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20caching%20plugins%20or%20a%20cdn%3F\"><h3>Does this work with caching plugins or a CDN?<\/h3><\/dt>\n<dd><p>Yes. Pages are served before WordPress's main query runs, with <code>ETag<\/code> and <code>Last-Modified<\/code> headers; conditional requests get a <code>304 Not Modified<\/code> and <code>HEAD<\/code> requests are supported. The page HTML is sent with <code>max-age=0, must-revalidate<\/code> so edits show immediately; assets get one hour. To let a CDN cache the HTML itself, use the <code>htmlpp_cache_max_age<\/code> filter:<\/p>\n\n<pre><code>add_filter( 'htmlpp_cache_max_age', function ( $seconds, $file, $ext, $is_page ) { return $is_page ? 600 : $seconds; }, 10, 4 );\n<\/code><\/pre><\/dd>\n<dt id=\"can%20i%20add%20cleanup%20rules%20for%20another%20export%20format%3F\"><h3>Can I add cleanup rules for another export format?<\/h3><\/dt>\n<dd><p>Yes. Rules are a named array of PCRE pattern\/replacement pairs:<\/p>\n\n<pre><code>add_filter( 'htmlpp_sanitizer_rules', function ( $rules, $slug, $context ) { $rules['my-tool-banner'] = array( 'pattern' =&gt; '\/&lt;div class=\"made-with\"&gt;.*?&lt;\\\/div&gt;\/is', 'replacement' =&gt; '' ); return $rules; }, 10, 3 );\n<\/code><\/pre>\n\n<p>You can also <code>unset()<\/code> a built-in rule by its key (for example <code>claude-design-cfasync-script<\/code>) if it interferes with your markup.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>New: REST API at <code>\/wp-json\/htmlpp\/v1<\/code> \u2014 list, create\/replace (JSON HTML or multipart HTML\/ZIP + files), update HTML and settings, rename, duplicate, delete, files, version history and restore, preview links. Application-password or cookie+nonce auth; <code>manage_options<\/code> required.<\/li>\n<li>New: WP-CLI \u2014 <code>wp htmlpp list|get|publish|update|delete|duplicate|preview|files|versions|restore<\/code>.<\/li>\n<li>New: <code>HTMLPP_Page_Service<\/code> (<code>htmlpp()-&gt;pages<\/code>) \u2014 one API for every page operation, shared by the admin screens, REST and CLI. Add-ons should use it instead of the storage classes.<\/li>\n<li>New: Claude Code plugin manifest and <code>publish-to-wordpress<\/code> skill in the repository.<\/li>\n<li>Improved: Admin form handlers are thin wrappers over the service; sideloaded files (CLI) are validated exactly like uploads.<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>New: Upload a ZIP bundle (index.html + css\/js\/images\/fonts in subfolders). Files keep their relative paths; executables are skipped and reported.<\/li>\n<li>New: Any file type a page needs can be added in the Files &amp; Assets panel \u2014 CSS, JS, fonts, video, PDF \u2014 not only images.<\/li>\n<li>New: Drafts with shareable preview links (secret token; no login needed). Save as draft on upload or switch status in Page settings.<\/li>\n<li>New: Custom paths \u2014 serve a page at <code>\/promo\/<\/code> or as the site's front page. Paths used by existing WordPress content are refused.<\/li>\n<li>New: Global head\/footer snippets (GA4, GTM, pixels, fonts, consent banners) injected into every published page at serve time, with a per-page opt-out.<\/li>\n<li>New: SEO \u2014 pages are listed in the WordPress XML sitemap, can be marked noindex (X-Robots-Tag + meta), and get a canonical link when they lack one (toggle in Settings).<\/li>\n<li>New: Rename a page's slug (the old URL redirects with a 301) and duplicate a page as a new draft.<\/li>\n<li>New: Pages list shows Draft \/ noindex badges and has a search filter.<\/li>\n<li>New: Changing a page's custom path keeps the old path redirecting; preview links can be reset; the front page mapping leaves WordPress's own query-string routes (search, previews, REST, feeds) alone.<\/li>\n<li>New: Hooks \u2014 <code>htmlpp_page_meta<\/code>, <code>htmlpp_page_meta_defaults<\/code>, <code>htmlpp_page_meta_updated<\/code>, <code>htmlpp_page_status_changed<\/code>, <code>htmlpp_page_created<\/code>, <code>htmlpp_page_renamed<\/code>, <code>htmlpp_page_copied<\/code>, <code>htmlpp_page_duplicated<\/code>, <code>htmlpp_page_html<\/code>, <code>htmlpp_can_preview<\/code>, <code>htmlpp_zip_imported<\/code>, <code>htmlpp_assets_uploaded<\/code>, <code>htmlpp_asset_replaced<\/code>, <code>htmlpp_asset_deleted<\/code>, <code>htmlpp_sitemap_entries<\/code>, <code>htmlpp_reserved_paths<\/code>, <code>htmlpp_path_collides<\/code>, <code>htmlpp_home_reserved_query_vars<\/code>, <code>htmlpp_zip_allowed_extensions<\/code>, <code>htmlpp_zip_max_bytes<\/code>, <code>htmlpp_zip_max_files<\/code>.<\/li>\n<li>Security: ZIP entries are streamed under a size cap, names with executable intermediate extensions are refused, and every entry (not only text files) is scanned for PHP code. Blocked extensions now cover php3\u2013php8, phtm, phps, phar, inc and shtml.<\/li>\n<li>Improved: Deleting a page also removes redirects that pointed at it; uninstall keeps page metadata so a reinstall never publishes former drafts.<\/li>\n<li>Improved: Links straight to <code>\/wp-content\/uploads\/html-page-publisher\/...<\/code> now redirect to the page\u2019s real URL instead of breaking, and <code>\/pages\/your-slug<\/code> (no trailing slash) redirects only when your site\u2019s permalinks use trailing slashes \u2014 otherwise the page is served with a <code>&lt;base&gt;<\/code> tag so its assets still resolve. Both are filterable (<code>htmlpp_canonical_redirect<\/code>).<\/li>\n<li>Security: Publishing raw HTML now also requires the <code>unfiltered_html<\/code> capability, so a multisite site administrator cannot inject scripts a network administrator has not allowed. Override with <code>htmlpp_require_unfiltered_html<\/code> on hardened single-site installs.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Security: Pages are now only reachable at their public URL. The storage and version-history folders get an <code>.htaccess<\/code> that denies direct access, and Settings shows whether your web server honours it (with an nginx snippet if it does not).<\/li>\n<li>New: Real caching headers \u2014 <code>ETag<\/code>, <code>Last-Modified<\/code>, conditional <code>304<\/code> responses and <code>HEAD<\/code> support \u2014 replacing the previous no-cache headers. Filterable via <code>htmlpp_cache_max_age<\/code> \/ <code>htmlpp_cache_control<\/code>.<\/li>\n<li>New: <code>\/pages\/slug<\/code> now redirects to <code>\/pages\/slug\/<\/code> so relative asset references always resolve, and the page has a single canonical URL.<\/li>\n<li>New: Uploading to an existing slug no longer silently overwrites the live page. Tick \"Replace the existing page\" to overwrite; the previous version is kept in the page's history.<\/li>\n<li>New: Pages list shows each page's <code>&lt;title&gt;<\/code>.<\/li>\n<li>New: Sanitizer rules are named and filterable (<code>htmlpp_sanitizer_rules<\/code>), and a rule that fails on very large exports can no longer blank the page.<\/li>\n<li>New: Extension points for add-ons: <code>htmlpp_loaded<\/code>, <code>htmlpp_upgraded<\/code>, <code>htmlpp_page_published<\/code>, <code>htmlpp_page_updated<\/code>, <code>htmlpp_page_deleted<\/code>, <code>htmlpp_before_serve<\/code>, <code>htmlpp_serve_headers<\/code>, <code>htmlpp_mime_map<\/code>, <code>htmlpp_allowed_asset_mimes<\/code>, <code>htmlpp_allow_overwrite<\/code>, <code>htmlpp_editing_disabled<\/code>, <code>htmlpp_htaccess_rules<\/code>, <code>htmlpp_home_path_candidates<\/code>, and an <code>htmlpp()<\/code> accessor. <code>htmlpp_sanitizer_rules<\/code> and <code>htmlpp_sanitize_html<\/code> now also receive the slug and context.<\/li>\n<li>New: The editor warns before you leave with unsaved changes.<\/li>\n<li>Security: Version-history snapshots get unguessable filenames and their own protected folder.<\/li>\n<li>Fixed: Pages are served on subdirectory WordPress installs (e.g. <code>example.com\/blog\/<\/code>).<\/li>\n<li>Fixed: Percent-encoded page and asset URLs resolve correctly.<\/li>\n<li>Fixed: Upload errors caused by PHP size limits are reported with the actual limit instead of a generic failure; per-image failures are listed instead of silently reported as success.<\/li>\n<li>Fixed: Uploads work on hosts where the WordPress filesystem method is not \"direct\", and on Windows servers.<\/li>\n<li>Fixed: Saving the editor after a browser refresh no longer re-submits the form (post\/redirect\/get).<\/li>\n<li>Fixed: An editor submission larger than <code>post_max_size<\/code> is refused instead of wiping the page.<\/li>\n<li>Improved: Admin previews of a page's images load from the main domain, so they work before a subdomain's DNS is live.<\/li>\n<li>Improved: Readme and admin copy now describe accurately which tools are supported.<\/li>\n<li>Compatibility: Tested up to WordPress 7.1.<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Fixed: Other plugins' admin notices no longer render inside the plugin's hero header; they now appear below it via a <code>.wp-header-end<\/code> marker.<\/li>\n<li>Compatibility: Tested up to WordPress 7.0.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>New: In-browser HTML editor for published pages, using the native WordPress code editor (syntax highlighting). No FTP required.<\/li>\n<li>New: Version history \u2014 every save snapshots the previous version; restore any earlier version with one click, and restoring is itself undoable. Retention is filterable via <code>htmlpp_max_backups<\/code> (default 10).<\/li>\n<li>New: Per-page image management \u2014 add, replace, or delete a page's images in place. Replace overwrites the original filename so existing HTML references keep working.<\/li>\n<li>New: Editing respects <code>DISALLOW_FILE_EDIT<\/code> \/ <code>DISALLOW_FILE_MODS<\/code>, mirroring WordPress's core file-editor lockdown.<\/li>\n<li>Improved: Large or minified files automatically fall back to a plain text editor so the browser stays responsive; the editor is a fixed-height box that scrolls internally.<\/li>\n<li>Security: Editing and image actions reuse the existing nonce, <code>manage_options<\/code>, MIME-whitelist, and realpath path-traversal protections; backups are stored outside the publicly served directory.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: A one-time, dismissible request for a WordPress.org review, shown only after you have published a few pages.<\/li>\n<li>New: Branded admin footer with author attribution, support, and donate links.<\/li>\n<li>New: Contextual Help tab on plugin admin pages (Overview, FAQ, Support).<\/li>\n<li>New: Settings and Donate links added to the WordPress plugins list (action links and row meta).<\/li>\n<li>Improved: Refreshed hero design with a custom plugin icon.<\/li>\n<li>Improved: Cleaner hero buttons with corner radius matching the rest of the admin UI.<\/li>\n<li>Improved: Settings page icon updated to a clearer sliders icon.<\/li>\n<li>Improved: Help \/ Screen Options bar repositioned below the hero for a cleaner layout.<\/li>\n<li>Improved: Admin footer now flows naturally below content and stays visible on small screens.<\/li>\n<li>Improved: Microcopy cleanup across admin strings.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Upload HTML + image assets via admin UI.<\/li>\n<li>Automatic stripping of AI-export runtime wrappers.<\/li>\n<li>Configurable URL prefix (default <code>\/pages\/<\/code>).<\/li>\n<li>Optional subdomain routing.<\/li>\n<\/ul>","raw_excerpt":"Publish standalone HTML files \u2014 Claude Design exports or any static HTML page \u2014 as landing pages at a clean URL on your own WordPress site.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/305672","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=305672"}],"author":[{"embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/hosseinkarami"}],"wp:attachment":[{"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=305672"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=305672"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=305672"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=305672"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=305672"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/zgh.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=305672"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}