{"id":244938,"date":"2025-08-08T14:28:58","date_gmt":"2025-08-08T14:28:58","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/init-content-protector\/"},"modified":"2026-07-19T18:48:52","modified_gmt":"2026-07-19T18:48:52","slug":"init-content-protector","status":"publish","type":"plugin","link":"https:\/\/kn.wordpress.org\/plugins\/init-content-protector\/","author":14479633,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.4","stable_tag":"1.4","tested":"7.0.2","requires":"5.7","requires_php":"7.4","requires_plugins":null,"header_name":"Init Content Protector","header_author":"Init HTML","header_description":"A lightweight plugin to protect your post content from copy, scraping, and inspection. Features include copy protection, keyword cloaking, noise injection, and full content encryption.","assets_banners_color":"653b5c","last_updated":"2026-07-19 18:48:52","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/inithtml.com\/plugin\/init-content-protector\/","header_author_uri":"https:\/\/inithtml.com\/","rating":5,"author_block_rating":0,"active_installs":20,"downloads":781,"num_ratings":1,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0":{"tag":"1.0","author":"brokensmile.2103","date":"2025-08-08 14:41:33"},"1.1":{"tag":"1.1","author":"brokensmile.2103","date":"2025-08-16 07:11:49"},"1.2":{"tag":"1.2","author":"brokensmile.2103","date":"2025-11-14 13:54:16"},"1.3":{"tag":"1.3","author":"brokensmile.2103","date":"2025-11-14 14:17:34"},"1.3.1":{"tag":"1.3.1","author":"brokensmile.2103","date":"2026-05-17 16:09:18"},"1.4":{"tag":"1.4","author":"brokensmile.2103","date":"2026-07-19 18:48:52"}},"upgrade_notice":[],"ratings":{"1":0,"2":0,"3":0,"4":0,"5":1},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3341703,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3341703,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3341703,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3341703,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0","1.1","1.2","1.3","1.3.1","1.4"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3613829,"resolution":"1","location":"assets","locale":"","width":1760,"height":1458}},"screenshots":{"1":"<strong>Settings Page<\/strong> \u2013 Configure protection methods, encryption, keyword cloaking, and per-post type options."}},"plugin_section":[],"plugin_tags":[181673,55129,18193,19832,12167],"plugin_category":[],"plugin_contributors":[242666],"plugin_business_model":[],"class_list":["post-244938","plugin","type-plugin","status-publish","hentry","plugin_tags-anti-copy","plugin_tags-anti-scraping","plugin_tags-content-protection","plugin_tags-copy-protection","plugin_tags-encryption","plugin_contributors-brokensmile2103-1","plugin_committers-brokensmile2103-1"],"banners":{"banner":"https:\/\/ps.w.org\/init-content-protector\/assets\/banner-772x250.png?rev=3341703","banner_2x":"https:\/\/ps.w.org\/init-content-protector\/assets\/banner-1544x500.png?rev=3341703","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/init-content-protector\/assets\/icon-128x128.png?rev=3341703","icon_2x":"https:\/\/ps.w.org\/init-content-protector\/assets\/icon-256x256.png?rev=3341703","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/init-content-protector\/assets\/screenshot-1.png?rev=3613829","caption":"<strong>Settings Page<\/strong> \u2013 Configure protection methods, encryption, keyword cloaking, and per-post type options."}],"raw_content":"<!--section=description-->\n<p><strong>Init Content Protector<\/strong> is a powerful yet lightweight plugin that safeguards your post content from unauthorized copying, scraping tools, and inspection via browser developer tools.<\/p>\n\n<p>This plugin is part of the <a href=\"https:\/\/en.inithtml.com\/init-plugin-suite-minimalist-powerful-and-free-wordpress-plugins\/\">Init Plugin Suite<\/a> \u2014 a collection of minimalist, fast, and developer-focused tools for WordPress.<\/p>\n\n<p>GitHub repository: <a href=\"https:\/\/github.com\/brokensmile2103\/init-content-protector\">https:\/\/github.com\/brokensmile2103\/init-content-protector<\/a><\/p>\n\n<p><strong>Features:<\/strong>\n- JavaScript-based copy protection (blocks selection, right-click, print, DevTools access)\n- Full AES-256 content encryption with client-side decryption via CryptoJS\n  - Inline delivery (default, simple) or Enhanced delivery (fetches the key via a REST API endpoint after page load \u2014 keeps it out of cached\/static HTML, cache-plugin friendly)\n  - Encrypted output is cached per post and auto-invalidated on edit, avoiding redundant crypto work on every page view\n  - Fails open gracefully on hosts missing OpenSSL\/PBKDF2 support, instead of breaking the page\n- Keyword cloaking using CSS pseudo-elements\n- Invisible noise injection to confuse crawlers \u2014 only ever injected into plain text, never inside HTML tags or inside <code>&lt;script&gt;<\/code>, <code>&lt;style&gt;<\/code>, <code>&lt;pre&gt;<\/code>, <code>&lt;select&gt;<\/code>, <code>&lt;svg&gt;<\/code>, and similar elements; injection rate is configurable (1\u201350%)\n- Automatic AMP detection \u2014 protection is skipped on AMP endpoints instead of producing invalid markup\n- Per-post type configuration\n- Excluded user roles (protection never applies to selected roles)\n- Custom encryption key per site\n- Custom content selector support, with an Auto-detect button in settings<\/p>\n\n<p>Use this plugin to harden your site's content visibility while maintaining a smooth reading experience for real users. Its goal is to deter casual bots and crawlers \u2014 not to stop a determined human, since anyone who can see content can always screenshot or retype it.<\/p>\n\n<h3>Source Code<\/h3>\n\n<p>This plugin uses <a href=\"https:\/\/github.com\/brix\/crypto-js\">CryptoJS<\/a> for encryption.<br \/>\n- Minified version: <code>assets\/js\/crypto-js.min.js<\/code><br \/>\n- Source version: <a href=\"https:\/\/github.com\/brix\/crypto-js\">GitHub Repo<\/a><\/p>\n\n<h3>License<\/h3>\n\n<p>This plugin is licensed under the GPLv2 or later.<br \/>\nYou are free to use, modify, and distribute it under the same license.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin files to the <code>\/wp-content\/plugins\/init-content-protector<\/code> directory, or install via the WordPress plugin screen.<\/li>\n<li>Activate the plugin through the 'Plugins' menu in WordPress.<\/li>\n<li>Go to <strong>Settings \u2192 Init Content Protector<\/strong> and configure your preferred options.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"will%20this%20affect%20seo%3F\"><h3>Will this affect SEO?<\/h3><\/dt>\n<dd><p>If you enable full content encryption, search engines will not be able to see the content. Only use this option if SEO visibility is not required.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20support%20custom%20post%20types%3F\"><h3>Does this plugin support custom post types?<\/h3><\/dt>\n<dd><p>Yes. You can choose which post types are protected in the settings page.<\/p><\/dd>\n<dt id=\"can%20i%20use%20my%20own%20encryption%20key%3F\"><h3>Can I use my own encryption key?<\/h3><\/dt>\n<dd><p>Yes. You can set a custom key per site for added security.<\/p><\/dd>\n<dt id=\"what%27s%20the%20difference%20between%20%22inline%22%20and%20%22enhanced%22%20key%20delivery%3F\"><h3>What's the difference between \"Inline\" and \"Enhanced\" key delivery?<\/h3><\/dt>\n<dd><p>Inline embeds the decryption key directly in page HTML \u2014 simple and works everywhere, but readable via view-source. Enhanced fetches the key from a REST API endpoint after page load instead, keeping it out of cached\/static HTML and working cleanly with full-page cache plugins. Neither mode makes content truly secret from a visitor running the page's own JavaScript \u2014 both are meant to raise the bar for casual scrapers, not stop a determined human.<\/p><\/dd>\n<dt id=\"is%20the%20%22enhanced%22%20rest%20api%20endpoint%20rate-limited%3F\"><h3>Is the \"Enhanced\" REST API endpoint rate-limited?<\/h3><\/dt>\n<dd><p>No, intentionally. Rate-limiting by visitor IP would mean storing one database row per unique IP with no automatic cleanup \u2014 on a busy or bot-scanned site that bloats the database worse than the scraping it aims to prevent. If you need rate limiting, apply it at your server, CDN, or WAF layer.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20amp%3F\"><h3>Does this work with AMP?<\/h3><\/dt>\n<dd><p>The plugin automatically detects AMP endpoints and skips all protection there (JS injection, encryption, noise), since AMP doesn't allow the custom scripts these features rely on.<\/p><\/dd>\n<dt id=\"can%20i%20control%20how%20much%20noise%20is%20injected%3F\"><h3>Can I control how much noise is injected?<\/h3><\/dt>\n<dd><p>Yes, via the \"Noise Injection Rate\" setting (1\u201350% per word, default 7%). Noise is only ever inserted into plain text \u2014 never inside HTML tags or inside elements like <code>&lt;script&gt;<\/code>, <code>&lt;style&gt;<\/code>, <code>&lt;select&gt;<\/code>, or <code>&lt;svg&gt;<\/code> \u2014 so it can't corrupt markup.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.4 \u2013 July 20, 2026<\/h4>\n\n<ul>\n<li>Fixed a critical bug in noise injection: the previous logic split raw HTML on whitespace, so noise spans could be inserted in the middle of tag attributes (e.g. between <code>&lt;img<\/code> and <code>src=\"...\"<\/code>), corrupting markup and breaking layout. Noise is now only ever injected into plain text runs between tags, never inside a tag itself.<\/li>\n<li>Noise injection now skips the entire contents of <code>&lt;script&gt;<\/code>, <code>&lt;style&gt;<\/code>, <code>&lt;pre&gt;<\/code>, <code>&lt;textarea&gt;<\/code>, <code>&lt;code&gt;<\/code>, <code>&lt;select&gt;<\/code>, <code>&lt;option&gt;<\/code>, <code>&lt;title&gt;<\/code>, and <code>&lt;svg&gt;<\/code> elements, since injecting spans there is invalid markup (breaks dropdowns, SVG rendering) or would corrupt whitespace-sensitive\/non-visual content.<\/li>\n<li>Added <code>aria-hidden=\"true\"<\/code> to noise spans as defense-in-depth (display:none already hides them from screen readers, this guards against the CSS failing to load).<\/li>\n<li>Added a configurable \"Noise Injection Rate\" setting (1\u201350%, default 7%, matching the previous hardcoded rate) instead of a fixed value in code.<\/li>\n<li>Added an optional \"Enhanced\" decryption key delivery mode (<code>includes\/rest-api.php<\/code>): fetches the key via a REST API endpoint after page load instead of embedding it directly in page HTML. Keeps the key out of cached\/static HTML and plays nicely with full-page cache plugins. Default \"Inline\" mode is unchanged for backward compatibility. Deliberately not rate-limited via per-IP transients \u2014 that pattern creates one wp_options row per unique visitor\/bot IP with no active garbage collection, which would bloat the database far worse than the scraping it aims to prevent. Use server\/CDN\/WAF-level rate limiting if needed.<\/li>\n<li>Added transient caching for encrypted content, keyed to post ID + last-modified time + encryption key, avoiding redundant OpenSSL\/PBKDF2 work on every single page view. Noise injection intentionally remains uncached since its per-request randomness is part of what makes it effective against scrapers.<\/li>\n<li>Added graceful fallback when a host is missing OpenSSL or PBKDF2 support: previously encryption calls could fatal-error or silently show \"Encryption failed\" to every visitor; now the plugin fails open (shows real content to visitors, with an admin-only notice) instead of breaking the page.<\/li>\n<li>Fixed a logic bug where a failed encryption result was still treated as truthy due to <code>wp_json_encode(false)<\/code> producing a non-empty string.<\/li>\n<li>Reduced PBKDF2 salt size from 256 bytes to 32 bytes (standard, sufficient size) to cut unnecessary CPU cost server- and client-side.<\/li>\n<li>Added AMP endpoint detection: protection (JS injection, encryption, noise CSS) is now skipped automatically on AMP pages instead of producing invalid AMP markup.<\/li>\n<li>Added \"Auto-detect\" button next to Content Selector in settings: tests common theme\/builder content-wrapper selectors against your most recent published post and fills in the field automatically.<\/li>\n<li>Added console warnings (visible to administrators only) when the configured content selector isn't found on a page, to make misconfiguration easier to diagnose instead of failing silently.<\/li>\n<li>Fixed a translation-escaping bug where <code>&lt;code&gt;<\/code> tags in two settings descriptions were rendered as literal text instead of formatted code.<\/li>\n<li>Removed unused <code>devtools<\/code> variable in <code>content-protector.js<\/code>.<\/li>\n<li>Added\/updated <code>.pot<\/code> and Vietnamese <code>.po<\/code>\/<code>.mo<\/code> translations for all new strings introduced in this release.<\/li>\n<\/ul>\n\n<h4>1.3 \u2013 November 15, 2025<\/h4>\n\n<ul>\n<li>Fully decoupled <strong>encryption<\/strong> and <strong>JS content protection<\/strong> into separate script modules (<code>decrypt.js<\/code> and <code>content-protector.js<\/code>)<\/li>\n<li>Split script loading into <strong>two independent wp_enqueue_scripts hooks<\/strong>, eliminating unwanted cross-dependencies<\/li>\n<li>Renamed localized JS object for encryption to <strong>InitContentDecryptData<\/strong> for clearer separation of responsibilities<\/li>\n<li>Ensured JS protection (block copy, right-click, print, DevTools) works independently even when encryption is disabled<\/li>\n<li>Improved maintainability by isolating crypto loading (<code>crypto-js.min.js<\/code>) strictly to encrypt mode<\/li>\n<li>Refined initialization order to guarantee consistent behavior across all themes and page builders<\/li>\n<\/ul>\n\n<h4>1.2 \u2013 November 14, 2025<\/h4>\n\n<ul>\n<li>Added option to <strong>exclude specific user roles<\/strong> from all protection layers (encryption, JS protection, noise injection, keyword cloaking)<\/li>\n<li>Implemented role-based bypass at both <strong>filter level<\/strong> and <strong>asset enqueue level<\/strong> for consistent behavior across frontend<\/li>\n<li>Refactored protection flow so encryption, JS protection, and noise injection operate <strong>independently<\/strong>, preventing unwanted coupling<\/li>\n<li>Improved script enqueue logic to load CryptoJS <strong>only when encryption is enabled<\/strong><\/li>\n<li>Optimized hook processing to avoid unnecessary filtering for excluded roles and unsupported post types<\/li>\n<li>Ensured clean fallback behavior when mixed protection settings are enabled<\/li>\n<\/ul>\n\n<h4>1.1 \u2013 August 16, 2025<\/h4>\n\n<ul>\n<li>Changed minimum WordPress requirement to 5.7 to leverage wp_get_inline_script_tag for safer inline script output<\/li>\n<li>Replaced wp_add_inline_script with direct inline  injection for guaranteed execution across all single post pages<\/li>\n<li>Added inline script nonce\/type support via wp_get_inline_script_tag for enhanced security<\/li>\n<li>Ensured encrypted payload is always available early in content, even if certain script handles are missing<\/li>\n<li>Added CustomEvent trigger (init-content-payload-ready) to allow frontend scripts to react when encrypted content is ready<\/li>\n<li>Prevented duplicate inline script injection when content filters run multiple times<\/li>\n<\/ul>\n\n<h4>1.0 \u2013 July 23, 2025<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>JavaScript-based content protection (block copy, right-click, print, DevTools)<\/li>\n<li>Full AES-256 content encryption with CryptoJS decryption<\/li>\n<li>Invisible keyword cloaking via ::before and randomized CSS class<\/li>\n<li>Random noise injection (hidden spans) to confuse crawlers<\/li>\n<li>Supports multiple post types (customizable)<\/li>\n<li>Custom encryption key per site<\/li>\n<li>Custom content selector for JS targeting<\/li>\n<li>Fallback styling compatible with light\/dark themes<\/li>\n<li>Modular settings page with sanitize and validation<\/li>\n<\/ul>","raw_excerpt":"Protect your content from copying, scraping, and inspection using JS blocking, keyword cloaking, noise injection, and optional full encryption.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/244938","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=244938"}],"author":[{"embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/brokensmile2103-1"}],"wp:attachment":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=244938"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=244938"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=244938"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=244938"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=244938"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=244938"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}