{"id":315425,"date":"2026-05-20T12:35:28","date_gmt":"2026-05-20T12:35:28","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/one-v-llm-serve\/"},"modified":"2026-05-29T13:20:49","modified_gmt":"2026-05-29T13:20:49","slug":"one-v-llm-serve","status":"publish","type":"plugin","link":"https:\/\/os.wordpress.org\/plugins\/one-v-llm-serve\/","author":23500864,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"1.1.0","tested":"7.0","requires":"6.2","requires_php":"8.0","requires_plugins":null,"header_name":"One-V LLM Serve","header_author":"One-V","header_description":"Automatically generate and serve clean Markdown versions of all your pages for AI crawlers, LLMs, and GEO optimization. Accessible at the same URL with a .md extension.","assets_banners_color":"070576","last_updated":"2026-05-29 13:20:49","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/one-v.co.il","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/one-v-llm-serve\/","header_author_uri":"https:\/\/one-v.co.il","rating":4.4,"author_block_rating":0,"active_installs":0,"downloads":102,"num_ratings":7,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.2":{"tag":"1.0.2","author":"onevteam","date":"2026-05-20 12:35:05"},"1.1.0":{"tag":"1.1.0","author":"onevteam","date":"2026-05-29 13:20:49"}},"upgrade_notice":[],"ratings":{"1":1,"2":0,"3":0,"4":0,"5":6},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3538981,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3538981,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3538911,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3538911,"resolution":"772x250","location":"assets","locale":"","width":722,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.2","1.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3539194,"resolution":"1","location":"assets","locale":"","width":1351,"height":1247},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3539194,"resolution":"2","location":"assets","locale":"","width":1351,"height":790},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3539194,"resolution":"3","location":"assets","locale":"","width":1351,"height":787}},"screenshots":{"1":"Settings page \u2014 master toggle, llms.txt and taxonomy toggles, frontmatter field picker, ACF Defaults, and post-type selection.","2":"Rendered Markdown output with YAML frontmatter served at the <code>.md<\/code> URL.","3":"Another rendered example showing a different page as Markdown."}},"plugin_section":[262246],"plugin_tags":[2353,2591,226124,4608,186],"plugin_category":[49,55],"plugin_contributors":[263686,263687],"plugin_business_model":[],"class_list":["post-315425","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-ai","plugin_tags-geo","plugin_tags-llm","plugin_tags-markdown","plugin_tags-seo","plugin_category-maps-and-location","plugin_category-seo-and-marketing","plugin_contributors-onevteam","plugin_contributors-vslnk","plugin_committers-onevteam"],"banners":{"banner":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/banner-772x250.png?rev=3538911","banner_2x":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/banner-1544x500.png?rev=3538911","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/icon-128x128.png?rev=3538981","icon_2x":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/icon-256x256.png?rev=3538981","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/screenshot-1.png?rev=3539194","caption":"Settings page \u2014 master toggle, llms.txt and taxonomy toggles, frontmatter field picker, ACF Defaults, and post-type selection."},{"src":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/screenshot-2.png?rev=3539194","caption":"Rendered Markdown output with YAML frontmatter served at the <code>.md<\/code> URL."},{"src":"https:\/\/ps.w.org\/one-v-llm-serve\/assets\/screenshot-3.png?rev=3539194","caption":"Another rendered example showing a different page as Markdown."}],"raw_content":"<!--section=description-->\n<p><strong>One-V LLM Serve<\/strong> makes every public page on your WordPress site available as clean Markdown at the same URL with a <code>.md<\/code> extension \u2014 zero configuration required.<\/p>\n\n<pre><code>https:\/\/example.com\/about\/      \u2190 HTML page for humans\nhttps:\/\/example.com\/about.md    \u2190 clean Markdown for AI\n<\/code><\/pre>\n\n<p>AI systems \u2014 ChatGPT, Perplexity, ClaudeBot, Google AI Overviews, and most RAG pipelines \u2014 parse Markdown far more efficiently than HTML. When these systems encounter an HTML page, they must strip navigation, headers, footers, sidebars, scripts, and tracking pixels before they can read the actual content. This noise introduces errors, increases token cost, and leads to lower-quality outputs.<\/p>\n\n<p>The Markdown file contains a configurable YAML frontmatter block followed by the page title, headings in correct hierarchy, and the body text. Nothing else.<\/p>\n\n<h4>Core features<\/h4>\n\n<ul>\n<li><strong>Zero-config Markdown endpoint<\/strong> for every public post, page, and custom post type<\/li>\n<li><strong>YAML frontmatter<\/strong> with configurable fields (<code>title<\/code>, <code>date<\/code>, <code>modified<\/code>, <code>url<\/code>, <code>description<\/code>, <code>image<\/code>, <code>tags<\/code>, <code>categories<\/code>, <code>lang<\/code>, <code>type<\/code>)<\/li>\n<li><strong><code>\/llms.txt<\/code> discovery file<\/strong> at the site root following the <a href=\"https:\/\/llmstxt.org\">llmstxt.org<\/a> convention<\/li>\n<li><strong>Taxonomy archives<\/strong> as Markdown \u2014 <code>\/category\/news.md<\/code>, <code>\/tag\/foo.md<\/code>, custom taxonomies<\/li>\n<li><strong><code>?format=markdown<\/code> query parameter<\/strong> as an alternative to the <code>.md<\/code> URL on any singular page<\/li>\n<li><strong>Per-post exclude<\/strong> via a sidebar checkbox on the post editor<\/li>\n<li><strong>Works with Classic Editor and Gutenberg<\/strong> via the <code>the_content<\/code> filter<\/li>\n<li><strong>ACF integration<\/strong> \u2014 opt-in per-post: pick which text, textarea, WYSIWYG, URL, email, or link fields to append below the body<\/li>\n<li><strong>Filterable AI analytics<\/strong> \u2014 per-hit events with full denormalised dimensions (UA bucket, referrer host, language, post type, response code), sticky filter bar that drives every chart and table live, six KPI tiles, a stacked-area time chart, three composition donuts (UA bucket \/ referrer source \/ language), four Top tables, a User-Agent classifier transparency table, and a Recent Activity stream. Referrers are tracked by <strong>hostname only<\/strong> \u2014 paths and query strings are stripped before storage so no PII is retained. Forward-compatible classification: when the bot or referrer catalogue is updated in a future release, historical rows are reclassified automatically \u2014 no Reset Analytics required.<\/li>\n<li><strong>Browser-bucket sub-classification<\/strong> \u2014 anything that <em>looks<\/em> like a browser visit gets split into four kinds based on the <code>Sec-Fetch-Site<\/code>, <code>Sec-Fetch-User<\/code>, and <code>Sec-CH-UA<\/code> request headers a real browser sends: <strong>verified user<\/strong> (top-level navigation triggered by a click or address-bar Enter in a recognised browser), <strong>headed agent<\/strong> (real Chromium driven programmatically \u2014 Playwright, Puppeteer, Selenium), <strong>script agent<\/strong> (bare HTTP client imitating a browser UA \u2014 <code>requests<\/code>, <code>httpx<\/code>, LangChain, custom agents), <strong>spoofer<\/strong> (UA shape that no real browser would emit, like modern Chrome with a non-reduced UA). Visible as a stacked-bar breakdown on the User-Agents subpage so you can see at a glance how much of your \"human\" traffic is actually automation, and rendered inline as colour-coded slugs on every browser-bucket row in the Recent Activity table on the Analytics page. Detection is server-side fingerprinting of the request itself \u2014 no cookies, no JS, no IP.<\/li>\n<\/ul>\n\n<h4>Discoverability<\/h4>\n\n<ul>\n<li><strong><code>Link: rel=\"alternate\"; type=\"text\/markdown\"<\/code><\/strong> HTTP header on every HTML page<\/li>\n<li><strong><code>&lt;link rel=\"alternate\"&gt;<\/code><\/strong> tag in <code>&lt;head&gt;<\/code> for HTML-based discovery<\/li>\n<li><strong><code>Allow: \/*.md$<\/code><\/strong> directive in <code>robots.txt<\/code><\/li>\n<li><strong>CORS <code>Access-Control-Allow-Origin: *<\/code><\/strong> on <code>.md<\/code> and <code>\/llms.txt<\/code> so browser-based AI clients can fetch them<\/li>\n<\/ul>\n\n<h4>Operations<\/h4>\n\n<ul>\n<li><strong>Transient caching<\/strong> with automatic invalidation on <code>save_post<\/code>, on ACF field value saves, on any ACF field group change, and on plugin settings save<\/li>\n<li><strong>\"Clear cache\" button<\/strong> in the settings page<\/li>\n<li><strong>Admin notice<\/strong> on fallback HTTP fetch failures<\/li>\n<li><strong>\"Settings\" link<\/strong> next to the plugin row in Plugins screen<\/li>\n<li><strong>\"View .md\" row action<\/strong> in the Posts and Pages list tables<\/li>\n<\/ul>\n\n<h4>Developer hooks<\/h4>\n\n<ul>\n<li><strong><code>ovls_markdown<\/code><\/strong> filter for the final Markdown output<\/li>\n<li><strong><code>ovls_frontmatter<\/code><\/strong> filter for adding, removing, or modifying frontmatter fields<\/li>\n<li><strong><code>ovls_content_queries<\/code><\/strong> filter for the HTML extraction XPath cascade<\/li>\n<\/ul>\n\n<h4>How it works<\/h4>\n\n<p>Each request to <code>\/about.md<\/code> is captured by a WordPress rewrite rule and routed through the plugin's content generator. The generator runs the post through <code>apply_filters( 'the_content', ... )<\/code> \u2014 the same pipeline WordPress uses on the front end \u2014 so Classic Editor, Gutenberg, and shortcodes all work without separate code paths. The rendered HTML is converted to Markdown via <code>league\/html-to-markdown<\/code>, then cached in a WordPress transient.<\/p>\n\n<p>The cache is invalidated automatically on <code>save_post<\/code>, on ACF field\/group changes, and whenever plugin settings are saved. A manual <strong>Clear cache<\/strong> button is also available on the settings page.<\/p>\n\n<h4>Access methods<\/h4>\n\n<p>There are three equivalent ways to request the Markdown version of a page:<\/p>\n\n<ul>\n<li><code>.md<\/code> extension \u2014 <code>https:\/\/example.com\/about.md<\/code><\/li>\n<li><code>?format=markdown<\/code> query \u2014 <code>https:\/\/example.com\/about\/?format=markdown<\/code><\/li>\n<li><code>Link: rel=\"alternate\"<\/code> header \u2014 returned by every HTML page<\/li>\n<\/ul>\n\n<p>The <code>.md<\/code> URL is the recommended canonical form.<\/p>\n\n<h4>ACF integration<\/h4>\n\n<p>When <a href=\"https:\/\/www.advancedcustomfields.com\/\">Advanced Custom Fields<\/a> is active, ACF field rendering is opt-in at two levels:<\/p>\n\n<ol>\n<li><strong>Site defaults per post type<\/strong> \u2014 at <strong>Settings \u2192 One-V LLM Serve \u2192 ACF Defaults<\/strong>, tick fields that should be appended to every post of a given post type.<\/li>\n<li><strong>Per-post override<\/strong> \u2014 the <strong>One-V LLM Serve<\/strong> metabox on each post editor lists every supported ACF field applicable to that post. Tick fields to replace the site defaults for that one post.<\/li>\n<\/ol>\n\n<p>Supported ACF types: <code>text<\/code>, <code>textarea<\/code>, <code>wysiwyg<\/code>, <code>url<\/code>, <code>email<\/code>, <code>link<\/code>. Each selected field is rendered under a <code>## Field Label<\/code> heading. Empty fields are skipped.<\/p>\n\n<h3>Disclaimer<\/h3>\n\n<p>This plugin is provided \"as is\", without warranty of any kind, express or implied, in accordance with the GNU General Public License v2 or later. The authors and contributors are not liable for any direct, indirect, incidental, special, or consequential damages \u2014 including but not limited to data loss, lost profits, business interruption, search-ranking changes, or third-party claims \u2014 arising from the use of, or inability to use, this software, even if advised of the possibility of such damages.<\/p>\n\n<p>By installing and activating the plugin you acknowledge that:<\/p>\n\n<ul>\n<li>You are responsible for testing the plugin in a staging environment before deploying to production.<\/li>\n<li>You are responsible for the content this plugin exposes as Markdown \u2014 <code>.md<\/code> URLs and <code>\/llms.txt<\/code> serve the same content as their HTML counterparts and are intended to be crawled and consumed by AI systems and third-party LLMs.<\/li>\n<li>The plugin does not transmit data to any external service. All Markdown generation, caching, and file writes happen on your own server.<\/li>\n<\/ul>\n\n<p>Nothing in this disclaimer is intended to exclude or limit liability for matters that cannot lawfully be excluded under the consumer-protection laws of your jurisdiction. For the full legal terms see the GPLv2 license at <a href=\"https:\/\/www.gnu.org\/licenses\/gpl-2.0.html\">https:\/\/www.gnu.org\/licenses\/gpl-2.0.html<\/a>.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>one-v-llm-serve<\/code> folder to <code>\/wp-content\/plugins\/<\/code>, or install via <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong>.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> screen in WordPress.<\/li>\n<li>Visit <strong>Settings \u2192 One-V LLM Serve<\/strong> to configure post types, frontmatter fields, and ACF defaults.<\/li>\n<\/ol>\n\n<p>Rewrite rules are flushed automatically on activation. If <code>.md<\/code> URLs return 404 immediately after activation, go to <strong>Settings \u2192 Permalinks<\/strong> and click <strong>Save Changes<\/strong>.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20activating%20the%20plugin%20change%20my%20existing%20pages%3F\"><h3>Does activating the plugin change my existing pages?<\/h3><\/dt>\n<dd><p>No. The plugin only responds to <code>.md<\/code> URLs, <code>\/llms.txt<\/code>, and the <code>?format=markdown<\/code> query parameter. All existing HTML URLs are unaffected.<\/p><\/dd>\n<dt id=\"will%20the%20%60.md%60%20urls%20hurt%20my%20seo%3F\"><h3>Will the `.md` URLs hurt my SEO?<\/h3><\/dt>\n<dd><p>No. <code>.md<\/code> responses are served with <code>X-Robots-Tag: noindex, follow<\/code>, so search engines do not index the Markdown variants and the canonical HTML page remains the sole entry in Google\/Bing\/etc. The <code>Link: rel=\"alternate\"; type=\"text\/markdown\"<\/code> header on each HTML page advertises the Markdown alternate to AI consumers without exposing it to SERPs.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20password-protected%20posts%3F\"><h3>Does it work with password-protected posts?<\/h3><\/dt>\n<dd><p>No. Password-protected and private posts return 404 on the <code>.md<\/code> URL. Only published posts are served.<\/p><\/dd>\n<dt id=\"what%20markdown%20flavour%20is%20used%3F\"><h3>What Markdown flavour is used?<\/h3><\/dt>\n<dd><p>CommonMark-compatible Markdown via <code>league\/html-to-markdown<\/code>. ATX-style headings (<code>#<\/code>), inline links (<code>[text](url)<\/code>), and fenced code blocks.<\/p><\/dd>\n<dt id=\"where%20is%20the%20markdown%20cached%3F\"><h3>Where is the Markdown cached?<\/h3><\/dt>\n<dd><p>In WordPress transients (database by default, or your object cache). Entries are invalidated when the post is saved, when ACF fields or settings change, or when you click <strong>Clear cache<\/strong> \u2014 a long safety-net expiry also lets any orphaned entry clear itself on object-cache setups.<\/p><\/dd>\n<dt id=\"the%20%60.md%60%20url%20returns%20404%20after%20activation.\"><h3>The `.md` URL returns 404 after activation.<\/h3><\/dt>\n<dd><p>Go to <strong>Settings \u2192 Permalinks<\/strong> and click <strong>Save Changes<\/strong> to flush rewrite rules.<\/p><\/dd>\n<dt id=\"can%20i%20disable%20markdown%20for%20specific%20posts%3F\"><h3>Can I disable Markdown for specific posts?<\/h3><\/dt>\n<dd><p>Yes. Two ways:<\/p>\n\n<ol>\n<li>Check <strong>Exclude from Markdown<\/strong> in the One-V LLM Serve metabox on the post editor.<\/li>\n<li>Return <code>''<\/code> from an <code>ovls_markdown<\/code> filter callback.<\/li>\n<\/ol><\/dd>\n<dt id=\"does%20it%20work%20with%20page%20builders%20like%20elementor%20or%20divi%3F\"><h3>Does it work with page builders like Elementor or Divi?<\/h3><\/dt>\n<dd><p>Yes. Any builder that hooks into <code>the_content<\/code> is supported (Elementor, Divi, WPBakery, Beaver Builder). For builders that bypass <code>the_content<\/code>, the plugin falls back to fetching the rendered frontend HTML and extracting the main content area.<\/p><\/dd>\n<dt id=\"is%20it%20compatible%20with%20caching%20plugins%3F\"><h3>Is it compatible with caching plugins?<\/h3><\/dt>\n<dd><p>Yes. Markdown is stored in WordPress transients. Object caches (Redis, Memcached) work transparently \u2014 and <strong>Clear cache<\/strong> correctly invalidates them, not just the database. Full-page caching layers (WP Rocket, W3 Total Cache, LiteSpeed Cache) serve fresh Markdown on the next request after a save.<\/p><\/dd>\n<dt id=\"%2Fllms.txt%20returns%20404%20on%20wpengine%20%2F%20kinsta%20%2F%20managed%20nginx%20hosts\"><h3>\/llms.txt returns 404 on WPEngine \/ Kinsta \/ managed nginx hosts<\/h3><\/dt>\n<dd><p>Some managed WordPress hosts configure their nginx to serve static file extensions (<code>.txt<\/code>, <code>.xml<\/code>, \u2026) directly from disk without passing the request to WordPress. When the file is generated dynamically by a plugin, that produces a 404 because nothing exists on disk.<\/p>\n\n<p>Fix: enable <strong>Settings \u2192 One-V LLM Serve \u2192 Write llms.txt to disk<\/strong>. The plugin then maintains a real <code>\/llms.txt<\/code> file at the site root, regenerating it on every post save, ACF change, or settings update. The file carries a marker comment on the first line; the plugin refuses to overwrite a <code>\/llms.txt<\/code> it did not create. On plugin deletion the managed file is removed via <code>uninstall.php<\/code>.<\/p><\/dd>\n<dt id=\"does%20disk-mode%20work%20on%20wordpress%20installed%20in%20a%20subdirectory%20or%20on%20multisite%3F\"><h3>Does disk-mode work on WordPress installed in a subdirectory or on multisite?<\/h3><\/dt>\n<dd><p>Not currently. The disk-mode writer assumes WordPress is installed at the site root (<code>ABSPATH<\/code> is the public root). Subdirectory installs (<code>\/wp\/<\/code>) and multisite are not supported by disk-mode yet \u2014 for those, the dynamic rewrite-rule path is still available on hosts where nginx forwards <code>.txt<\/code> requests to PHP (most hosts other than WPEngine\/Kinsta).<\/p><\/dd>\n<dt id=\"my%20.md%20endpoint%20returns%20the%20same%20x-robots-tag%20to%20every%20bot.%20why%3F\"><h3>My .md endpoint returns the same X-Robots-Tag to every bot. Why?<\/h3><\/dt>\n<dd><p>The plugin sends a User-Agent-conditional <code>X-Robots-Tag<\/code>: AI crawlers (GPTBot, ClaudeBot, PerplexityBot, \u2026) get <code>index, follow<\/code> so they will use the Markdown variant, while traditional search engines (Bingbot, Googlebot, \u2026) get <code>noindex, follow<\/code> so the canonical HTML page remains the sole entry in SERPs. To keep shared caches from collapsing the two variants into one, the response carries <code>Vary: User-Agent<\/code> and <code>Cache-Control: private, max-age=0, must-revalidate<\/code>.<\/p>\n\n<p>These signals work on standard WordPress hosting. They can be overridden by edge layers in specific hosting \/ CDN configurations:<\/p>\n\n<ul>\n<li>Some managed WordPress hosts (Kinsta, WPEngine, Pressable, SiteGround, and others) ship default edge caching that treats static-looking file extensions like <code>.md<\/code> as long-lived static assets and rewrites the plugin's <code>Cache-Control<\/code> header to a public, long-<code>max-age<\/code> value.<\/li>\n<li>Some CDNs (most notably Cloudflare on its default cache key) ignore <code>Vary: User-Agent<\/code> entirely \u2014 they cache one variant per URL and serve it to every visitor regardless of UA.<\/li>\n<\/ul>\n\n<p>When one of these is in front of your site, the first <code>.md<\/code> request to reach the edge caches the response for everyone afterwards. The plugin is still doing the right thing at origin, but visitors only ever see the cached copy.<\/p>\n\n<p><strong>Diagnosing it.<\/strong> Open a terminal and run:<\/p>\n\n<pre><code>curl -skI -A \"Mozilla\/5.0 (compatible; bingbot\/2.0; +http:\/\/www.bing.com\/bingbot.htm)\" https:\/\/example.com\/your-page.md\n<\/code><\/pre>\n\n<p>Headers to look for:<\/p>\n\n<ul>\n<li><code>cf-cache-status: HIT<\/code> (Cloudflare), <code>x-kinsta-cache: HIT<\/code>, <code>x-cache: HIT<\/code> (generic) \u2014 the response is coming from the edge cache.<\/li>\n<li><code>age: &lt;large number&gt;<\/code> \u2014 the response has been sitting in cache for that many seconds.<\/li>\n<li><code>cache-control: public, max-age=&lt;large&gt;<\/code> instead of the plugin's <code>private, max-age=0<\/code> \u2014 your host or CDN has rewritten it.<\/li>\n<\/ul>\n\n<p>Then add a cache-busting query string and try again:<\/p>\n\n<pre><code>curl -skI -A \"Mozilla\/5.0 (compatible; bingbot\/2.0; \u2026)\" \"https:\/\/example.com\/your-page.md?cb=12345\"\n<\/code><\/pre>\n\n<p>If the headers are now correct (<code>x-robots-tag: noindex, follow<\/code> for Bingbot, <code>index, follow<\/code> for an AI UA), the plugin is fine \u2014 the edge is the source of the problem.<\/p>\n\n<p><strong>Fixing it.<\/strong> The fix has to be applied at the layer that is caching, not in WordPress. Common remedies:<\/p>\n\n<ul>\n<li>On the CDN, exclude <code>*.md<\/code> URLs from any \"Cache Everything\" rule, or add a Bypass Cache rule for them.<\/li>\n<li>On managed hosts, contact support and ask them to exempt <code>*.md<\/code> from the host's edge cache (so the plugin's <code>Cache-Control: private<\/code> is honoured).<\/li>\n<li>If neither is available, enable <strong>Allow search engines to index .md (advanced)<\/strong> at <strong>One-V LLM Serve \u2192 Settings<\/strong> \u2014 the plugin then sends <code>index, follow<\/code> to every UA. The behavior is consistent at the cost of allowing search engines to index the Markdown variants alongside the HTML pages.<\/li>\n<\/ul><\/dd>\n<dt id=\"what%20does%20the%20ai%20bot%20analytics%20feature%20collect%3F\"><h3>What does the AI bot analytics feature collect?<\/h3><\/dt>\n<dd><p>For each <code>.md<\/code> request the plugin stores: the timestamp, the User-Agent string (deduplicated via a small dictionary table \u2014 one row per unique UA), the referrer <strong>hostname only<\/strong> (path and query string are stripped), the requested post or term and its post type \/ taxonomy, the post language, and the HTTP response code (200 \/ 304 \/ 404).<\/p>\n\n<p><strong>Never stored:<\/strong> IP addresses, cookies, session identifiers, geolocation, full referrer URLs with query strings, or any user-account data. Counts visits exclusively to <code>.md<\/code> URLs \u2014 the regular HTML pages are not tracked.<\/p>\n\n<p>Detailed per-hit events are kept for the number of days you choose in Settings (default: 365). Older events can optionally be rolled up into a daily aggregate table for long-term trend charts \u2014 the aggregate retains only the bucket \/ language \/ referrer-bucket \/ response-code dimensions, no per-UA or per-post detail. Daily WP-cron <code>ovls_events_cleanup<\/code> enforces the retention.<\/p>\n\n<p>Analytics is enabled by default and can be turned off at <strong>One-V LLM Serve \u2192 Settings<\/strong>. All stored data can be wiped at <strong>One-V LLM Serve \u2192 Analytics \u2192 Reset analytics<\/strong>. Uninstalling the plugin drops the three analytics tables (<code>ovls_events<\/code>, <code>ovls_ua_dict<\/code>, <code>ovls_events_archive<\/code>) completely.<\/p><\/dd>\n<dt id=\"why%20doesn%27t%20my%20analytics%20show%20every%20ai%20bot%20that%20visits%3F\"><h3>Why doesn't my analytics show every AI bot that visits?<\/h3><\/dt>\n<dd><p>The plugin classifies crawlers by their User-Agent header. Almost all major AI companies (OpenAI, Anthropic, Perplexity, Google, Apple, Amazon, Meta, ByteDance, Cohere, Mistral, Common Crawl, \u2026) honestly self-identify because they have publicly committed to respecting <code>robots.txt<\/code>. Those are detected accurately.<\/p>\n\n<p>However, some traffic is invisible to User-Agent-based detection:<\/p>\n\n<ul>\n<li><strong>Stealth crawlers<\/strong> that spoof a regular browser User-Agent (some training-data brokers, certain less-ethical scrapers).<\/li>\n<li><strong>Agentic browsers<\/strong> like OpenAI Operator or Claude\/Claude-Browse running an actual headless Chrome \u2014 technically indistinguishable from a human visit at the header level.<\/li>\n<li><strong>AI assistants<\/strong> that ingest a page through an integrated third-party fetcher (e.g. a <code>python-requests<\/code> script) \u2014 these show up under \"Other bot\" rather than the underlying model.<\/li>\n<\/ul>\n\n<p>For these the plugin records what it can \u2014 they will appear in the \"Other bot\" bucket or the \"Browser\" bucket with a sub-classification that flags the request as a Playwright-class headed agent, a script agent, or a UA-shape spoofer rather than a real user. The plugin's bot signature list is updated each release as new identifiers are publicly documented.<\/p><\/dd>\n<dt id=\"how%20do%20you%20tell%20a%20real%20human%20from%20a%20scraper%20that%20imitates%20a%20browser%20user-agent%3F\"><h3>How do you tell a real human from a scraper that imitates a browser User-Agent?<\/h3><\/dt>\n<dd><p>By looking at request headers that real browsers emit automatically and bare HTTP clients usually don't. Specifically:<\/p>\n\n<ul>\n<li><code>Sec-Fetch-User: ?1<\/code> \u2014 present only when the navigation was triggered by user activation (link click, address-bar Enter, tap-out from an AI app to the system browser). Programmatic navigation in Playwright\/Puppeteer doesn't set it.<\/li>\n<li><code>Sec-CH-UA<\/code> brand list \u2014 a real downstream browser (Chrome, Edge, Brave, Opera, Vivaldi, Yandex, Samsung Internet, Arc, DuckDuckGo) announces its brand here. The open-source Chromium build that Playwright runs by default does not \u2014 it identifies as bare <code>\"Chromium\"<\/code>. Detectable difference.<\/li>\n<li><code>Sec-Fetch-Site<\/code> \u2014 sent by every modern browser since Safari 16.4 (March 2023). Absence indicates the request didn't come from a browser engine at all.<\/li>\n<li>User-Agent shape \u2014 Chrome \u2265 110 must report <code>Chrome\/X.0.0.0<\/code> (User-Agent Reduction); a UA claiming <code>Chrome\/133.0.6943.141<\/code> with non-zero minor digits is impossible for a real browser and flags the request as a copy-pasted scraper UA.<\/li>\n<\/ul>\n\n<p>Combined, these four signals split the \"Browser\" bucket into <strong>verified user<\/strong>, <strong>headed agent<\/strong> (real Chromium under automation), <strong>script agent<\/strong> (curl\/httpx\/requests imitating a UA), and <strong>spoofer<\/strong> (impossible UA shape). None of this requires JavaScript or cookies \u2014 it's all server-side inspection of the HTTP request the client already sent.<\/p>\n\n<p>A motivated scraper can manually set these headers to bypass detection. The classifier doesn't claim to catch every bot ever \u2014 it catches default-configuration tools, which is the vast majority.<\/p><\/dd>\n<dt id=\"is%20the%20analytics%20feature%20gdpr-compliant%3F\"><h3>Is the analytics feature GDPR-compliant?<\/h3><\/dt>\n<dd><p>The events store no personal data \u2014 no IP addresses, no user identifiers, no cookies, no full referrer URLs (the path and query string are stripped before storage, so utm parameters or any PII that might be encoded in a URL never reach the database). User-Agent strings, Fetch Metadata Request Headers (<code>Sec-Fetch-Site<\/code>, <code>Sec-Fetch-User<\/code>), and User-Agent Client Hints (<code>Sec-CH-UA<\/code>) are technical request-level metadata used to classify automated crawlers and tell browser-class clients apart from script-class clients \u2014 they carry strictly less information than the User-Agent and don't, on their own or in combination, identify a person. The plugin relies on legitimate-interest (Art. 6(1)(f)) as the lawful basis \u2014 server-side bot analytics is widely accepted as a legitimate interest, and the suggested privacy text at <strong>Tools \u2192 Privacy<\/strong> discloses precisely what is collected so it can be copied into your site policy.<\/p>\n\n<p>If your jurisdiction requires explicit user disclosure for any server-side analytics, you can turn the feature off at <strong>One-V LLM Serve \u2192 Settings<\/strong>.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<p>Major feature release: AI-traffic analytics and stronger crawler-facing HTTP semantics. Highlights:<\/p>\n\n<ul>\n<li>Added: AI traffic analytics \u2014 a new top-level \"One-V LLM Serve\" admin menu with an Analytics subpage and a WP-Admin dashboard widget. Per-hit events record the UA bucket (AI \/ search \/ other-bot \/ browser \/ unknown), referrer source, language, target post or term, and response code, with a sticky filter bar that drives every chart and table live, KPI tiles, a time chart, composition donuts, Top tables, a User-Agent classifier transparency table, and a Recent Activity stream. Referrers are stored by hostname only \u2014 no IPs, cookies, or full URLs are ever recorded.<\/li>\n<li>Added: Browser-bucket sub-classification \u2014 traffic that looks like a browser is split into verified user \/ headed agent (Playwright-class automation) \/ script agent (curl, httpx, requests, LangChain) \/ spoofer, using server-side fingerprinting of the Sec-Fetch and Sec-CH-UA request headers. No JavaScript, no cookies, no IP.<\/li>\n<li>Added: Referrer attribution with a six-bucket catalogue (search \/ chatbot \/ social \/ direct \/ internal \/ other). The \"chatbot\" bucket surfaces when an LLM cited your page in an answer and the reader clicked through.<\/li>\n<li>Added: Forward-compatible classification \u2014 when the bot or referrer catalogue is updated in a future release, historical rows are reclassified automatically; no Reset Analytics required. <code>wp ovls reclassify<\/code> runs the same pass on demand.<\/li>\n<li>Changed: User-Agent-conditional <code>X-Robots-Tag<\/code> \u2014 known AI crawlers receive <code>index, follow<\/code> so they ingest the Markdown variant, while search engines receive <code>noindex, follow<\/code> so the canonical HTML page stays the sole SERP entry. A new \"Allow search engines to index .md (advanced)\" toggle opts every User-Agent into <code>index, follow<\/code>.<\/li>\n<li>Added: Conditional GET (<code>ETag<\/code> \/ <code>If-None-Match<\/code> and <code>Last-Modified<\/code> \/ <code>If-Modified-Since<\/code>) returning 304 without a body, plus <code>X-Content-Type-Options: nosniff<\/code>, <code>Referrer-Policy: no-referrer<\/code>, <code>Vary: User-Agent, Accept-Encoding<\/code>, and <code>Cache-Control: private, max-age=0, must-revalidate<\/code>.<\/li>\n<li>Added: <code>X-OVLS-Version<\/code> HTTP header on every <code>.md<\/code> response and in the <code>\/llms.txt<\/code> marker, so operators can confirm the deployed build with a single <code>curl -I<\/code>.<\/li>\n<li>Added: WP-CLI namespace <code>wp ovls \u2026<\/code> \u2014 <code>flush<\/code>, <code>regenerate<\/code>, <code>list<\/code>, <code>warm<\/code>, and <code>reclassify<\/code>.<\/li>\n<li>Added: Multilingual slug-fallback for WPML and Polylang so language-prefixed <code>.md<\/code> URLs resolve to the correct translation instead of the default-language post.<\/li>\n<li>Fixed: Unaddressable permalinks \u2014 off-host \"Page Links To\" links, non-viewable custom post types, and missing permalinks are now skipped everywhere a <code>.md<\/code> link is built and 404 on direct request, instead of polluting <code>\/llms.txt<\/code>.<\/li>\n<li>Fixed: Clearing the cache now reliably invalidates entries on sites running a persistent object cache (Redis \/ Memcached), and cached Markdown no longer loads into memory on every request.<\/li>\n<li>Changed: The admin menu moved to its own top-level \"One-V LLM Serve\" entry (Settings + Analytics). The settings page slug is unchanged, so existing deep-links keep working.<\/li>\n<li>Added: Edge-cache guidance for hosts and CDNs (Kinsta, Cloudflare) that cache <code>.md<\/code> across User-Agents and defeat the conditional headers; Kinsta installs get an in-dashboard notice.<\/li>\n<li>Added: <code>\/llms.txt<\/code> now carries the \"Generated by One-V LLM Serve\" marker in both dynamic and disk-mode delivery, and lists posts of every language on multilingual sites.<\/li>\n<li>Hardening: PHP 8.0 runtime guard, a generator lock plus 30-second wall-clock cap against bot storms, 508 loop detection, dbDelta-failure admin notices, a <code>\/llms.txt<\/code> size cap, self-healing rewrite flush on upgrade, and safer HTML\/YAML handling.<\/li>\n<li>Added: Suggested Privacy Policy text at Tools \u2192 Privacy describing exactly what the analytics feature collects and how to opt out.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Added: rel=\"canonical\" Link HTTP header on .md responses pointing to the HTML permalink \u2014 consolidates SEO signals and avoids duplicate-content indexing. Index.md points at the homepage; taxonomy term .md points at the term archive.<\/li>\n<li>Added: Disclaimer section in readme covering warranty, liability, and data-transmission stance per GPLv2.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Fixed: disk-mode for <code>\/llms.txt<\/code> now detects multisite and \"WordPress in a subdirectory\" installs and refuses to write to <code>ABSPATH<\/code> when it does not map to the public docroot. Settings page surfaces a clear \"unsupported install layout\" state instead of silently writing the file to the wrong location. The dynamic rewrite-rule path keeps working on all install layouts.<\/li>\n<li>Fixed: uninstall script applies the same layout check before attempting to delete the managed <code>\/llms.txt<\/code>.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"Serve clean Markdown versions of every public page at the same URL with a .md extension for AI crawlers, LLMs, and GEO optimization.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/315425","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=315425"}],"author":[{"embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/onevteam"}],"wp:attachment":[{"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=315425"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=315425"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=315425"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=315425"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=315425"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/os.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=315425"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}