<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
  <title>CARL</title>
  <link>https://carlriedel.com/</link>
  <description>CARL is the Content Automation Ranking Launchpad. It is a comprehensive website management system that puts you in full control of your content, data, and online presence.</description>
  <language>en</language>
  <lastBuildDate>Fri, 05 Jun 2026 19:12:15 +0000</lastBuildDate>
  <atom:link href="https://carlriedel.com/rss.xml" rel="self" type="application/rss+xml"/>

  <item>
    <title>CMS for Affiliate Marketing | Own Your Links, Own Your Revenue | Carl Riedel</title>
    <link>https://carlriedel.com/use-cases/affiliate-sites/cms-for-affiliate-marketing.php</link>
    <guid isPermaLink="true">https://carlriedel.com/use-cases/affiliate-sites/cms-for-affiliate-marketing.php</guid>
    <pubDate>Fri, 05 Jun 2026 15:12:15 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/cms-for-affiliate-marketing-own-your-links-own-your-revenue--1780686658.webp" alt="CMS for Affiliate Marketing | Own Your Links, Own Your Revenue | Carl Riedel" style="max-width:100%;height:auto;">
<h1>CMS for Affiliate Marketing</h1>

<p>Most affiliate sites run on WordPress with a plugin stack that has grown one problem at a time: ThirstyAffiliates for link cloaking, WP Rocket because the site is too slow without it, Wordfence because the login page gets hammered overnight, Yoast because WordPress doesn't handle meta tags cleanly on its own. Each plugin solves a problem WordPress created, and each one invoices you every January.</p><p><img src="/images/cms-for-affiliate-marketing-own-your-links-own-your-revenue--1780686658.webp" alt="CMS for Affiliate Marketing" class="img-fluid" style=""><br></p>

<p>Add up the renewals, and you're looking at £300 or more per year just to make the platform functional for affiliate work, before you've written a single article or earned a first commission. Those subscriptions don't stop when the site starts earning. They compound as you add features, and your hosting costs climb with traffic.</p>

<h2>What a CMS for Affiliate Marketing Actually Needs</h2>

<p>Link cloaking and click tracking are the two non-negotiable operational requirements of any affiliate site. Without them, you can't manage your links cleanly or know which pages are actually driving revenue. On WordPress, both require paid plugin subscriptions. CARL includes both as built-in CMS features with no additional cost.</p>

<p>The <a href="/features/affiliate-link-tracker.php">Affiliate Link Tracker</a> handles link creation, cloaking, and tracking in one place. You create a link, assign it a slug, and every click gets logged: the count, the referring page, and the traffic source attribution. That data lives in your own database on your own server, not in a plugin's cloud account.</p>

<h2>Page Speed Is a Revenue Issue on Affiliate Sites</h2>

<p>A slow page costs money in a way that's different from a regular content site. On a blog, a slow load annoys a reader; on an affiliate product comparison page, it drops the visitor right when purchase intent is highest, before they reach your link. The math is direct: faster pages drive more clicks, and more clicks produce more commissions.</p>

<p>CARL generates static PHP files and writes them directly to your server. When a visitor lands, the server delivers a file that already exists on disk. The page is pre-built, so there's nothing to query or assemble on request. Pages load in under a second on standard shared hosting without a caching plugin, because the architecture doesn't need one.</p>

<h2>One Payment, All the Features</h2>

<p>CARL is a one-time purchase: you pay once and own the license permanently, including all future standard updates and unlimited domain installs. What a WordPress affiliate stack delivers through four separate paid plugins, link tracking, page speed, security, and SEO meta handling, CARL includes as standard. The math closes before year one is out, and from year two onward, the cost difference compounds in your favor.</p>

<p>The <a href="/pricing.php">pricing page</a> lays out the full comparison: what a functional WordPress affiliate stack costs annually versus the CARL one-time price.</p>

<h2>Your Data Belongs on Your Server</h2>

<p>Every click record, every redirect, and every piece of link-tracking data that CARL generates lives in your database on your hosting account. If you ever change CMS, migrate hosts, or walk away from a niche entirely, that data comes with you. There's no subscription required to keep your affiliate link history accessible, because the records exist in your database, not on someone else's platform.</p>

<p>The same principle applies to your content. CARL generates real PHP files and saves them to your server. Remove the admin panel entirely, and every page you've published keeps working. The files exist independently of the platform that created them.</p>

<p>For everything CARL does for affiliate sites, the <a href="/use-cases/affiliate-sites/index.php">Affiliate Marketing Sites overview</a> is the starting point.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/cms-for-affiliate-marketing-own-your-links-own-your-revenue--1780686658.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>What Site Health Checks For on Every Page | A Complete List of CARL's SEO Audit Criteria | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/what-site-health-checks-for.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/what-site-health-checks-for.php</guid>
    <pubDate>Fri, 05 Jun 2026 07:13:50 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/what-site-health-checks-for-on-every-page-a-complete-list-of-1780352949.webp" alt="What Site Health Checks For on Every Page | A Complete List of CARL&#039;s SEO Audit Criteria | Carl Riedel" style="max-width:100%;height:auto;">
<h1>What Site Health Checks For on Every Page</h1>

<p>Site Health runs a fixed set of checks against every published page in your CARL install. This article documents exactly what each check looks for, what a pass looks like, and what triggers a flag. If you're working through a Site Health report and want to understand what a specific issue means, this is the reference page.</p><p><img src="/images/what-site-health-checks-for-on-every-page-a-complete-list-of-1780352949.webp" alt="What Site Health Checks For on Every Page" class="img-fluid" style=""><br></p>

<h2>Page Title</h2>

<p>Site Health checks that a title tag is present and that its length falls within an acceptable range. Titles below 30 characters are flagged as too short: they're typically not descriptive enough to compete in search results. Titles above 60 characters are flagged as too long: Google truncates them in search results, cutting off whatever appears after the limit. The target is a title between 30 and 60 characters that leads with the primary keyword and clearly describes the page.</p>

<p>If you're using the <a href="https://carlriedel.com/wiki/seo/how-carl-generates-json-ld-schema-markup.php">Generate Schema workflow</a>, the title Claude uses in the schema block is derived from the page title field. A well-formed title in the editor produces a well-formed title in the schema.</p>

<h2>Meta Description</h2>

<p>Site Health flags pages where the meta description is missing entirely or exceeds 160 characters. A missing description means Google will pull whatever text it deems relevant from the page body, which may or may not accurately represent the page. A description over 160 characters gets truncated in search results, cutting your message short at exactly the wrong moment.</p>

<p>If you're using Generate Schema, Claude writes the meta description and automatically appends it to the Head Injection field. Site Health checks the Head Injection field for the presence of a description tag, so pages where Generate Schema has been run and the result saved will pass this check.</p>

<h2>H1 Tag</h2>

<p>Every published page should have exactly one H1 tag. Site Health flags pages where the H1 field in the editor is blank. A missing H1 is a basic on-page SEO issue: it's the strongest heading signal on the page, and Google uses it to understand what the page is about. Fill in the H1 field for every page, and make it descriptive rather than a copy of the title tag.</p>

<h2>Canonical Tag</h2>

<p>Site Health checks two things about the canonical tag: whether one is present, and whether the URL it declares matches the page's actual URL. A missing canonical is flagged because without it Google makes its own determination about which version of the page to treat as authoritative. A canonical mismatch is flagged because it tells Google to treat a different URL as the authoritative version, usually due to a slug or directory change that wasn't followed by an updated canonical.</p>

<p>For the full process of fixing a canonical mismatch, see <a href="https://carlriedel.com/wiki/seo/how-to-fix-a-canonical-mismatch.php">How to fix a canonical mismatch in CARL</a>.</p>

<h2>Head Injection Content</h2>

<p>Site Health flags pages where the Head Injection field is empty. An empty Head Injection field indicates the page has no schema markup, Open Graph tags, Twitter Card tags, or an AI-optimized meta description. These are pages where Generate Schema hasn't been run, or where the Head Injection field was cleared before regenerating. The fix is to open the page, click Generate Schema, save the result, and regenerate.</p>

<h2>File Sync Status</h2>

<p>Site Health checks whether the PHP file on disk matches the current state of the page record in the database. If a page has been edited and saved in the admin but not regenerated, the file on disk reflects the old version of the content. Site Health flags these out-of-sync pages so you can regenerate them and bring the published file up to date. This check catches pages that were edited but where the regenerate step was skipped before moving on to the next task.</p>

<h2>Using the Results</h2>

<p>A clean Site Health report means every published page has a valid title, a meta description, an H1, a canonical tag pointing to the right URL, a populated Head Injection field, and a file on disk that matches the current database record. That's the baseline. Run <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health</a> regularly to keep it there.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/what-site-health-checks-for-on-every-page-a-complete-list-of-1780352949.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Meta Descriptions | Write Once, Baked Into Every Page File | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-handles-meta-descriptions.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-handles-meta-descriptions.php</guid>
    <pubDate>Fri, 05 Jun 2026 07:09:23 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-meta-descriptions-write-once-baked-into-eve-1780349825.webp" alt="How CARL Handles Meta Descriptions | Write Once, Baked Into Every Page File | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Meta Descriptions</h1>

<p>The meta description is the short summary that appears under your page title in search results. It doesn't directly affect rankings, but it affects click-through rates, which in turn affect traffic.</p><p>CARL gives you two ways to handle it: the AI-powered Generate Schema button, which is the recommended approach for most pages, and the Manual SEO Override field for the rare cases where you want to write it yourself.</p><p><img src="/images/how-carl-handles-meta-descriptions-write-once-baked-into-eve-1780349825.webp" alt="How CARL Handles Meta Descriptions" class="img-fluid" style=""><br></p>

<h2>The Recommended Approach: Generate Schema</h2>

<p>Once you've written your page content, click the Generate Schema button in the page editor. CARL sends your content to Claude, which reads it and writes an optimized meta description specifically crafted for click-through performance. The result is returned as part of a complete head block and is automatically appended to the Head Injection field. You don't paste anything, you don't copy anything. CARL handles the transfer.</p>

<p>When you click Generate to publish the page, that entire head block, including the meta description, gets baked into the <code></code> section of the generated PHP file. The tag is present in the published page exactly as Claude wrote it, with no further intervention required from you.</p>

<h2>What "Generate Schema" Produces</h2>

<p>The meta description is one part of a larger block that Generate Schema produces in a single pass. Along with the description, Claude writes a keywords tag, a full Article JSON-LD schema with Thing entities for knowledge graph enhancement, Twitter Card tags, Open Graph tags, and the canonical tag, all delivered in the correct order and automatically appended to Head Injection. Running Generate Schema once handles everything that belongs in the <code></code> of your page.</p>

<p>The Manual SEO Override section in the page editor is labeled "Handled Automatically by Generate Schema" for exactly this reason. If you're using Generate Schema, you don't need to touch the manual fields. The AI output covers them.</p>

<h2>When to Use the Manual Field Instead</h2>

<p>The Manual SEO Override field exists for cases where you have a specific reason to write the meta description yourself: a page with unusual framing that Claude is unlikely to nail without significant prompting, a page where you're testing a specific description against a known search query, or a legacy page you're updating without regenerating the full schema block.</p><p>These cases are the exception. For the vast majority of pages, Generate Schema produces a better-optimized description faster than manually writing one.</p>

<h2>What Makes a Good Meta Description</h2>

<p>Whether you're reviewing Claude's output or writing one manually, the same principles apply. Keep it under 160 characters, or Google will truncate it in search results. Lead with what the page is about, be specific about what the reader will get, and write it as a sentence a person would actually read rather than a string of keywords. The meta description is part of the pitch: someone sees your title and description side by side before deciding whether to click, and a clear, specific description earns that click.</p>

<h2>After the Page Is Generated</h2>

<p>If you need to update the meta description after a page is live, open the Head Injection field, edit the description tag directly, and regenerate the page. The updated tag will be in the new file immediately. For a deeper look at everything Generate Schema produces and how it fits into CARL's SEO system, see <a href="https://carlriedel.com/wiki/seo/how-carl-generates-json-ld-schema-markup.php">How CARL generates JSON-LD schema markup</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-meta-descriptions-write-once-baked-into-eve-1780349825.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Canonical URLs | Prevent Duplicate Content Before It Becomes a Problem | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-handles-canonical-urls.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-handles-canonical-urls.php</guid>
    <pubDate>Fri, 05 Jun 2026 07:08:32 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-canonical-urls-prevent-duplicate-content-be-1780350134.webp" alt="How CARL Handles Canonical URLs | Prevent Duplicate Content Before It Becomes a Problem | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Canonical URLs</h1>

<p>A canonical URL tells search engines which version of a page is the authoritative one. Without it, Google can interpret minor URL variations as separate pages with duplicate content and split the ranking signals that should be concentrated on one URL across several. CARL handles canonicals at the page level, giving you direct control over what gets declared in every generated file.</p><p><img src="/images/how-carl-handles-canonical-urls-prevent-duplicate-content-be-1780350134.webp" alt="How CARL Handles Canonical URLs" class="img-fluid" style=""><br></p>

<h2>How CARL Sets the Canonical Tag</h2>

<p>Every page in CARL has a Canonical URL field in the page editor. Whatever you enter there gets written into a <code><link rel="canonical"></code> tag in the <code></code> section of the generated PHP file. If you use the Generate Schema button, Claude writes the canonical tag as part of the complete head block and automatically appends it to the Head Injection field, along with the meta description, schema, and Open Graph tags. Either way, the canonical is present in the published file exactly as specified.</p>

<p>The canonical tag is written once at generation time and baked into the static file. There's no dynamic canonical generation on each page request, no plugin calculating the URL at runtime, and no risk of the tag being output incorrectly due to a caching layer interfering with PHP execution.</p>

<h2>What the Canonical URL Should Be</h2>

<p>For most pages, the canonical URL is simply the full URL of the page itself: <code>https://yourdomain.com/directory/slug.php</code>. Setting a page as its own canonical tells Google that this is the preferred version of the content and that there are no other versions it should defer to.</p><p>This is the correct setting for any page with unique content that you want indexed at its own URL.</p>

<p>The cases where you'd set a different canonical are less common but worth understanding. If you publish the same article on your site and on Medium, the canonical on the Medium version should point back to your original.</p><p>If you have a page accessible at two different URLs for technical reasons, set the canonical on both to point to the URL you want Google to index. CARL's canonical field handles both scenarios: just enter the URL you want declared as authoritative.</p>

<h2>Canonicals and the Generate Schema Workflow</h2>

<p>When you run Generate Schema, Claude writes the canonical tag based on the page URL and includes it at the end of the head block, after the Open Graph tags. The URL it uses is derived from your domain settings, the page's slug, and the directory. Check the Head Injection field after generating to confirm the canonical URL matches what you expect before publishing the page.</p>

<p>If the generated canonical isn't quite right, you can edit it directly in the Head Injection field before clicking Generate. The Head Injection content is written verbatim into the published file, so what you see in that field is exactly what lands in the page's <code></code>.</p>

<h2>Canonical Mismatches and Site Health</h2>

<p>CARL's <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health checker</a> scans your published pages and flags canonical mismatches: cases where the canonical tag in the file doesn't match the page's actual URL. A mismatch typically means a page was generated with one URL, then moved to a different slug or directory without the canonical being updated. If Site Health flags a mismatch, the fix is straightforward: update the canonical in the Head Injection field and regenerate. For the full process, see <a href="https://carlriedel.com/wiki/seo/how-to-fix-a-canonical-mismatch.php">How to fix a canonical mismatch in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-canonical-urls-prevent-duplicate-content-be-1780350134.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Use the Head Snippet Field in CARL | Add Custom Code to Any Page's Head Section | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-use-the-head-snippet-field.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-use-the-head-snippet-field.php</guid>
    <pubDate>Fri, 05 Jun 2026 07:06:19 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-use-the-head-snippet-field-in-carl-add-custom-code-to-1780328278.webp" alt="How to Use the Head Snippet Field in CARL | Add Custom Code to Any Page&#039;s Head Section | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Use the Head Snippet Field in CARL</h1>

<p>The Head Snippet field lets you add custom code to the <code></code> section of an individual page without modifying the template. Whatever you put in this field gets written directly into the <code></code> of the generated PHP file, sitting alongside the standard meta tags, title, and schema markup that CARL outputs by default. It's a clean way to add page-specific head content without affecting your other pages.</p><p><img src="/images/how-to-use-the-head-snippet-field-in-carl-add-custom-code-to-1780328278.webp" alt="How to Use the Head Snippet Field in CARL" class="img-fluid" style=""><br></p>

<h2>What Goes in the Head Section</h2>

<p>The <code></code> section of an HTML document is where browsers and crawlers look for instructions about the page: how to render it, what to preload, which scripts to load early, what additional metadata is attached. Things that belong here include custom stylesheet links, preload directives, verification meta tags, third-party script tags that must load in the head, and any other markup that needs to appear before the page content renders.</p>

<p>Most of the standard head content, including the title tag, meta description, canonical URL, Open Graph tags, and JSON-LD schema, is handled automatically by CARL based on the fields you fill in the page editor. The Head Snippet field is for everything else: the one-off additions a specific page needs that don't belong in the template.</p>

<h2>How It Differs from the PHP Snippet Field</h2>

<p>The <a href="/wiki/page-management/how-to-use-the-php-snippet-field.php">PHP Snippet field</a> is for server-side logic that runs before any output is sent to the browser. The Head Snippet field is for client-side markup that sits inside the <code></code> tag in the rendered HTML. They serve different purposes and appear in different positions in the generated file. If you need a redirect or an access check, that's a PHP snippet. If you need a custom stylesheet or a verification tag, that's a head snippet.</p>

<h2>Practical Uses</h2>

<p>A common use of the Head Snippet field is to add a page-specific stylesheet. If one page uses a custom layout or a component that needs its own CSS, you link that stylesheet in the head snippet rather than adding it to the template, where it would load on every page unnecessarily. The same logic applies to the JavaScript libraries a single page needs: load them in the head snippet and keep the template clean.</p>

<p>Another frequent use is ownership verification tags. Google Search Console, Pinterest, and other platforms sometimes ask you to add a <code><meta></code> tag to a specific page to verify ownership. Drop it into the Head Snippet field, regenerate the page, and the tag will appear in the live file within seconds. No template edit required, no other pages affected.</p>

<h2>Keeping Templates Clean</h2>

<p>The Head Snippet field exists specifically to prevent template bloat. Without it, any page-specific head requirement would mean either editing the template (affecting every page using it) or creating a separate template variant just for that one page. Neither option is clean. The head snippet lets you handle the exception without disturbing the rule.</p>

<p>As with the PHP Snippet field, if the logic or code you're adding is needed on multiple pages, it belongs in the <a href="https://carlriedel.com/wiki/page-management/how-to-build-a-custom-template.php">template</a> rather than being repeated in individual snippets. Use the head snippet for genuine one-page requirements. For anything sitewide, put it where sitewide things belong.</p>

<h2>Snippets Are Baked In at Generation</h2>

<p>Whatever is in the Head Snippet field when you click Generate is what ends up in the published file. Update the snippet, regenerate, and the new code will be in the file. The previous version is gone. There's no version history for snippet content, so if you're making significant changes to a head snippet, keep a copy of the original somewhere before you overwrite it.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-use-the-head-snippet-field-in-carl-add-custom-code-to-1780328278.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Affiliate Link Tracker Works | Click Tracking and Link Management Built In | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/link-tracking/how-carls-affiliate-link-tracker-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/link-tracking/how-carls-affiliate-link-tracker-works.php</guid>
    <pubDate>Fri, 05 Jun 2026 07:01:08 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-affiliate-link-tracker-works-click-tracking-and-l-1780404324.webp" alt="How CARL&#039;s Affiliate Link Tracker Works | Click Tracking and Link Management Built In | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Affiliate Link Tracker Works</h1>

<p>CARL's Link Tracker lets you create, manage, and track every affiliate and outbound link on your site from one place in the admin panel. Every click is logged, bots are automatically filtered out, and the data is stored on your own server with no third-party tracking service involved.</p><p><img src="/images/how-carl-s-affiliate-link-tracker-works-click-tracking-and-l-1780404324.webp" alt="How CARL's Affiliate Link Tracker Works" class="img-fluid" style=""><br></p>

<h2>How Links Work</h2>

<p>Each link you create gets a unique 6-character short code. When a visitor clicks the link, they're routed through <code>go.php</code> on your site using a URL in the format <code>yoursite.com/go.php?code=XXXXXX</code>. CARL logs the click, resolves the destination, and sends the visitor on their way. The whole process takes milliseconds.</p>

<p>Links can be organized into groups, making it much cleaner to manage large numbers of affiliate links across multiple programs or campaigns. Each group has its own name and slug, and reports can be filtered by group to isolate performance by campaign or affiliate program.</p>

<h2>Redirect Types</h2>

<p>Each link is assigned one of four redirect types. A 301 sends a permanent redirect to the destination. A 302 sends a temporary redirect. The two cloaking options keep your domain in the browser's address bar throughout the visit: iframe cloaking loads the destination inside a full-screen frame, and JavaScript cloaking bounces the visitor instantly via a client-side redirect while serving a noindex page to search engines. For more details on when to use each option, see <a href="https://carlriedel.com/wiki/link-tracking/how-to-cloak-affiliate-links-in-carl.php">How to cloak affiliate links in CARL</a>.</p>

<h2>UTM Parameters</h2>

<p>Each link has optional UTM fields: source, medium, campaign, term, and content. When a visitor clicks a link with UTM parameters configured, the CARL appends them to the destination URL automatically. This means your affiliate traffic appears correctly segmented in Google Analytics, without you having to manually build UTM URLs for every link.</p>

<h2>Click Logging</h2>

<p>Every click is logged with the timestamp, referrer URL, country code, and user agent. IP addresses are hashed before storage, so raw visitor IPs are never retained. Bot detection runs on every click using a pattern list that covers search engine crawlers, SEO tools, uptime monitors, and common HTTP clients. Bot clicks are logged separately and excluded from your human-click totals, so your performance data stays clean.</p>

<h2>Managing Links</h2>

<p>From the Link Tracker dashboard in the admin panel, you can create new links, edit existing ones, toggle any link active or inactive without deleting it, and see a running human click total for each link at a glance. Inactive links return a 404 rather than redirecting, allowing you to pause a link during a campaign without removing it from your pages.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-affiliate-link-tracker-works-click-tracking-and-l-1780404324.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Configure CARL's Email Settings | Connect Kit and Start Building Your List | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-configure-carls-email-settings.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-configure-carls-email-settings.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:59:06 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-configure-carl-s-email-settings-connect-kit-and-start-1780360266.webp" alt="How to Configure CARL&#039;s Email Settings | Connect Kit and Start Building Your List | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Configure CARL's Email Settings</h1>

<p>CARL's email settings connect your install to Kit (ConvertKit), the email platform CARL integrates with natively. Once connected, every subscriber who signs up through a CARL signup form is added directly to your Kit account. The setup takes about five minutes and only needs to be done once.</p><p><img src="/images/how-to-configure-carl-s-email-settings-connect-kit-and-start-1780360266.webp" alt="How to Configure CARL's Email Settings" class="img-fluid" style=""><br></p>

<h2>Where to Find the Email Settings</h2>

<p>In the CARL admin panel, go to Settings and click the Subscribers tab. This is where all Kit integration settings live. You'll need your Kit API key and the form ID of the form you want subscribers added to. Have your Kit account open in a separate browser tab before you start.</p>

<h2>Getting Your Kit API Key</h2>

<p>Log in to your Kit account at app.kit.com. Go to Settings&gt; Developer Settings, then copy your API key. Back in CARL, paste it into the Kit API Key field. This key authorizes CARL to communicate with your Kit account and add subscribers on your behalf.</p>

<h2>Finding Your Kit Form ID</h2>

<p>In Kit, go to Grow, then Landing Pages and Forms. Click the form you want CARL to subscribe people to. Look at the URL in your browser address bar: the number in the URL is your Form ID. Copy that number and paste it into the Default Kit Form ID field in CARL's Subscribers settings.</p>

<p>The Default Kit Form ID is the form CARL uses when a visitor signs up through your site's signup form. If you have multiple forms for different lead magnets or sections of your site, the default applies across the board. Individual signup form embeds can reference specific form IDs if needed.</p>

<h2>Customizing the Signup Form Wording</h2>

<p>The Subscribers tab also lets you customize the text displayed on CARL's native signup form: the heading, description, button label, and confirmation message shown after someone subscribes. Update these to match your brand voice before the form goes live. The default text is functional but generic. Your subscribers will read it, so write it like you mean it.</p>

<h2>Saving and Testing</h2>

<p>Click Save All Settings after entering your API key and Form ID. To confirm the connection is working, go to one of your published pages that includes a signup form, enter a test email address, and submit. Check your Kit account to confirm the subscriber was added to the correct form. If the subscriber doesn't appear, go back to the Subscribers tab and verify the API key and Form ID are entered correctly with no extra spaces.</p>

<h2>If You're Not Using Kit Yet</h2>

<p>You can skip the Subscribers tab during initial setup and return to it when you're ready to connect your email platform. CARL functions fully without Kit integration. The signup form simply won't sync subscribers to an external list until the API key is in place. For more details on how CARL's subscriber system works and how the native signup form connects to your pages, see <a href="https://carlriedel.com/wiki/installation/how-to-configure-carls-email-settings.php">How CARL integrates with Kit</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-configure-carl-s-email-settings-connect-kit-and-start-1780360266.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Configure CARL's General Settings | The First Things to Set After Installation | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-configure-carl-general-settings.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-configure-carl-general-settings.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:56:45 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-configure-carl-s-general-settings-the-first-things-to-1780360025.webp" alt="How to Configure CARL&#039;s General Settings | The First Things to Set After Installation | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Configure CARL's General Settings</h1>

<p>The first thing to do after logging into CARL for the first time is to open Settings and work through each tab. The General tab contains the most critical settings on the entire install: get these right before you publish a single page. Getting them wrong after pages are already live creates SEO problems that take real effort to clean up.</p><p><img src="/images/how-to-configure-carl-s-general-settings-the-first-things-to-1780360025.webp" alt="How to Configure CARL's General Settings" class="img-fluid" style=""><br></p>

<h2>Site Name</h2>

<p>The Site Name field is your website or brand name. CARL uses it in browser tabs, schema markup, and throughout the admin interface. Enter it exactly as you want it to appear publicly. This is not a URL, just the name: "Carl Riedel", "The Takeaway Tuesday", "Acme Plumbing Services", whatever your brand is called.</p>

<h2>Base URL</h2>

<p>The Base URL is the single most critical setting in CARL. It must be your site's exact root URL with no trailing slash and with the correct HTTPS protocol. The correct format is <code>https://yoursite.com</code>. Not <code>https://yoursite.com/</code> with a trailing slash. Not <code>http://yoursite.com</code> with HTTP instead of HTTPS.</p>

<p>CARL uses the Base URL to build every canonical URL, every sitemap entry, and every schema link on your site. A wrong Base URL means every SEO signal on every page points to the wrong address. Set it correctly once, click Save All Settings, and don't change it after pages are published.</p>

<h2>Admin Recovery Key</h2>

<p>The Recovery Key is your emergency access key. If you ever forget your admin password, CARL's Forgot Password page asks for this key before allowing a reset. Set it immediately after your first login, before you need it. Write it down and store it in a password manager. If you lose both your password and your Recovery Key, recovering admin access requires direct access to the database through your hosting provider.</p>

<h2>Favicon</h2>

<p>The favicon section on the General tab lets you upload your site icon directly through the admin. Click Choose File, select a 32x32 or 64x64-pixel PNG or ICO file, then click Upload Favicon. CARL saves it and automatically injects it into every page. After uploading, use the Regen All button on the Pages list to update all existing published pages with the new favicon tag.</p>

<h2>Saving Your Settings</h2>

<p>Click Save All Settings before moving to any other tab. CARL doesn't auto-save settings across tabs, so changes on the General tab must be saved explicitly before you navigate elsewhere. Make it a habit: change something, save, then continue.</p>

<h2>The Other Settings Tabs</h2>

<p>The General tab is the starting point, but the other tabs matter too. The Organization tab powers your Organization JSON-LD schema and should be filled in completely. The System tab contains the Admin Timezone and Cron Secret Key needed for scheduled publishing. The AI Settings tab is where you enter your Claude API key, which enables the Generate Schema button in the page editor. The Subscribers tab connects CARL to your Kit account if you're building an email list.</p>

<p>For full details on the AI settings that power schema generation, see <a href="/wiki/installation/how-to-use-carls-ai-schema-generator.php">How to use CARL's AI Schema Generator</a>. For email integration, see <a href="https://carlriedel.com/wiki/email-subscribers/how-carl-integrates-with-kit.php">How CARL integrates with Kit</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-configure-carl-s-general-settings-the-first-things-to-1780360025.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Create a Product Page for a Digital Download | Build Your Sales Page Inside CARL | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-to-create-a-product-page-for-a-digital-download.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-to-create-a-product-page-for-a-digital-download.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:54:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-create-a-product-page-for-a-digital-download-build-yo-1780342737.webp" alt="How to Create a Product Page for a Digital Download | Build Your Sales Page Inside CARL | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Create a Product Page for a Digital Download</h1>

<p>Every digital product you sell through CARL needs its own page on your site. That page does two jobs: it sells the product to visitors who land there, and it displays the PayPal buy button that starts the purchase flow. Both happen in CARL's page editor, with the Downloads module's product renderer handling the technical side while you focus on the copy.</p><p><img src="/images/how-to-create-a-product-page-for-a-digital-download-build-yo-1780342737.webp" alt="How to Create a Product Page for a Digital Download" class="img-fluid" style=""><br></p>

<h2>Set Up the Product in the Downloads Module First</h2>

<p>Before you create the page, the product needs to exist in CARL's Downloads module. Go to Downloads in the admin panel, add a new product, and fill in the details: the product name, price, currency, the file you're selling, and the token settings that control how long the download link remains valid after purchase. Save the product record. CARL assigns it an ID that the product renderer uses to pull the buy button and product data onto the page.</p>

<p>Getting the product set up first means you have everything you need when you open the page editor. You won't need to switch back and forth between the Downloads module and the page editor while you're writing.</p>

<h2>Creating the Page</h2>

<p>Create a new page in CARL the same way you'd <a href="https://carlriedel.com/wiki/page-management/how-to-create-a-page.php">create any page</a>. Set the slug and directory to something clean and descriptive: the product name works well as a slug. Choose the template that fits your sales page layout. Fill in the title, H1, meta description, and Open Graph fields with copy that reflects what you're selling and who it's for.</p>

<p>The content field is where you write the sales copy: what the product is, what's in it, who it's for, what problem it solves. Write this as you would any page on your site. The buy button gets added separately through the product renderer shortcode, so you don't need to manually build any payment markup.</p>

<h2>Adding the Buy Button</h2>

<p>CARL's Downloads module includes a product renderer that automatically renders the PayPal buy button and any associated product details. In the content field, place the renderer shortcode where you want the buy button to appear, typically after the main sales copy and before any closing content. When CARL generates the page, it replaces the shortcode with the fully rendered buy button tied to your product record.</p>

<p>The button uses the PayPal credentials you configured in <a href="/wiki/downloads/how-to-set-up-paypal-in-carl.php">CARL's PayPal settings</a>. When a visitor clicks it, PayPal's native checkout opens. When the payment completes, <a href="/wiki/downloads/how-the-paypal-webhook-works-in-carl.php">the webhook fires</a>, the purchase is confirmed, and the buyer receives their download link by email. You don't need to add any payment logic to the page itself.</p>

<h2>Generating and Reviewing the Page</h2>

<p>Once the content is written and the renderer shortcode is in place, use CARL's preview to check the page before generating it. Confirm the buy button renders correctly, the layout looks right, and the sales copy reads the way you intended. When you're satisfied, click Generate. The page goes to disk at the URL you specified and is immediately accessible.</p>

<p>Visit the live URL and run a test purchase using your PayPal Sandbox credentials to confirm the full flow works: button click, PayPal checkout, payment confirmation, download email, working download link. Once that passes, the page is ready for real buyers. For a complete overview of how all the pieces fit together, see <a href="https://carlriedel.com/wiki/downloads/how-selling-digital-downloads-works.php">How selling digital downloads works in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-create-a-product-page-for-a-digital-download-build-yo-1780342737.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How Selling Digital Downloads Works in CARL | Secure Delivery, No Third-Party Storefronts | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-selling-digital-downloads-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-selling-digital-downloads-works.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:51:43 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-selling-digital-downloads-works-in-carl-secure-delivery--1780319271.webp" alt="How Selling Digital Downloads Works in CARL | Secure Delivery, No Third-Party Storefronts | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How Selling Digital Downloads Works in CARL</h1>
<p>Selling a digital product from your CARL site doesn't require a third-party storefront, a monthly subscription to a delivery platform, or a percentage cut going to a middleman. The Downloads Module handles the entire process: product catalog, PayPal checkout, secure file delivery, and purchase history, all running directly on your server.</p><p><img src="/images/how-selling-digital-downloads-works-in-carl-secure-delivery--1780319271.webp" alt="How Selling Digital Downloads Works in CARL" class="img-fluid" style=""><br></p>
<p>This page walks through how the whole system fits together, from the moment a buyer lands on your product page to the moment they download their file.</p>
<h2>The Product Page</h2>
<p>Each product you sell gets its own page on your CARL site, built using the <code>download-product</code> template. The page pulls the product details from your database in real time: the name, price, currency, description, file type, file size, download limit, and the duration the download link remains valid after purchase.</p>
<p>The product renderer also automatically renders the PayPal buy button using your PayPal Client ID from Settings. The buyer sees your product, your price, and a clean checkout button. Everything happens on your domain. There's no redirect to a storefront you don't control.</p>
<h2>The Payment</h2>
<p>When the buyer clicks the PayPal button, PayPal's native checkout opens. The buyer pays using their PayPal balance, a linked card, or a bank account. CARL doesn't handle payment card data at any point. PayPal processes the transaction entirely on its end.</p>
<p>The price and currency displayed on the page are exactly what gets charged. The product's currency setting controls this, with a fallback to the global PayPal currency you set in Settings. There are no surprises at checkout.</p>
<h2>The Webhook</h2>
<p>When PayPal confirms the payment, it fires a <code>PAYMENT.CAPTURE.COMPLETED</code> event to a webhook listener running at <code>/admin/modules/downloads/webhook.php</code> on your server. This is the trigger that sets everything in motion on CARL's side.</p>
<p>The webhook handler verifies the event, extracts the product ID embedded in the PayPal order, and runs an idempotency check to ensure the same order isn't processed twice. If everything checks out, CARL moves immediately to delivery.</p>
<h2>The Download Token</h2>
<p>CARL generates a secure download token: a 64-character random hexadecimal string produced by PHP's <code>random_bytes()</code> function. This token is tied to the specific product, stamped with an expiry time, and given a download counter starting at zero.</p>
<p>The token is stored in the <code>download_tokens</code> table alongside the buyer's details and the PayPal order ID. It cannot be guessed. It cannot be reused beyond the download limit you set. Once it expires, it stops working entirely.</p>
<h2>The Confirmation Email</h2>
<p>As soon as the token is created, CARL sends the buyer a confirmation email containing their download link. The link follows this format:</p>
<p><code>https://yourdomain.com/admin/modules/downloads/deliver.php?token=XXXX</code></p>
<p>The email also tells the buyer how many times the link can be used and when it expires. Delivery is immediate. By the time the buyer closes the PayPal confirmation screen, the email is already on its way.</p>
<h2>The File Delivery</h2>
<p>When the buyer clicks their download link, CARL's delivery script validates the token, checks the expiry timestamp, and checks the download count against the limit. If everything is valid, CARL streams the file directly to the browser.</p>
<p>The file itself is stored above <code>public_html</code>, in a folder called <code>carl-downloads</code> that sits entirely outside your web root. It cannot be accessed by a direct URL. The real file path is never exposed to the buyer. The only thing they see is the token URL, and the only way to access it is with a valid, unexpired token.</p>
<p>Each successful download increments the counter. When the counter reaches the limit, the link stops working.</p>
<h2>The Purchase Record</h2>
<p>Every completed sale is logged in the <code>download_purchases</code> table and visible in the Purchase History tab inside your Downloads admin. You can see the buyer's name and email, the product they bought, the amount paid, the PayPal order ID, how many times they've used their link, and whether the link is still active.</p>
<p>The stats summary at the top of the Downloads admin keeps a running total of sales, revenue, downloads delivered, and active products. It's a clear, honest record of what's moving and what's been delivered.</p>
<h2>What You Need to Get Started</h2>
<p>Three things: a PayPal Business account with your Client ID and Secret entered in Settings, at least one product created in the Downloads admin with a file uploaded, and a published product page using the <code>download-product</code> template with a slug that matches your product. That's the complete setup.</p>
<p>For the full step-by-step setup guide, see <a href="/wiki/downloads/how-to-set-up-paypal-in-carl.php">How to set up PayPal in CARL</a> and <a href="https://carlriedel.com/wiki/downloads/how-to-create-a-product-page-for-a-digital-download.php">How to create a product page for a digital download</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-selling-digital-downloads-works-in-carl-secure-delivery--1780319271.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Delivers Files Securely | No Public URLs, No Direct Server Access | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-carl-delivers-files-securely.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-carl-delivers-files-securely.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:49:22 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-delivers-files-securely-no-public-urls-no-direct-se-1780340376.webp" alt="How CARL Delivers Files Securely | No Public URLs, No Direct Server Access | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Delivers Files Securely</h1>

<p>When someone buys a digital product through CARL, they don't get a link to a file sitting in a public folder on your server. They get a time-limited, single-use download token that points to a delivery script. The file itself is stored outside your public document root and is completely inaccessible to anyone who hasn't completed a verified purchase. That's the foundation of how CARL handles secure file delivery.</p><p><img src="/images/how-carl-delivers-files-securely-no-public-urls-no-direct-se-1780340376.webp" alt="How CARL Delivers Files Securely" class="img-fluid" style=""><br></p>

<h2>Why Public URLs Are a Problem</h2>

<p>The simplest way to deliver a digital file is to upload it to your server and share the URL. It's also the worst way. A URL that points directly to a file can be shared, indexed by search engines, discovered by scanners, or stumbled on by anyone who guesses the path. Once the URL is out, the file is out. You have no way to revoke access, no way to know who downloaded it, and no way to tie a download to a specific purchase.</p>

<p>CARL's delivery system eliminates that problem entirely by keeping the file off the public web and routing every download through a controlled access layer.</p>

<h2>Where the Files Are Stored</h2>

<p>Files you add to CARL's Downloads module are stored in a protected directory outside <code>public_html/</code>. A visitor browsing your site has no path to that directory. There's no URL that resolves to it, no way to request files from it directly, and no index that lists its contents. The files exist on your server but are invisible to the public web.</p>

<p>This is a straightforward application of a principle that applies to any sensitive server-side resource: if the public doesn't need direct access to it, it shouldn't be in the public directory. CARL enforces this automatically when you add a product. You upload the file through the admin panel, and CARL handles where it goes.</p>

<h2>How Delivery Actually Works</h2>

<p>When a purchase is confirmed, CARL generates a <a href="/wiki/downloads/how-carl-download-tokens-work.php">unique download token</a> and attaches it to the buyer's purchase record. The buyer receives a download link containing that token. When they click it, the link hits CARL's delivery script, which checks the token against the database: is it valid, has it been used, has it expired? If the check passes, the script reads the file from its protected location and streams it to the buyer's browser. The file never moves to a public URL at any point in that process.</p>

<p>If someone tries to use an expired token, a used token, or a token that doesn't exist, the delivery script returns an error. The file doesn't move. There's nothing to intercept, share, or reuse.</p>

<h2>What This Means for Your Products</h2>

<p>Secure delivery protects the value of what you're selling. A PDF guide, a software download, a template pack: these have value because access to them is controlled. CARL's delivery system keeps that control in place after the sale, not just before it. Buyers get what they paid for through a process that works cleanly and reliably. Everyone else gets nothing.</p>

<p>For a full picture of how the purchase-to-download flow fits together, see <a href="https://carlriedel.com/wiki/downloads/how-selling-digital-downloads-works.php">How selling digital downloads works in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-delivers-files-securely-no-public-urls-no-direct-se-1780340376.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Remove the CARL Admin Panel and Keep Your Pages | Remove the Admin, Keep the Site | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/how-to-remove-the-carl-admin-panel-and-keep-your-pages.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/how-to-remove-the-carl-admin-panel-and-keep-your-pages.php</guid>
    <pubDate>Fri, 05 Jun 2026 06:46:22 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-remove-the-carl-admin-panel-and-keep-your-pages-remov-1780323427.webp" alt="How to Remove the CARL Admin Panel and Keep Your Pages | Remove the Admin, Keep the Site | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Remove the CARL Admin Panel and Keep Your Pages</h1>

<p>One of the more unusual things you can do with CARL is delete the admin panel entirely, leaving a fully functional website behind. Every page you've published keeps loading. Google keeps crawling. Visitors keep reading. The site has no idea the admin is gone.</p><p><img src="/images/how-to-remove-the-carl-admin-panel-and-keep-your-pages-remov-1780323427.webp" alt="How to Remove the CARL Admin Panel and Keep Your Pages" class="img-fluid" style=""><br></p>

<h2>Why This Is Possible</h2>

<p>When you <a href="https://carlriedel.com/wiki/architecture/what-happens-when-you-publish-a-page.php">publish a page in CARL</a>, a finished PHP file is written to disk in your document root. That file contains everything: your content, meta tags, navigation, schema, and footer. It has no dependency on the admin panel, the database, or any part of CARL's application code. It's a standalone file.</p>

<p>The admin panel is a separate directory on your server. It handles content management, settings, and page generation. Once a page is published, the admin's job for that page is done. Removing the admin directory doesn't touch the published files at all.</p>

<h2>What You're Actually Removing</h2>

<p>The CARL admin panel lives in its own folder, typically named <code>admin/</code> inside your document root. That folder contains the admin application, the database connection config, and all the management tools. Your published pages live outside that folder, directly in your document root or in subdirectories based on their URL structure.</p>

<p>Deleting the <code>admin/</code> folder removes your ability to create or edit pages. It removes access to settings, subscribers, and every other management function. Your published pages are unaffected because they never referenced the admin folder in the first place.</p>

<h2>When You'd Actually Do This</h2>

<p>The most common reason is a client handoff. You build the site, publish every page, hand the files to the client, and remove the admin so there's no login page for bots to probe and no CMS overhead on the server. The client gets a fast, clean website with no platform attached.</p>

<p>It's also useful if you've finished a project and want to archive it. Strip the admin, zip the files, and you have a complete working website in a folder. Upload it to any cPanel host, point a domain at it, and it runs. No database is required because the published pages don't use one.</p>

<h2>The Database Stays Behind</h2>

<p>If you remove the admin and leave the database in place, nothing breaks. The published pages don't query it. You can also drop the database entirely if you want a completely clean server with no leftover tables. Either way, the live site is unaffected.</p>

<p>This is the logical conclusion of the <a href="https://carlriedel.com/wiki/architecture/what-carls-database-actually-contains.php">separation between admin data and published pages</a>. The database was always an admin tool. Remove the admin, and the database has no role left to play.</p>

<h2>Getting Back In</h2>

<p>If you removed the admin and need it back, you can upload it again. Re-upload the admin folder, restore the database from a backup, and you're back to full management. Your published pages were sitting on the server the whole time, unchanged.</p>

<p>That's worth sitting with for a moment. In WordPress, the CMS and the site are the same thing. Remove WordPress, remove the site. In CARL, the CMS and the site are separate from the moment you click Generate. The published files are yours, on your server, with no platform holding them together.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-remove-the-carl-admin-panel-and-keep-your-pages-remov-1780323427.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Build a CARL Theme | Package and Distribute a Complete Site Skin | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/themes/how-to-build-a-carl-theme.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/themes/how-to-build-a-carl-theme.php</guid>
    <pubDate>Thu, 04 Jun 2026 20:38:38 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-build-a-carl-theme-package-and-distribute-a-complete--1780619848.webp" alt="How to Build a CARL Theme | Package and Distribute a Complete Site Skin | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Build a CARL Theme</h1>

<p>A CARL theme is a zip archive renamed with a <code>.carltheme</code> extension. It contains three-page template files, a set of site include files, and a <code>theme.json</code> manifest. Build those components, run the package script, and you have a distributable theme file anyone can install on their CARL site in one step.</p><p><img src="/images/how-to-build-a-carl-theme-package-and-distribute-a-complete--1780619848.webp" alt="How to Build a CARL Theme" class="img-fluid" style=""><br></p>

<h2>The Package Structure</h2>

<p>A valid <code>.carltheme</code> package must follow this directory structure inside the zip:</p>

<pre><code>mytheme/
  theme.json
  tpl/
    blog.tpl
    full-width.tpl
    category.tpl
  includes/
    css_include.txt
    nav_include.txt
    footer_include.txt
    footer_scripts.txt
    site_header.txt
    header_scripts.txt
    hub_render.txt
    recent_posts.txt
    sidebar_include.txt
    special_cta_include.txt
  assets/              (optional — images, fonts)</code></pre>

<p>The top-level folder name doesn't matter. CARL locates <code>theme.json</code> automatically wherever it appears inside the zip. The <code>tpl/</code> folder deploys to <code>admin/tpl/</code> on the server. The <code>includes/</code> folder deploys to <code>site_includes/</code>. The optional <code>assets/</code> folder deploys to a web-accessible path at <code>/assets/themes/{slug}/</code>.</p>

<h2>The theme.json Manifest</h2>

<p>Every theme package requires a <code>theme.json</code> file that tells the installer what to do with the package contents:</p>

<pre><code>{
  "name":          "My Theme",
  "slug":          "my-theme",
  "version":       "1.0",
  "description":   "A short description shown on the install confirmation screen.",
  "author":        "Your Name",
  "requires_carl": "1.0",
  "templates": {
    "blog":        "tpl/blog.tpl",
    "full-width":  "tpl/full-width.tpl",
    "category":    "tpl/category.tpl"
  },
  "includes": [
    "css_include.txt",
    "nav_include.txt",
    "footer_include.txt",
    "footer_scripts.txt",
    "site_header.txt",
    "header_scripts.txt",
    "hub_render.txt",
    "recent_posts.txt",
    "sidebar_include.txt",
    "special_cta_include.txt"
  ]
}</code></pre>

<p>The slug field must be lowercase letters, numbers, and hyphens only. It's used to construct the template slugs stored in the database (for example, <code>my-theme-blog</code>) and the assets path on the server. The three template role names — blog, full-width, and category — are fixed. CARL uses them to remap existing pages to the new theme's layouts during install.</p>

<h2>Building the Template Files</h2>

<p>Each template file is a complete HTML document with CARL tokens in place of dynamic content. The four tokens are <code>{{TITLE}}</code> for the page title, <code>{{META}}</code> for the meta tag block, <code>{{HEAD_EXTRA}}</code> for any additional head content, and <code>{{BODY}}</code> for the full page content. Never hardcode an H1 heading directly in a template file. CARL generates the H1 as part of <code>{{BODY}}</code> and writes it into the file at generation time.</p>

<p>Every template must include <code>lang="en"</code> on the html element for accessibility compliance. Your viewport meta tag and any preload directives belong in <code>header_scripts.txt</code> rather than hardcoded in the template, so they can be updated without rebuilding the template files. The feedback widget, if you want it on category pages, is included via <code>admin/modules/feedback/display.php</code>.</p>

<h2>The Include Files</h2>

<p>All ten include files are required in the package. <code>css_include.txt</code> contains your entire stylesheet as an inline style block — putting CSS here rather than as an external file eliminates asset path problems when deploying the theme and removes any render-blocking external CSS request. <code>nav_include.txt</code> contains your site navigation HTML. <code>footer_include.txt</code> contains your footer. <code>hub_render.txt</code> contains the PHP that queries and outputs the article card grid on category pages. <code>recent_posts.txt</code> contains the PHP that powers the sidebar related posts widget. The remaining files handle scripts, the site header, the sidebar wrapper, and the mid-article CTA slot.</p>

<h2>CSS and PageSpeed</h2>

<p>Two CSS rules are essential for a zero Cumulative Layout Shift score. Content images need <code>aspect-ratio</code> and <code>object-fit: cover</code> declared so the browser reserves their space before they download. The site header banner image needs the same treatment. Without these, images cause layout jumps as they load and PageSpeed will penalize the score. Do not add <code>transition: margin-top</code> to the body rule — it causes measurable CLS on desktop.</p>

<p>For the H1 selector, use a descendant selector (<code>.article-main h1</code>) rather than a direct child selector. The H1 sits inside a content column div inside the article element, so a direct child selector won't reach it.</p>

<h2>Packaging the Theme</h2>

<p>Once all files are ready, create a folder named after your theme slug, place <code>theme.json</code> at the root, and add your <code>tpl/</code> and <code>includes/</code> subfolders with the correct files in each. Zip the folder, then rename the resulting zip file with the <code>.carltheme</code> extension. On Windows, a PowerShell script that copies and renames files into the correct structure and zips them in one command makes rebuilding fast and consistent whenever you update any source file.</p>

<h2>Testing Before Distribution</h2>

<p>Test every theme on a local or staging CARL install before distributing it. Verify the blog, full-width, and category templates all render correctly. Check the hamburger menu opens and closes on mobile. Run PageSpeed Insights on all three templates and confirm CLS is 0. Verify <code>theme.json</code> parses correctly, all ten include files are present, all three template files are in <code>tpl/</code>, and the install completes without errors on a clean test site with pages remapping correctly after install.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-build-a-carl-theme-package-and-distribute-a-complete--1780619848.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>The CARL Magazine Theme | Newspaper-Style Layout With No External Dependencies | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/themes/the-magazine-theme.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/themes/the-magazine-theme.php</guid>
    <pubDate>Thu, 04 Jun 2026 20:27:46 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/magazine-theme.webp" alt="The CARL Magazine Theme | Newspaper-Style Layout With No External Dependencies | Carl Riedel" style="max-width:100%;height:auto;">
<h1>The CARL Magazine Theme</h1>

<p>The Magazine Theme gives your CARL site a newspaper-style appearance: bold article headlines with a coloured accent bar, a structured sidebar with related posts, and a full-width banner header across the top of every page. It's designed for content publishers, affiliate sites, and anyone running a high-volume article site where speed and readability come first.</p><p><img src="/images/magazine-theme.webp" alt="The CARL Magazine Theme" class="img-fluid" style=""><br></p>

<h2>No External Dependencies</h2>

<p>Unlike CARL's default Bootstrap templates, the Magazine Theme uses no external CSS framework. Every style is written natively and delivered as a single inline stylesheet. There are no CDN requests, no render-blocking external CSS files, and no dependency on any third-party service. Everything the theme needs is contained in the theme package itself.</p>

<h2>Page Layouts</h2>

<p>The Magazine Theme includes three layouts covering every standard page type in CARL. The blog layout is the standard article view: a large headline, a featured image, the article body, and a related posts sidebar on the right. A mid-article call-to-action slot is included for internal linking or promotions.</p><p>The full-width layout removes the sidebar and stretches the content to the full column width, making it well suited for landing pages and long-form content. The category layout displays all published pages in a directory as a two-column card grid, with each card showing the title, description, thumbnail, and a continue reading link, populated automatically with no manual configuration required.</p>

<h2>Navigation</h2>

<p>The desktop navigation is a full horizontal bar with support for drop-down menus. On smaller screens, it collapses to a hamburger icon. Tapping it slides in a full-screen navigation panel from the left, with all links clearly listed. The mobile panel is built by cloning the desktop nav in JavaScript, so there's only one nav to maintain, and the mobile view always stays in sync with the desktop version.</p>

<h2>Performance</h2>

<p>The Magazine Theme is built to score well on Google PageSpeed. All images reserve their space before loading, which eliminates layout shift during page load. The site header banner is preloaded so it's in the browser cache before the page layout requests it. There are no render-blocking external resources. On standard shared cPanel hosting, the blog layout scores 92 for performance and 100 for best practices on mobile. The full-width layout scores 99 for performance. Cumulative Layout Shift is 0 on both.</p>

<h2>Setting Up Your Header Image</h2>

<p>The Magazine Theme displays a full-width banner image at the top of every page. Upload your header image to your site's <code>/images/</code> folder via cPanel File Manager or FTP. The image displays at a 3:1 aspect ratio, so a width of 1200px or wider is recommended. The WebP format delivers the best performance, though JPEG and PNG are also supported. Once uploaded, open <code>site_header.txt</code> in Include Files in your CARL admin and update the image filename to match the file you uploaded. Save and regenerate to apply the change.</p>

<h2>Customizing the Navigation</h2>

<p>The Magazine Theme navigation is controlled by your <code>nav_include.txt</code> include file, the same as all other CARL templates. Edit it in the Include Files in your admin panel. Because the nav is a PHP include rather than baked into each generated page, changes take effect immediately across your entire site without needing to regenerate. For a full walkthrough of working with include files, see <a href="/wiki/includes/how-to-manage-include-files.php">How to manage include files in CARL</a>.</p>

<h2>Installing the Magazine Theme</h2>

<p>Install the Magazine Theme the same way as any CARL theme. Go to Themes in the admin sidebar, upload <code>magazine.carltheme</code>, review the confirmation screen, click Install Theme, then regenerate all pages. For the full step-by-step process, see <a href="/wiki/themes/how-to-install-a-carl-theme.php">How to install a CARL theme</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/magazine-theme.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Roll Back a Theme in CARL | Restore Your Previous Theme From the Auto-Backup | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/themes/how-to-roll-back-a-theme-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/themes/how-to-roll-back-a-theme-in-carl.php</guid>
    <pubDate>Thu, 04 Jun 2026 20:18:08 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-roll-back-a-theme-in-carl-restore-your-previous-theme-1780618557.webp" alt="How to Roll Back a Theme in CARL | Restore Your Previous Theme From the Auto-Backup | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Roll Back a Theme in CARL</h1>

<p>Every time you install a theme in CARL, your previous template files and include files are saved automatically to a timestamped backup folder on your server. If you install a new theme and want to go back, everything you need is already there. The rollback process takes a few minutes and requires access to your server via cPanel File Manager or FTP.</p><p><img src="/images/how-to-roll-back-a-theme-in-carl-restore-your-previous-theme-1780618557.webp" alt="How to Roll Back a Theme in CARL" class="img-fluid" style=""><br></p>

<h2>Where the Backup Is Stored</h2>

<p>Theme backups are stored in a timestamped folder inside your site's include files directory on the server. The folder name includes the date and time of the install, so you can identify which backup corresponds to which theme. If you've installed multiple themes, you'll have a separate backup folder for each install. Access them via cPanel File Manager or an FTP client.</p>

<h2>What the Backup Contains</h2>

<p>The backup covers your template files from <code>admin/tpl/</code> and all include files from <code>site_includes/</code>. This is everything the theme system touches during an install. It does not cover your page content, images, database records, or any other part of your site. If you need a full site rollback rather than just a theme rollback, use CARL's Backup tool instead.</p>

<h2>How to Restore the Previous Theme</h2>

<p>Open cPanel File Manager or connect via FTP and navigate to the backup folder. Copy the template files back to <code>admin/tpl/</code> and copy the include files back to <code>site_includes/</code>, overwriting the current versions. Take care to copy all files from both folders, not just some, since a partial restore will leave your site with a mixed set of files from two different themes.</p>

<h2>Regenerate to Complete the Rollback</h2>

<p>Once the files are restored, go to Pages in your CARL admin panel and run a bulk regeneration. This rewrites every published page using the restored template files. After regeneration, your site returns to the previous theme. Visit your live site to confirm everything is displaying correctly before considering the rollback complete.</p>

<h2>Preventing the Need for a Rollback</h2>

<p>The most reliable way to avoid rollbacks is to test a new theme on a staging installation before deploying it to your live site. Install the theme on a test CARL install, regenerate, and check every page type — blog layout, full-width layout, and category pages — before touching your live site. If everything looks correct on the test install, you can install with confidence on the live site.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-roll-back-a-theme-in-carl-restore-your-previous-theme-1780618557.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Install a CARL Theme | Upload, Confirm, and Go Live in Under a Minute | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/themes/how-to-install-a-carl-theme.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/themes/how-to-install-a-carl-theme.php</guid>
    <pubDate>Thu, 04 Jun 2026 20:11:21 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-install-a-carl-theme-upload-confirm-and-go-live-in-un-1780618188.webp" alt="How to Install a CARL Theme | Upload, Confirm, and Go Live in Under a Minute | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Install a CARL Theme</h1>

<p>Installing a theme in CARL is a two-step process: upload the package, then confirm the installation. CARL handles everything else — backing up your current files, extracting the new theme, remapping your pages, and registering the new templates. The whole process takes under a minute. Regenerating your pages afterward pushes the new look to your live site.</p><p><img src="/images/how-to-install-a-carl-theme-upload-confirm-and-go-live-in-un-1780618188.webp" alt="How to Install a CARL Theme" class="img-fluid" style=""><br></p>

<h2>Before You Start</h2>

<p>Have your <code>.carltheme</code> file downloaded and ready before opening the Themes page. The installer requires a valid theme package to proceed. It's also worth running a full site backup through CARL's Backup tool before making any major changes, since theme backups cover only templates and include files, not your page content or database.</p>

<h2>Step 1: Open Themes</h2>

<p>In your CARL admin panel, click Themes in the left sidebar. This opens the theme manager where you can upload and install theme packages.</p>

<h2>Step 2: Upload Your Theme File</h2>

<p>Click the upload area or drag your <code>.carltheme</code> file onto it. CARL reads the package immediately and validates its contents. Nothing is installed at this step. If the package is valid, a confirmation panel appears showing the theme name, version, author, and a summary of what will be installed.</p>

<h2>Step 3: Review and Confirm</h2>

<p>Check the confirmation screen to verify the theme details match what you intended to install. When you're satisfied, click Install Theme. CARL backs up your current files, extracts the new templates and include files to their correct locations on your server, registers the new templates in the database, and remaps all your existing pages to the new theme's layouts.</p>

<h2>Step 4: Regenerate Your Pages</h2>

<p>After the install completes, go to Pages in the sidebar and click Regenerate All. This rewrites every published page on your site, applying the new theme. Until you regenerate, your live pages still display the previous theme. Regeneration time depends on how many pages your site has, but on most sites, it completes in a few seconds.</p>

<h2>After Regenerating</h2>

<p>Visit your live site and confirm the new theme is displaying correctly. Check your navigation links, footer content, and sidebar. If the theme uses a header banner image, you may need to upload your header image and update the reference in your <code>site_header.txt</code> include file — each theme may expect a different image filename or dimensions. Run a <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health check</a> after regenerating to confirm all pages are in good shape.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-install-a-carl-theme-upload-confirm-and-go-live-in-un-1780618188.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Theme System Works | Install a Complete Site Skin in One Step | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/themes/how-carls-theme-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/themes/how-carls-theme-system-works.php</guid>
    <pubDate>Thu, 04 Jun 2026 20:06:27 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-theme-system-works-install-a-complete-site-skin-i-1780617885.webp" alt="How CARL&#039;s Theme System Works | Install a Complete Site Skin in One Step | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Theme System Works</h1>

<p>CARL's theme system lets you change the entire look and structure of your site in a single operation. Upload a <code>.carltheme</code> file, confirm the install, and CARL replaces your templates, stylesheets, navigation, footer, and all site-wide include files at once. Every page on your site automatically switches to the new theme. No manual reassignment, no page-by-page updates.</p><p><img src="/images/how-carl-s-theme-system-works-install-a-complete-site-skin-i-1780617885.webp" alt="How CARL's Theme System Works" class="img-fluid" style=""><br></p>

<h2>What a Theme Is</h2>

<p>A CARL theme is a complete site skin packaged as a single <code>.carltheme</code> file. Under the hood, it's a zip archive containing everything your site needs to look and function as designed: page layout templates, all CSS, navigation structure, footer, sidebar widgets, hub card grid, and the full set of include files that wire the layout together.</p><p>One file delivers the entire package.</p>

<p>CARL runs one active theme at a time across your entire site. Installing a new theme replaces the current one everywhere. There's no way to run different themes on different sections, but you can use different page types within a theme — for example, a blog layout for articles and a full-width layout for landing pages — to get the variation you need.</p>

<h2>What Gets Installed</h2>

<p>When you install a theme, CARL writes new versions of your template files and all site include files to your server. This covers your page layouts, stylesheet, navigation, footer, sidebar, hub card grid renderer, and header and footer scripts. Everything that controls how your site looks and is structured gets replaced in one pass.</p>

<p>Your page content is never touched. All your titles, meta descriptions, body copy, images, and settings remain exactly as they are. Themes control how pages look, not what's in them.</p>

<h2>Automatic Backup Before Every Install</h2>

<p>Before overwriting anything, CARL saves a timestamped copy of your current template files and include files to a backup folder on your server. If you install a theme and want to revert, the files you need are still there. For the full rollback process, see <a href="/wiki/themes/how-to-roll-back-a-theme-in-carl.php">How to roll back a theme in CARL</a>.</p>

<h2>Pages Remap Automatically</h2>

<p>CARL tracks which template type each page uses: blog layout, full-width layout, or category layout. When a new theme is installed, CARL automatically updates every page's template assignment to the new theme's equivalent layout. A page that was using the old blog layout switches to the new theme's blog layout without you having to touch it. The remapping happens as part of the install process.</p>

<h2>Regeneration Completes the Switch</h2>

<p>Installing a theme updates your template and includes files on the server, but your published pages are static files that were already written to disk under the previous theme. They continue serving visitors unchanged until you regenerate. After installing, go to Pages and run a bulk regenerate to rewrite every published page using the new theme. That's when the change becomes visible to your site visitors.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-theme-system-works-install-a-complete-site-skin-i-1780617885.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Secure Your CARL Include Files | Safe Input Handling for PHP Forms and Widgets | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/how-to-secure-your-carl-include-files.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/how-to-secure-your-carl-include-files.php</guid>
    <pubDate>Wed, 03 Jun 2026 18:06:06 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-secure-your-carl-include-files-safe-input-handling-fo-1780524236.webp" alt="How to Secure Your CARL Include Files | Safe Input Handling for PHP Forms and Widgets | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Secure Your CARL Include Files</h1>

<p>Include files that display content only are low risk. Include files that accept user input, handle file uploads, or make database calls that carry real attack surface. The rules for securing them are the same rules that apply to any PHP form on any server: validate everything that comes in, sanitize everything that goes out, and never trust user-supplied data.</p><p><img src="/images/how-to-secure-your-carl-include-files-safe-input-handling-fo-1780524236.webp" alt="How to Secure Your CARL Include Files" class="img-fluid" style=""><br></p>

<h2>Sanitize All Output</h2>

<p>Any user-supplied value that gets displayed back on a page must be sanitized before output. Use PHP's <code>htmlspecialchars()</code> on any value you echo back to the browser. This prevents cross-site scripting (XSS) attacks, in which malicious JavaScript is injected through a form field and executed in a visitor's browser. It's a one-line fix that applies to every field: names, emails, addresses, notes, anything a user typed.</p>

<h2>Validate All Input Server-Side</h2>

<p>Client-side validation in JavaScript improves the user experience but provides zero security. Any attacker can bypass it by submitting a request directly to your processor. Validate every field in PHP on the server before doing anything with the data. Check that required fields are present, that email addresses match the expected format, that ZIP codes are numeric and the right length, and that date fields contain valid dates. Reject anything that doesn't pass.</p>

<h2>File Upload Security</h2>

<p>File uploads are the highest-risk element in most include files. Validate the MIME type of every uploaded file server-side, not just the extension. Check file size against a maximum before processing. Store uploaded files outside <code>public_html</code> if they don't need to be publicly accessible, or in a dedicated uploads directory with an <code>.htaccess</code> file that prevents PHP execution inside it. Never trust the filename supplied by the user: generate your own filename on the server when saving the file.</p>

<h2>CSRF Protection on Forms</h2>

<p>Any form that performs an action, such as submitting a lead, processing a request, or writing to a database, should include a CSRF token. Generate a random token when the form page loads, store it in the session, include it as a hidden field in the form, and verify it matches on submission before processing anything. This prevents cross-site request forgery attacks, in which a malicious third-party site tricks a visitor's browser into submitting your form without the visitor's knowledge. CARL's own admin forms use this pattern throughout.</p>

<h2>Honeypot Fields for Spam</h2>

<p>For public-facing forms that don't require a login, a honeypot field is a simple and effective spam deterrent. Add a hidden form field that legitimate visitors will never fill in. If the field contains any value upon submission, silently discard the request. Bots that fill in every field get rejected without any friction for real visitors. This is lighter than a CAPTCHA and effective against most automated spam submissions.</p>

<h2>Performance: Keep Includes Lean</h2>

<p>Include files that run on every page load should be as lightweight as possible. Avoid database queries in sidebar includes if you can cache the output instead. Avoid external API calls in any include that appears site-wide: a slow or unavailable API will slow down or break every page that includes it. Reserve heavy logic for page-specific includes where the impact is contained to a single page rather than your entire site.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-secure-your-carl-include-files-safe-input-handling-fo-1780524236.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Build a Custom Include File in CARL | Add Any Block of HTML or PHP to Any Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/how-to-build-a-custom-include-file.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/how-to-build-a-custom-include-file.php</guid>
    <pubDate>Wed, 03 Jun 2026 18:00:30 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-build-a-custom-include-file-in-carl-add-any-block-of--1780523952.webp" alt="How to Build a Custom Include File in CARL | Add Any Block of HTML or PHP to Any Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Build a Custom Include File in CARL</h1>

<p>Building a custom include file in CARL doesn't require programming experience. If you can write HTML, you can build an include file. If you need PHP logic in it, the same rules apply as any PHP file: write the code, test it, save it. The process from idea to deployment is straightforward.</p><p><img src="/images/how-to-build-a-custom-include-file-in-carl-add-any-block-of--1780523952.webp" alt="How to Build a Custom Include File in CARL" class="img-fluid" style=""><br></p>

<h2>Step 1: Decide What the Include Will Do</h2>

<p>Be clear about what the file will contain before you create it. A promotional banner is a simple HTML block. A contact form is HTML with a PHP processor handling the submission. A sidebar widget displaying recent posts is a PHP snippet that queries the database. The complexity of the include is determined entirely by what you need it to do, not by any CARL requirement.</p>

<h2>Step 2: Create the File</h2>

<p>In the CARL admin panel, go to Include Files and create a new file. Give it a descriptive name that makes its purpose clear: <code>promo_banner.txt</code>, <code>contact_widget.txt</code>, <code>booking_form.txt</code>. The <code>.txt</code> extension is the convention CARL uses for include files, though the file can contain any valid HTML or PHP regardless of extension. Write your content in the editor and save.</p>

<h2>Step 3: Place It on a Page</h2>

<p>You have two ways to deploy a custom include file. The first is through a page's PHP Snippet field: add a standard PHP include call referencing the file, and it will appear at the bottom of that page's content area when generated. The second is through a template file: add the include call directly to the template, and every page that uses that template gets the include automatically. Use the PHP Snippet approach for page-specific includes and the template approach for includes that belong to a category of pages.</p>

<h2>Step 4: Test Before Bulk Deploying</h2>

<p>If you're adding the include to a template that affects many pages, test it on a single page first. Add the include call to one page's PHP Snippet field, generate that page, and check it in a browser. Confirm that the include renders correctly, that any PHP logic executes without errors, and that the layout looks right. Once you're satisfied, move the include call into the template and bulk-regenerate.</p>

<h2>Updating the Include Later</h2>

<p>One of the advantages of the include system is that updating is as simple as building. Open the file in Include Files, make your changes, save, and run a bulk regenerate. Every page that references the file gets the update. If the include is only on specific pages via their PHP Snippet fields, regenerate only those pages. Either way, you're editing one file regardless of how many pages it appears on.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-build-a-custom-include-file-in-carl-add-any-block-of--1780523952.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Site-Wide Include Files Work | Headers, Footers, and Sidebars From One Place | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/how-carls-site-wide-include-files-work.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/how-carls-site-wide-include-files-work.php</guid>
    <pubDate>Wed, 03 Jun 2026 17:55:45 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-site-wide-include-files-work-headers-footers-and--1780523619.webp" alt="How CARL&#039;s Site-Wide Include Files Work | Headers, Footers, and Sidebars From One Place | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Site-Wide Include Files Work</h1>

<p>CARL ships with a set of standard include files that every page template references by default. These files control the site-wide elements that appear on every page: your header, navigation, footer, sidebar content, search bar, signup form, and social links. Each one is a separate file in your <code>site_includes/</code> folder, and each one is yours to edit.</p><p><img src="/images/how-carl-s-site-wide-include-files-work-headers-footers-and--1780523619.webp" alt="How CARL's Site-Wide Include Files Work" class="img-fluid" style=""><br></p>

<h2>The Standard Include Files</h2>

<p>CARL's default templates expect seven include files by name. <code>site_header.txt</code> contains your site header HTML. <code>nav_include.txt</code> contains your navigation menu. <code>footer_include.txt</code> contains your footer. <code>search_bar.txt</code> contains the site search widget. <code>carl_signup_form.txt</code> contains your email signup form. <code>recent_posts.txt</code> contains the recent posts sidebar widget. <code>social_link_list.txt</code> contains your social media links. These filenames are hardcoded into the template files, so the names must match exactly.</p>

<h2>How They Get Into Your Pages</h2>

<p>Each template file contains a PHP include call for each of these files, wrapped in a file existence check. If the file exists, its contents are included at that position in the page. If the file doesn't exist, that position is simply empty. This means you can choose which elements appear on your site by controlling which include files you create and populate. A template that references <code>carl_signup_form.txt</code> won't show a signup form until that file exists and contains form code.</p>

<h2>Editing Site-Wide Elements</h2>

<p>To change your navigation, open <code>nav_include.txt</code> in the Include Files manager, make your edits, save, and run a bulk regenerate. Every page on your site gets the updated navigation in a single pass. The same process applies to your footer, your header, your signup form, or any other standard include. There is no theme customizer, no menu management screen, no widget drag-and-drop interface. You edit the file and regenerate.</p>

<h2>Customizing Per-Template</h2>

<p>Different templates can reference different include files. If you have a landing page template that shouldn't show the sidebar or signup form, simply omit the include calls for those elements from that template. A blog template might show the full sidebar with recent posts and social links. A sales page template might show nothing but the header and footer. Each template decides independently which includes it pulls in, giving you precise control over what appears on each type of page.</p>

<h2>Header Scripts and CSS</h2>

<p>Two additional include files sit outside the visible layout: <code>header_scripts.txt</code> and <code>site_css.txt</code>. These are injected into the <code></code> section of every page. <code>header_scripts.txt</code> is where your Google Analytics tag, custom fonts, or any other third-party head code lives. <code>site_css.txt</code> is where you add custom CSS that applies across your entire site. Update either file and bulk regenerate to push the changes everywhere.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-site-wide-include-files-work-headers-footers-and--1780523619.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>What You Can Build With CARL Include Files | PHP Applications Without Plugins or Subscriptions | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/what-you-can-build-with-carl-include-files.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/what-you-can-build-with-carl-include-files.php</guid>
    <pubDate>Wed, 03 Jun 2026 17:49:47 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/what-you-can-build-with-carl-include-files-php-applications--1780523223.webp" alt="What You Can Build With CARL Include Files | PHP Applications Without Plugins or Subscriptions | Carl Riedel" style="max-width:100%;height:auto;">
<h1>What You Can Build With CARL Include Files</h1>

<p>Because include files can contain any valid PHP and HTML, they're not just for navigation and footers. They're a full application injection point. Anything that runs on PHP can be deployed to a CARL page via an include file, without a plugin, third-party service, or recurring subscription.</p><p><img src="/images/what-you-can-build-with-carl-include-files-php-applications--1780523223.webp" alt="What You Can Build With CARL Include Files" class="img-fluid" style=""><br></p>

<h2>Lead Generation Forms</h2>

<p>A multi-step quote form, a contact form, a callback request widget — all of these can be built as include files and placed on any page. The form submits to a PHP processor on your server, which handles validation, stores the lead, sends a notification email, and redirects back with a success or error status. The entire flow runs on your own hosting account with no external dependency.</p>

<p>Two real examples: a 4-step moving company quote form that walks visitors through their contact details, pickup address, destination, and photo uploads, with client-side step validation and a progress indicator. And a pest control service request form with a honeypot field for spam protection and a direct post to a leads processor on a subdomain.</p><p>Both are self-contained include files. No WPForms license. No Gravity Forms subscription. No form builder account.</p>

<h2>Booking and Appointment Widgets</h2>

<p>A service business that needs a booking form can build one as an include file: date picker, time slot selection, service type, contact fields, and a submission handler that writes to the database and sends a confirmation email. Place it on a service page, a landing page, or a dedicated booking page. Update the available time slots by editing one file.</p>

<h2>Price Calculators and Estimators</h2>

<p>A calculator that takes user inputs and returns a price estimate is a natural fit for an include file. The logic runs in JavaScript on the client side, or in PHP if the calculation requires server-side data. Either way, the whole thing lives in a single file that can be included on any page.</p>

<h2>Promotional Banners and Announcements</h2>

<p>A site-wide promotional banner — a sale, a deadline, a new product announcement — can be managed through a single include file. Add it to your template, set it live, and it appears on every page instantly. When the promotion ends, remove the content from the file and run a bulk regeneration. No plugin toggle. No widget management screen. One file edit.</p>

<h2>The WordPress Comparison</h2>

<p>WordPress users solve these problems with plugins: WPForms or Gravity Forms for lead capture, a booking plugin for appointments, a calculator plugin for estimators, and a notification bar plugin for banners. Each plugin is a subscription, a potential conflict, and a door into your site controlled by someone else. CARL users write PHP. The functionality is identical. The cost and the risk are not.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/what-you-can-build-with-carl-include-files-php-applications--1780523223.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Includes System Works | Site-Wide Content Blocks Controlled From One File | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/how-carls-includes-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/how-carls-includes-system-works.php</guid>
    <pubDate>Wed, 03 Jun 2026 17:43:57 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-includes-system-works-site-wide-content-blocks-co-1780522746.webp" alt="How CARL&#039;s Includes System Works | Site-Wide Content Blocks Controlled From One File | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Includes System Works</h1>

<p>CARL's includes system lets you define a block of HTML or PHP once and have it appear on every page that needs it. Your site navigation, footer, sidebar, and signup form all work this way. The content lives in a single file. Every page that references it pulls from that file at the time of generation. Change the file, regenerate your pages, and the change is everywhere.</p><p><img src="/images/how-carl-s-includes-system-works-site-wide-content-blocks-co-1780522746.webp" alt="How CARL's Includes System Works" class="img-fluid" style=""><br></p>

<h2>What an Include File Is</h2>

<p>An include file is a plain text file stored in your <code>site_includes/</code> folder inside <code>public_html</code>. It contains whatever you want it to contain: HTML, PHP, JavaScript, a form, a widget, a navigation menu. There's no special syntax and no CARL-specific format. It's just a file with content, and CARL pulls it into your pages using a standard PHP include call.</p>

<h2>How Templates Use Include Files</h2>

<p>Every CARL page template contains PHP include calls that reference specific files in <code>site_includes/</code>. When CARL generates a page, it builds the complete PHP file from the template, writing those include calls into the output. When a visitor loads the page, PHP executes the include calls, and the content from each file is inserted at the correct position. Your navigation appears at the top, your footer at the bottom, your sidebar in the right column, exactly where the template expects them.</p>

<h2>The Power of a Single Source File</h2>

<p>Because every page references the same file, there is only ever one version of your navigation, one version of your footer, one version of your signup form. You don't maintain copies. You don't risk them falling out of sync. You update the source file, and every page on your site reflects the change on the next load. A site with 500 pages gets a navigation update in the same amount of time as a site with 5.</p>

<h2>Include Files vs. Page Content</h2>

<p>Include files handle everything that surrounds your content: layout chrome, site-wide elements, and reusable blocks. Page content lives in the page record itself. This separation is deliberate. It keeps your content clean and portable, and it keeps your site infrastructure manageable. If you ever change your navigation structure, you only need to change one file. Your page content is untouched.</p>

<h2>Beyond Headers and Footers</h2>

<p>The system isn't limited to structural elements. Because include files can contain any valid PHP code, you can use them to deploy interactive applications, lead-generation forms, booking widgets, and multi-step quote systems directly into any page. The same mechanism that puts your footer on every page can put a fully functional quote form on a specific page. For a full look at what's possible, see <a href="/wiki/includes/what-you-can-build-with-carl-include-files.php">What you can build with CARL include files</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-includes-system-works-site-wide-content-blocks-co-1780522746.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Manage Include Files in CARL | Site-Wide Content Blocks From One Place | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/includes/how-to-manage-include-files.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/includes/how-to-manage-include-files.php</guid>
    <pubDate>Wed, 03 Jun 2026 17:29:32 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-manage-include-files-in-carl-site-wide-content-blocks-1780522070.webp" alt="How to Manage Include Files in CARL | Site-Wide Content Blocks From One Place | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Manage Include Files in CARL</h1>

<p>Include files are text files stored in your <code>site_includes/</code> folder that gets pulled into your published pages at the point defined by your template. Your site header, navigation, footer, sidebar, signup form, and search bar all work this way. Edit the file once, regenerate your pages, and every page on your site reflects the change.</p><p><img src="/images/how-to-manage-include-files-in-carl-site-wide-content-blocks-1780522070.webp" alt="How to Manage Include Files in CARL" class="img-fluid" style=""><br></p>

<h2>Where Include Files Live</h2>

<p>All include files are stored in the <code>site_includes/</code> directory inside your <code>public_html</code> folder. CARL's default templates expect specific filenames: <code>site_header.txt</code>, <code>nav_include.txt</code>, <code>footer_include.txt</code>, <code>search_bar.txt</code>, <code>carl_signup_form.txt</code>, <code>recent_posts.txt</code>, and <code>social_link_list.txt</code>. These filenames are referenced directly in the template files, so the name has to match exactly.</p>

<h2>Managing Include Files in the Admin Panel</h2>

<p>In the CARL admin panel, go to Include Files. You'll see a list of all files currently in your <code>site_includes/</code> directory. Click any file to open it in the editor. Make your changes and save. The updated file is written to disk immediately. Your published pages don't reflect the change until you regenerate them, since the included content is baked into the static file at generation time.</p>

<h2>Pushing Changes to All Pages</h2>

<p>After saving changes to an include file, run a bulk regenerate to push the update across your entire site. Every published page that uses a template referencing that include file will be rebuilt with the new content. For a site with hundreds of pages, this takes seconds. For the full walkthrough, see <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">How to bulk regenerate pages in CARL</a>.</p>

<h2>Creating Custom Include Files</h2>

<p>You're not limited to CARL's default include filenames. Create any file in <code>site_includes/</code> with any name you choose and reference it in a template or a page's PHP Snippet field using a standard PHP include call. This is how you add custom sidebar widgets, promotional banners, or any reusable block of HTML or PHP to your pages without editing every page individually. For a full walkthrough of building custom include files, see <a href="/wiki/includes/how-to-build-a-custom-include-file.php">How to build a custom include file in CARL</a>.</p>

<h2>Include Files Are Not Templates</h2>

<p>Include files handle content blocks. Templates handle page structure. The distinction matters: changing a template affects the layout of every page using it, while changing an include file only affects the content of the block that file provides. Keep layout decisions in templates and content decisions in include files, and site-wide updates stay straightforward.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-manage-include-files-in-carl-site-wide-content-blocks-1780522070.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Hub Renderer Works | Automatic Article Cards From Your Published Pages | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/content/how-carls-hub-renderer-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/content/how-carls-hub-renderer-works.php</guid>
    <pubDate>Wed, 03 Jun 2026 16:38:54 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-hub-renderer-works-automatic-article-cards-from-y-1780519021.webp" alt="How CARL&#039;s Hub Renderer Works | Automatic Article Cards From Your Published Pages | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Hub Renderer Works</h1>

<p>CARL's hub renderer automatically generates a grid of article cards for any directory on your site. Point it at a directory, and it queries the database for all published pages in that directory and renders them as a Bootstrap card grid, complete with title, meta description, and a read more link. No manual list maintenance, no editing the hub page every time you publish a new article.</p><p><img src="/images/how-carl-s-hub-renderer-works-automatic-article-cards-from-y-1780519021.webp" alt="How CARL's Hub Renderer Works" class="img-fluid" style=""><br></p>

<h2>How It Works</h2>

<p>The hub renderer is a PHP include that accepts a directory path as its input. When the hub page loads, the renderer queries the database for all published pages whose directory matches the one you specified, orders them by publish date, and renders a card for each. Add a new article to the directory, publish it, and it appears on the hub page automatically on the next load. Nothing else required.</p>

<h2>What Each Card Contains</h2>

<p>Each card displays the page title, the meta description, and a link to the full article. This is why well-written meta descriptions matter on every page: the meta description is what visitors read on the hub page when deciding which article to click. A blank or weak meta description produces a weak card. The Site Health checker flags pages with missing meta descriptions for exactly this reason.</p>

<h2>Hub Pages in CARL</h2>

<p>A hub page in CARL is just a regular page with the hub renderer included in its PHP Snippet field and a short introductory paragraph in the body. The renderer handles everything else. This is why hub pages should never contain manual lists of article links: the renderer already outputs those cards dynamically, and manual links would duplicate them and fall out of sync every time you add or remove an article.</p>

<h2>Using the Hub Renderer for Any Directory</h2>

<p>The hub renderer isn't limited to wiki clusters. You can use it on any page where you want an auto-generated index of content from a specific directory: a blog index, a use case listing, a product documentation section. Any directory with published pages in it can have a hub page built this way. The renderer reads from the database, so the output is always up to date without any manual maintenance.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-hub-renderer-works-automatic-article-cards-from-y-1780519021.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Site Search Works | Built-In Search Without a Plugin or Third-Party Service | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-carls-site-search-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-carls-site-search-works.php</guid>
    <pubDate>Wed, 03 Jun 2026 16:13:46 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-site-search-works-built-in-search-without-a-plugi-1780361300.webp" alt="How CARL&#039;s Site Search Works | Built-In Search Without a Plugin or Third-Party Service | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Site Search Works</h1>

<p>CARL includes a built-in site search that lets visitors search your content without a third-party service, a search plugin, or an API subscription. The search functionality is part of CARL's core install. You add the search bar to your pages using an include file, and the search dashboard in the admin gives you visibility into what your visitors are looking for.</p><p><img src="/images/how-carl-s-site-search-works-built-in-search-without-a-plugi-1780361300.webp" alt="How CARL's Site Search Works" class="img-fluid" style=""><br></p>

<h2>The Search Bar Widget</h2>

<p>CARL's search bar lives in an include file called <code>search_bar.txt</code>. If you're using the Bootstrap Blog template, the search bar is already present in the sidebar by default. It's one of the standard include files that the Blog template loads automatically, so any page using that template has search built in without any additional setup required.</p>

<p>For pages using a different template, or for pages where you want the search bar in a different position, you add it through the PHP Snippet field. Open the page in the editor, go to the PHP Snippet field, open the Include Picker, select <code>search_bar.txt</code>, and click Insert. When you regenerate the page, the search bar is present at that position.</p>

<h2>How Search Works on a Static Site</h2>

<p>CARL pages are static PHP files, which means there's no dynamic page assembly happening at request time. The search system works within that constraint: when a visitor submits a search query, the request goes to CARL's search handler, which queries the database for matching page records and returns the results. The search results page is served dynamically, while the individual content pages themselves remain static files.</p>

<p>This is a clean division of responsibility. Your content pages are fast, secure, static files. Search is a separate, contained operation that only runs when a visitor actively requests it. The search function doesn't add any overhead to regular page loads.</p>

<h2>The Search Dashboard</h2>

<p>In the CARL admin panel, go to Tools, then Search. The search dashboard shows you what queries visitors have been entering on your site. This data is genuinely useful for content planning: recurring searches for topics you haven't covered yet are a direct signal of what your audience wants. If visitors keep searching for something and not finding it, that's an article to write.</p>

<h2>Search and Your Content Structure</h2>

<p>CARL's search indexes your published pages by their titles and content. Pages that are clearly titled, well-structured, and contain the specific language your audience uses will return better search results than pages with vague titles or thin content. The same principles that make a page rank well in Google also ensure it surfaces correctly in CARL's internal search.</p>

<p>Hub pages and index pages are generally less useful search results than individual articles. If you want to refine what appears in search results over time, the search dashboard gives you the data to make those decisions based on what visitors are actually looking for rather than what you assume they want.</p>

<h2>Setting Up Search on a New Install</h2>

<p>If you're using the Bootstrap Blog template for your articles, the search is already in place, and nothing further is needed. If you're using a custom template and want search available sitewide, add <code>search_bar.txt</code> to your template directly via Include Files so it appears on every page using that template without requiring individual PHP snippet insertions. For a full walkthrough of how include files work in CARL, see <a href="/wiki/includes/how-to-manage-include-files.php">How to manage include files in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-site-search-works-built-in-search-without-a-plugi-1780361300.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Optimizes Images | Automatic Compression and WebP Conversion on Upload | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/content/how-carl-optimizes-images.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/content/how-carl-optimizes-images.php</guid>
    <pubDate>Wed, 03 Jun 2026 16:09:01 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-optimizes-images-automatic-compression-and-webp-con-1780517241.webp" alt="How CARL Optimizes Images | Automatic Compression and WebP Conversion on Upload | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Optimizes Images</h1>

<p>When you upload an image through the CARL admin panel, CARL processes it automatically before saving it to your server. Compression is applied, oversized images are resized to a sensible maximum width, and the file is converted to WebP format. By the time the image is available to use on a page, the heavy lifting is already done.</p><p><img src="/images/how-carl-optimizes-images-automatic-compression-and-webp-con-1780517241.webp" alt="How CARL Optimizes Images" class="img-fluid" style=""><br></p>

<h2>Why Image Optimization Matters for SEO</h2>

<p>Page speed is a ranking factor. Large, unoptimized images are among the most common reasons pages load slowly, and slow pages rank lower and convert worse. CARL handles optimization at upload time, so you never have to think about it. You upload the photo from your phone or camera, and what ends up on your server is a compressed, correctly sized WebP file, a fraction of the original size.</p>

<h2>WebP Conversion</h2>

<p>WebP is the image format Google recommends for web use. It produces smaller file sizes than JPG or PNG at equivalent visual quality, which means faster page loads and lower bandwidth usage. CARL converts uploaded images to WebP automatically using PHP's GD library. The original file format doesn't matter: JPG, PNG, and GIF uploads are all converted to WebP on your server.</p>

<h2>Resizing and Compression</h2>

<p>CARL resizes images that exceed the configured maximum width, scaling them down proportionally. Images already within the limit are left at their original dimensions. Compression is applied during the WebP conversion process. The result is an image that looks correct on your pages without carrying the file weight of an unoptimized original.</p>

<h2>What Happens to the Original</h2>

<p>CARL works on the uploaded file during processing and saves the optimized WebP version to your images directory. You don't need to pre-optimize images before uploading. Upload what you have, and CARL handles the rest. The Image Manager in the admin panel displays your uploaded images and their file sizes, so you can see exactly what's on your server.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-optimizes-images-automatic-compression-and-webp-con-1780517241.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Popup Management | Timed and Exit-Intent Popups Without a Plugin | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/conversion-tools/how-carl-handles-popup-management.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/conversion-tools/how-carl-handles-popup-management.php</guid>
    <pubDate>Wed, 03 Jun 2026 14:09:24 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-popup-management-timed-and-exit-intent-popu-1780510023.webp" alt="How CARL Handles Popup Management | Timed and Exit-Intent Popups Without a Plugin | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Popup Management</h1>

<p>CARL includes a built-in popup manager that lets you create and deploy popups on any page without a plugin or third-party service. Popups are configured in the admin panel and written into the generated page file, so they load instantly with no external script required.</p><p><img src="/images/how-carl-handles-popup-management-timed-and-exit-intent-popu-1780510023.webp" alt="How CARL Handles Popup Management" class="img-fluid" style=""><br></p>

<h2>Popup Trigger Types</h2>

<p>CARL supports two trigger types: timed and exit-intent. A timed popup appears after the visitor has been on the page for a specified number of seconds. An exit-intent popup fires when the visitor's cursor moves toward the top of the browser window, indicating they're about to leave. You set the trigger type and timing when building the popup, with no code required.</p>

<h2>Creating a Popup</h2>

<p>In the CARL admin panel, go to Popup Manager. Create a new popup and fill in the content: headline, body text, and an optional button with a label and URL. Set the trigger type and, for timed popups, the delay in seconds. Give the popup a name for identification and save it. The popup is now available to assign to any page on your site.</p>

<h2>Assigning Popups to Pages</h2>

<p>When editing a page in CARL, the popup field lets you select any saved popup to attach to that page. When you generate the page, CARL writes the popup HTML and trigger logic directly into the file. The popup runs from code already present in the page, with no dependency on an external popup service loading at runtime.</p>

<h2>Frequency Control</h2>

<p>CARL sets a cookie when a visitor dismisses a popup so it doesn't reappear on every visit. The cookie has a configurable duration, so you control how long a visitor is suppressed before the popup shows again. This keeps the experience from becoming intrusive while still re-engaging visitors who return after the suppression window has expired.</p>

<h2>Updating Popups Across Pages</h2>

<p>Like the CTA Builder, a single popup record can be assigned to multiple pages. Update the popup content once, then run a bulk regenerate to push the change across every page that uses it. If you want to remove a popup from all pages at once, clear the popup assignment and regenerate. No page-by-page editing required.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-popup-management-timed-and-exit-intent-popu-1780510023.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's CTA Builder Works | Add Calls to Action to Any Page Without Editing Templates | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/conversion-tools/how-carls-cta-builder-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/conversion-tools/how-carls-cta-builder-works.php</guid>
    <pubDate>Wed, 03 Jun 2026 14:05:08 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-cta-builder-works-add-calls-to-action-to-any-page-1780509840.webp" alt="How CARL&#039;s CTA Builder Works | Add Calls to Action to Any Page Without Editing Templates | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's CTA Builder Works</h1>

<p>CARL's CTA Builder lets you create styled calls to action and place them on any page without touching a template file. You build the CTA once in the admin panel, assign it to a page, and CARL writes it into the generated file at the correct position when you publish or regenerate.</p><p><img src="/images/how-carl-s-cta-builder-works-add-calls-to-action-to-any-page-1780509840.webp" alt="How CARL's CTA Builder Works" class="img-fluid" style=""><br></p>

<h2>What a CTA Is in CARL</h2>

<p>A CTA in CARL is a configurable block: a headline, supporting text, a button label, and a button URL. You control the styling through the builder interface. The output is a self-contained HTML block injected into the page at the point defined by your template. You don't need to write any HTML or edit any files to produce it.</p>

<h2>Creating a CTA</h2>

<p>In the CARL admin panel, go to CTA Builder. Click to create a new CTA and fill in the fields: headline, body text, button label, and destination URL. Give the CTA a name so you can identify it when assigning it to pages. Save it, and it becomes available to use on any page on your site.</p>

<h2>Assigning a CTA to a Page</h2>

<p>When creating or editing a page, the CTA field lets you select any saved CTA to attach to that page. When you generate the page, CARL pulls the CTA content and writes it into the file. If you update the CTA record later and regenerate the page, the page reflects the updated version. One CTA can be assigned to multiple pages, so a single update propagates to every page where it's used.</p>

<h2>Updating CTAs Across Multiple Pages</h2>

<p>This is where the CTA Builder earns its place. If you're running a promotion and need to update the button text or destination URL across 50 pages, you change the CTA record once and run a bulk regenerate. Every page using that CTA is updated in a single pass. No manual editing, no risk of missing a page, no inconsistency across your site. For how to run a bulk regeneration, see <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">How to bulk regenerate pages in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-cta-builder-works-add-calls-to-action-to-any-page-1780509840.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Integrates With Kit | Push New Subscribers Directly to Your Kit List | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/email-subscribers/how-carl-integrates-with-kit.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/email-subscribers/how-carl-integrates-with-kit.php</guid>
    <pubDate>Wed, 03 Jun 2026 13:12:04 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-integrates-with-kit-push-new-subscribers-directly-t-1780506612.webp" alt="How CARL Integrates With Kit | Push New Subscribers Directly to Your Kit List | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Integrates With Kit</h1>

<p>CARL connects directly to Kit (formerly ConvertKit), so new subscribers are added to your Kit list the moment they sign up. When the integration is active, CARL stores the subscriber record locally and forwards it to Kit simultaneously. You get a copy on your own server, and the subscriber lands in Kit ready to receive your sequences.</p><p><img src="/images/how-carl-integrates-with-kit-push-new-subscribers-directly-t-1780506612.webp" alt="How CARL Integrates With Kit" class="img-fluid" style=""><br></p>

<h2>How the Integration Works</h2>

<p>When a visitor submits your signup form, CARL calls the Kit API in the background using your API key and the form ID you've configured. Kit processes the submission and adds the subscriber to the associated form, which triggers any automation or sequence attached to that form. From the subscriber's perspective, the experience is identical to submitting a native Kit form, just hosted on your domain.</p>

<h2>Setting Up the Integration</h2>

<p>In the CARL admin panel, go to Settings and open the Subscribers tab. Enter your Kit API key and your Kit form ID. Your API key is available in your Kit account under Settings, then Developer. Your form ID is the number in the URL when you open a form in the Kit dashboard. Save the settings, and the integration is active immediately for all new signups through CARL's native form.</p>

<h2>Finding Your Kit Form ID</h2>

<p>In your Kit account, go to Grow, then Landing Pages and Forms. Open the form you want to connect. Look at the URL in your browser. The form ID is the number at the end of the URL. It typically looks like a 7-digit number. Copy that number into the Kit Form ID field in CARL's Settings. CARL uses this ID to submit new subscribers to the correct form, triggering the appropriate automation in Kit.</p>

<h2>What Gets Sent to Kit</h2>

<p>CARL sends the subscriber's email address and first name to Kit on each new signup. Kit matches these to its standard subscriber fields. Tags, custom fields, and sequences are managed on the Kit side through your form's automation settings, not through CARL. If you want new subscribers to receive a specific sequence or be tagged on signup, configure that in Kit on the form itself.</p>

<h2>Fallback Behavior</h2>

<p>If the Kit API call fails for any reason, CARL still saves the subscriber record locally. The signup is never lost. Failed Kit submissions are logged so you can identify them and add them to Kit manually if needed. This means a temporary Kit outage or API issue won't result in lost subscribers on your end.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-integrates-with-kit-push-new-subscribers-directly-t-1780506612.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Embed a Signup Form in CARL | Add Your Email Form to Any Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/email-subscribers/how-to-embed-a-signup-form.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/email-subscribers/how-to-embed-a-signup-form.php</guid>
    <pubDate>Wed, 03 Jun 2026 13:06:26 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-embed-a-signup-form-in-carl-add-your-email-form-to-an-1780505894.webp" alt="How to Embed a Signup Form in CARL | Add Your Email Form to Any Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Embed a Signup Form in CARL</h1>

<p>CARL offers two ways to add a signup form to your site: a native form that submits to CARL's built-in subscriber system, and an embed code option for pasting a form from an external platform like Kit. Both methods work on any published page.</p><p><img src="/images/how-to-embed-a-signup-form-in-carl-add-your-email-form-to-an-1780505894.webp" alt="How to Embed a Signup Form in CARL" class="img-fluid" style=""><br></p>

<h2>Using CARL's Native Signup Form</h2>

<p>CARL's native form is included via the include file system. The signup form is stored as an include file and can be added to any page template or injected into individual pages through the Head Injection field. When a visitor submits the native form, the data is stored directly in your CARL subscriber database. No external service is required.</p>

<p>The native form collects an email address and an optional name field. The form handles its own validation, duplicate checking, and confirmation message. You can customize the form's appearance by editing the include file or adding CSS to your site stylesheet.</p>

<h2>Embedding a Form From Kit</h2>

<p>If you're using Kit as your email platform, you can paste your Kit form embed code directly into any CARL page using the Head Injection field or the page body. Kit provides the embed code from your Forms section in the Kit dashboard. Copy the inline or hosted form embed snippet, paste it into the relevant field in the CARL page editor, and regenerate the page. The form renders and submits directly to Kit.</p>

<p>This approach is useful when you want Kit to handle confirmation emails, tags, and sequences from the moment of signup. The subscriber goes straight into Kit without any manual export or sync step required.</p>

<h2>Adding a Form to Multiple Pages at Once</h2>

<p>The most efficient way to add a signup form across your entire site is through an include file. Save your form code to a site, include it in your page template, and every page using that template will display the form. If you update the form later, update the include file and run a bulk regenerate to push the change to all existing pages at once. For information on how to manage include files, see <a href="" target="_blank">"How to manage include files in CARL"</a>.</p>

<h2>Sidebar and Inline Placement</h2>

<p>CARL's default blog templates include a sidebar column that already pulls in a signup form include file. If you're using one of those templates, the form appears automatically on every page using that layout once the include file contains your form code. For pages using a single-column template or a custom layout, add the form include call directly to the template file, or paste the form code into the page body at the point where you want it to appear.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-embed-a-signup-form-in-carl-add-your-email-form-to-an-1780505894.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Subscriber System Works | Email List Management Built Into the CMS | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/email-subscribers/how-carls-subscriber-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/email-subscribers/how-carls-subscriber-system-works.php</guid>
    <pubDate>Wed, 03 Jun 2026 12:46:55 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-subscriber-system-works-email-list-management-bui-1780505114.webp" alt="How CARL&#039;s Subscriber System Works | Email List Management Built Into the CMS | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Subscriber System Works</h1>

<p>CARL includes a built-in email subscriber system that collects and manages your list directly on your server. Visitors sign up through a form on your site, their details are stored in your database, and you can export the list at any time. No third-party list service is required to start collecting subscribers.</p><p><img src="/images/how-carl-s-subscriber-system-works-email-list-management-bui-1780505114.webp" alt="How CARL's Subscriber System Works" class="img-fluid" style=""><br></p>

<h2>How Signups Work</h2>

<p>When a visitor submits your signup form, CARL records their email address, an optional name field, the page they signed up from, and a timestamp. Each new subscriber is added with confirmed status by default, which means they're immediately on your list and no email verification step is required. If you're sending to a platform that requires confirmed opt-in, you can use the Kit integration to handle that on the platform side.</p>

<h2>Subscriber Records</h2>

<p>Each subscriber record includes their email, name, sign-up date, source page, and current status: active, unsubscribed, or bounced. The status can be changed manually from the Subscribers dashboard in the admin panel. Active subscribers appear in your exports. Unsubscribed and bounced records are retained in the database but excluded from exports by default, so you have a complete history without contaminating your active list.</p>

<h2>The Subscribers Dashboard</h2>

<p>In the CARL admin panel, the Subscribers section shows your total subscriber count, a breakdown by status, and a list of recent signups. You can search by email or name, filter by status, and delete individual records. The dashboard also shows which pages are generating the most signups, so you can see at a glance which forms and content are working.</p>

<h2>Exporting Your List</h2>

<p>The export function downloads your active subscriber list as a CSV with one click. The file includes email, name, signup date, and source page for every active subscriber. Use this to import into your email-sending platform, back up your list, or cross-reference it against your sending platform's own list. Your subscribers are in your database on your server, not locked inside a platform you don't control.</p>

<h2>Kit Integration</h2>

<p>If you're using Kit as your email sending platform, CARL can push new subscribers directly to your Kit list at signup instead of storing them locally only. See <a href="/wiki/email-subscribers/how-carl-integrates-with-kit.php">How CARL integrates with Kit</a> for the setup steps. Both systems can run in parallel: CARL stores the record locally and forwards it to Kit at the same time, so you always have a copy of your list on your own server regardless of what happens with your Kit account.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-subscriber-system-works-email-list-management-bui-1780505114.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Restricts Pages to Members Only | Access Control Built Into Every Protected Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/members/how-carl-restricts-pages-to-members.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/members/how-carl-restricts-pages-to-members.php</guid>
    <pubDate>Tue, 02 Jun 2026 18:54:25 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-restricts-pages-to-members-only-access-control-buil-1780440457.webp" alt="How CARL Restricts Pages to Members Only | Access Control Built Into Every Protected Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Restricts Pages to Members Only</h1>

<p>When you generate a members-only page in CARL, the access control code is written directly into the page file. There's no middleware, no separate authentication layer, and no request routing through the admin panel. The protection is built directly into the file that lives on your server.</p><p><img src="/images/how-carl-restricts-pages-to-members-only-access-control-buil-1780440457.webp" alt="How CARL Restricts Pages to Members Only" class="img-fluid" style=""><br></p>

<h2>How the Guard Works</h2>

<p>At the top of every protected page CARL generates, an access guard is injected before any content is rendered. The guard reads the member session cookie, validates the token against the database, confirms the account is active, and checks the member's access level against the level required for that page. All of this happens before a single line of page content is sent to the browser.</p>

<p>If the visitor has no valid session cookie, they're redirected to <code>/members/login.php</code> with the current URL attached as a redirect parameter. After logging in, they're returned to the page they were trying to reach. If they're logged in but their access level is too low, they're redirected to <code>/members/upgrade.php</code> instead.</p>

<h2>Setting the Required Access Level</h2>

<p>When creating or editing a page in CARL, the members' access setting lets you choose which membership level is required: free or premium. Free-level pages are accessible to any logged-in member regardless of their tier. Premium-level pages are restricted to members with premium access only. Pages with no access restriction are public and visible to everyone.</p>

<h2>What Happens if the Database Is Unavailable</h2>

<p>Because the guard makes a database call to validate the session, a protected page cannot be served if the database is down. The guard will fail to load the session and treat the visitor as unauthenticated, redirecting them to the login page. This is the correct behavior: it's better to redirect than to accidentally serve protected content. Your public pages, which have no guard code, are unaffected and continue to load from disk as normal.</p>

<h2>Regenerating Protected Pages</h2>

<p>If you change the access level setting on a page after it has already been published, you need to regenerate the page for the change to take effect on disk. The guard code embedded in the existing file still reflects the old setting until you regenerate. Use <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> when updating access levels across many pages at once.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-restricts-pages-to-members-only-access-control-buil-1780440457.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Create a Members-Only Content Area in CARL | Free and Premium Tiers on One Site | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/members/how-to-create-members-only-content.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/members/how-to-create-members-only-content.php</guid>
    <pubDate>Tue, 02 Jun 2026 18:54:11 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-create-a-members-only-content-area-in-carl-free-and-p-1780440727.webp" alt="How to Create a Members-Only Content Area in CARL | Free and Premium Tiers on One Site | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Create a Members-Only Content Area in CARL</h1>

<p>A members-only content area in CARL is a section of your site where some or all pages require a login to view. You can run free and premium tiers on the same site, with different pages accessible to each level. The setup is entirely managed through the page editor and your Members settings, with no code required.</p><p><img src="/images/how-to-create-a-members-only-content-area-in-carl-free-and-p-1780440727.webp" alt="How to Create a Members-Only Content Area in CARL" class="img-fluid" style=""><br></p>

<h2>Plan Your Access Structure First</h2>

<p>Before creating any pages, decide which content is public, which is free-member-only, and which is premium-only. CARL supports three states for any page: public (accessible to all logged-in members), accessible to all logged-in members, or accessible to premium members only. A clear structure before you start saves you from having to change access levels and regenerate pages later.</p>

<h2>Set Up the Member Pages</h2>

<p>Your members' area needs a small set of supporting pages: a registration page, a login page, a member dashboard, and an upgrade page for logged-in visitors trying to access premium content. Create these first using the appropriate CARL member templates before you start restricting any content pages.</p><p>The login page must be at <code>/members/login.php</code> since that's where CARL's access guard redirects unauthenticated visitors. For the full setup walkthrough, see <a href="/wiki/members/how-to-set-up-member-registration.php">How to set up member registration in CARL</a>.</p>

<h2>Restrict Your Content Pages</h2>

<p>With the supporting pages in place, open any content page you want to protect in the CARL page editor. Find the members' access setting and select the required level: free membership or premium. Save and generate the page. The access guard is written into the file at that point. Repeat for every page you want to restrict.</p>

<p>There's no requirement that all protected pages use the same access level. You can have a mix of public articles, free-member guides, and premium content on the same site. Each page has its own access level.</p>

<h2>Directing Visitors to Register</h2>

<p>Public pages that preview your members' content are your main conversion tool. Link to the registration page from those public pages at the natural point where a visitor would want more. A public introduction page that ends with a clear call to action linking to registration converts better than a wall that simply redirects visitors without explanation.</p>

<h2>Managing the Content Area Over Time</h2>

<p>As your members area grows, use the Members dashboard to monitor signups, approve pending registrations if you're running approval mode, and upgrade free members to premium when appropriate. If you restructure your access levels at any point, remember to regenerate the affected pages so the updated guard code is written to disk. The content itself doesn't change during a regeneration, only the access rules embedded in the file.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-create-a-members-only-content-area-in-carl-free-and-p-1780440727.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Set Up Member Registration in CARL | Configure Open or Approval-Based Signup | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/members/how-to-set-up-member-registration.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/members/how-to-set-up-member-registration.php</guid>
    <pubDate>Tue, 02 Jun 2026 18:40:11 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-set-up-member-registration-in-carl-configure-open-or--1780439285.webp" alt="How to Set Up Member Registration in CARL | Configure Open or Approval-Based Signup | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Set Up Member Registration in CARL</h1>

<p>Before visitors can register as members on your CARL site, you need to create the registration and login pages and configure the registration mode in Settings. The setup takes a few minutes and doesn't require any code.</p><p><img src="/images/how-to-set-up-member-registration-in-carl-configure-open-or--1780439285.webp" alt="How to Set Up Member Registration in CARL" class="img-fluid" style=""><br></p>

<h2>Configure Registration Settings</h2>

<p>In the CARL admin panel, go to Settings and open the Members tab. The registration mode setting controls how new signups are handled. Set it to open if you want new accounts to become active immediately after registration. Set it to approval-required if you want to review and manually approve each registration before the account becomes active. You can change this setting at any time without affecting existing members.</p>

<h2>Create Your Member Pages</h2>

<p>CARL's membership system requires a set of pages on your site to handle the member-facing flows. At minimum, you need a registration page, a login page, and a members' dashboard page. In the CARL admin, create these pages using the members-specific templates. The templates include the forms and logic for each step — registration, login, logout, and session handling — so there's no code to write.</p>

<p>The login page should be published at <code>/members/login.php</code>. CARL's access guard redirects unauthenticated visitors to that exact path when they attempt to reach a protected page. If you publish the login page at a different URL, update the redirect path accordingly.</p>

<h2>Open Registration</h2>

<p>With open registration enabled, a visitor fills out the registration form with their email, username, and password. CARL validates the input, checks for duplicate emails and usernames, creates the account with active status, sets the session cookie, and logs them in immediately. The whole process is a single form submission with no email verification step required.</p>

<h2>Approval-Required Registration</h2>

<p>With approval mode enabled, the registration process is the same from the visitor's side, but the account is created with pending status instead of active. The visitor sees a confirmation message telling them their account is awaiting approval. In your admin panel, go to Members and filter by the pending status to see registrations awaiting review. Click approve to activate the account, or delete it if you don't want to grant access.</p>

<h2>Managing Members After Setup</h2>

<p>Once registration is live, new members appear in the Members dashboard as they sign up. You can change any member's access level from free to premium at any time, suspend accounts without deleting them, and reinstate suspended accounts. If a member's email matches an existing subscriber in your email list, CARL links the two records automatically so you can see the connection between your members and your email audience.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-set-up-member-registration-in-carl-configure-open-or--1780439285.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Members Area Works | Built-In Membership Without a Plugin | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/members/how-carls-members-area-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/members/how-carls-members-area-works.php</guid>
    <pubDate>Tue, 02 Jun 2026 18:24:55 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-members-area-works-built-in-membership-without-a--1780439024.webp" alt="How CARL&#039;s Members Area Works | Built-In Membership Without a Plugin | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Members Area Works</h1>

<p>CARL includes a built-in membership system that lets you restrict pages to registered members. There's no plugin, no third-party service, and no separate membership platform to manage. Registration, login, session handling, and access control all run directly on your server as part of CARL.</p><p><img src="/images/how-carl-s-members-area-works-built-in-membership-without-a--1780439024.webp" alt="How CARL's Members Area Works" class="img-fluid" style=""><br></p>

<h2>Access Levels</h2>

<p>CARL offers two membership levels: free and premium. Free membership is open to anyone who registers. Premium membership is for paid or manually upgraded accounts. When you restrict a page, you choose which level is required to view it. A premium page is inaccessible to free members. A free-tier member's page is inaccessible to visitors who aren't logged in.</p>

<h2>How Sessions Work</h2>

<p>When a member logs in, CARL creates a session token and sets a secure, HTTP-only cookie in the browser with a 30-day expiry. The token is hashed before it's stored in the database, so the raw session value is never retained on the server. On each request to a protected page, CARL checks the cookie, validates the session against the database, confirms the member's account is active, and verifies their access level before allowing the page to load.</p>

<p>If the session has expired or the cookie is missing, the visitor is redirected to the login page, with the original URL included as a redirect parameter, so they land back on the correct page after logging in. If they're logged in but their access level is insufficient, they're redirected to an upgrade page instead.</p>

<h2>Registration Modes</h2>

<p>Registration can be set to open or approval-required in your Members settings. With open registration, new accounts become active immediately after signup. Once approval is required, new accounts remain pending until you approve them from the Members dashboard in the admin panel. The approval mode is useful when you want to control who gets access rather than letting anyone register freely.</p>

<h2>Password Requirements</h2>

<p>CARL enforces a minimum password standard on registration: at least 8 characters, one uppercase letter, and one number. Passwords are stored as bcrypt hashes. The plain-text password is never written to the database or any log file.</p>

<h2>The Members Dashboard</h2>

<p>In the CARL admin panel, the Members section shows a summary of total members broken down by status (active, pending, suspended) and access level. From there, you can approve pending registrations, suspend accounts, change a member's access level, and delete members. Each member record shows their email, username, registration date, last login, and current status.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-members-area-works-built-in-membership-without-a--1780439024.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Read Your Link Reports in CARL | Click Data, Referrers, and Campaign Performance | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/link-tracking/how-to-read-your-link-reports-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/link-tracking/how-to-read-your-link-reports-in-carl.php</guid>
    <pubDate>Tue, 02 Jun 2026 09:16:33 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-read-your-link-reports-in-carl-click-data-referrers-a-1780405584.webp" alt="How to Read Your Link Reports in CARL | Click Data, Referrers, and Campaign Performance | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Read Your Link Reports in CARL</h1>

<p>CARL's link reports show you exactly what's happening with every tracked link on your site: how many clicks each link received, where those clicks came from, which countries they originated in, and how performance breaks down across your link groups. Everything is filterable by date range, so you can isolate any period you need.</p><p><img src="/images/how-to-read-your-link-reports-in-carl-click-data-referrers-a-1780405584.webp" alt="How to Read Your Link Reports in CARL" class="img-fluid" style=""><br></p>

<h2>Accessing the Reports</h2>

<p>In the CARL admin panel, go to Link Tracker and click the Reports button in the top bar. The reports page opens with a default date range applied. You can adjust the from and to dates to cover any period, filter by a specific link or link group, and choose whether to include bot clicks in the totals. Bot clicks are excluded by default because they inflate the numbers without reflecting real visitor interest.</p>

<h2>The Summary Panel</h2>

<p>At the top of the reports page, a summary shows total clicks, human clicks, bot clicks, and unique IPs for the selected period and filters. Human clicks are the number you should focus on. Unique IPs give you a rough sense of how many distinct visitors clicked, since the same IP clicking multiple times counts once in that figure.</p>

<h2>Clicks Over Time</h2>

<p>Below the summary, a chart plots daily click totals across your selected date range. Gaps in the chart are genuine zero-click days, not missing data. Use this view to spot spikes after a new article goes live, a social post gets traction, or a campaign starts sending traffic. Flat lines between spikes tell you which links are passive earners versus which ones need fresh promotion.</p>

<h2>Per-Link Breakdown</h2>

<p>The per-link table ranks every link by human clicks for the selected period. Each row shows the link label, short code, redirect type, group, total clicks, human clicks, unique IPs, and the timestamp of the last click. Links with zero clicks during the period still appear in the table, making it easy to identify links that have gone cold and may need attention.</p>

<h2>Referrers and Countries</h2>

<p>The referrers breakdown shows the top sources sending clicks to your tracked links, so you can see whether traffic is coming from organic search, direct visits, social referrals, or specific pages on your own site. The countries breakdown shows where your clickers are located by country code. Both tables are limited to the top 20 results for the selected period.</p>

<h2>Exporting the Data</h2>

<p>The export function on the reports page downloads the full click log for the selected filters in CSV format. Each row contains the click timestamp, link label, short code, redirect type, group name, referrer, country code, user agent, and a bot flag. Use the export when you want to analyze the raw data in a spreadsheet or cross-reference click activity against your affiliate network's own reporting.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-read-your-link-reports-in-carl-click-data-referrers-a-1780405584.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Cloak Affiliate Links in CARL | Keep Your Domain in the Address Bar | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/link-tracking/how-to-cloak-affiliate-links-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/link-tracking/how-to-cloak-affiliate-links-in-carl.php</guid>
    <pubDate>Tue, 02 Jun 2026 09:02:48 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-cloak-affiliate-links-in-carl-keep-your-domain-in-the-1780405265.webp" alt="How to Cloak Affiliate Links in CARL | Keep Your Domain in the Address Bar | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Cloak Affiliate Links in CARL</h1>

<p>Link cloaking keeps your domain in the visitor's browser address bar when they click an affiliate link. Instead of the visitor seeing the affiliate network's URL, they stay on your domain throughout the visit. CARL offers two cloaking methods: iframe cloaking and JavaScript cloaking.</p><p><img src="/images/how-to-cloak-affiliate-links-in-carl-keep-your-domain-in-the-1780405265.webp" alt="How to Cloak Affiliate Links in CARL" class="img-fluid" style=""><br></p>

<h2>Why Cloak Affiliate Links</h2>

<p>Cloaked links look cleaner and more trustworthy to visitors. A URL like <code>yoursite.com/go.php?code=abc123</code> reads better than a raw affiliate URL with tracking IDs appended. It also protects your affiliate relationships: competitors can't identify which programs you're promoting just by hovering over your links. And because all your links run through your own domain, you keep full control over where they point at any time.</p>

<h2>Iframe Cloaking</h2>

<p>Iframe cloaking loads the destination page inside a full-screen, borderless iframe. Your domain stays in the address bar for the entire visit. From the visitor's perspective, they've arrived at the destination. From the browser's perspective, they're still on your site.</p>

<p>The iframe method is the default cloaking option in CARL and works well for most affiliate destinations. The one situation where it won't work is when the destination site sends an <code>X-Frame-Options: DENY</code> or <code>X-Frame-Options: SAMEORIGIN</code> header, which instructs browsers to refuse to load the page inside a frame. If you notice a cloaked link displaying a blank page, that's likely the cause. Switch that link to JavaScript cloaking instead.</p>

<h2>JavaScript Cloaking</h2>

<p>JavaScript cloaking serves a minimal, noindexed page that immediately bounces the visitor to the destination via a client-side redirect. The transition is nearly instant. Search engines see a noindex, nofollow page and don't follow the link, which keeps the affiliate URL out of Google's index. Visitors with JavaScript disabled see a plain fallback link instead.</p>

<p>JavaScript cloaking is the right choice for destinations that block iframes, or for any situation where you want the redirect to be clean and direct rather than frame-based.</p>

<h2>301 and 302 Redirects</h2>

<p>If you don't need cloaking, CARL also supports standard 301 and 302 redirects. A 301 tells search engines the destination is permanent and passes link equity. A 302 signals a temporary redirect and doesn't pass equity. Neither keeps your domain in the address bar. Use these when you want a clean, short URL that simply redirects rather than a cloaked link.</p>

<h2>Setting the Redirect Type</h2>

<p>When creating or editing a link in the Link Tracker, select the redirect type from the dropdown. You can change the type on any existing link at any time without affecting its short code or click history. If you're unsure which method to use, start with iframe cloaking and switch to JavaScript cloaking if the destination doesn't load correctly.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-cloak-affiliate-links-in-carl-keep-your-domain-in-the-1780405265.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Feedback System Works | Visitor Reactions on Every Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/analytics/how-carls-feedback-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/analytics/how-carls-feedback-system-works.php</guid>
    <pubDate>Tue, 02 Jun 2026 08:35:09 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-feedback-system-works-visitor-reactions-on-every--1780403613.webp" alt="How CARL&#039;s Feedback System Works | Visitor Reactions on Every Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Feedback System Works</h1>

<p>CARL's feedback system adds a reaction widget to your published pages. Visitors can respond to your content with one of 5 reactions: upvote, funny, love, angry, or sad. The counts are visible on the page in real time, and the results are collected in your admin panel so you can see how your content is landing.</p><p><img src="/images/how-carl-s-feedback-system-works-visitor-reactions-on-every--1780403613.webp" alt="How CARL's Feedback System Works" class="img-fluid" style=""><br></p>

<h2>How the Widget Works</h2>

<p>The feedback widget is included on pages via CARL's include file system. When a visitor clicks a reaction, the vote is submitted to the server and the count updates on the page immediately without a reload. If a visitor clicks the same reaction again, the vote is removed. If they click a different reaction, their vote changes to the new one. Each visitor can hold one active reaction per page at any time.</p>

<p>The system records which reaction each visitor left on each page, so when they return, their previous reaction is highlighted. The widget shows them where they stand without requiring an account or login.</p>

<h2>Spam Protection</h2>

<p>Votes are rate-limited per IP address in an hourly window. If the same IP submits an unusually high number of reactions within an hour, further votes from that address are blocked until the window resets. This keeps the counts meaningful without requiring captchas or friction for genuine visitors.</p>

<h2>The Feedback Dashboard</h2>

<p>In the CARL admin panel, go to Tools and then Feedback. The dashboard shows a summary of total votes across your site broken down by reaction type, a chart of vote activity over the last 30 days, and a ranked list of your most-reacted pages. Each page in the list shows its individual reaction breakdown, so you can see not just which pages got the most engagement but what kind of engagement they received.</p>

<p>From the dashboard, you can also reset votes for any individual page and export the full vote dataset. The export is useful if you want to analyze reaction data outside the admin panel or keep a record before clearing old data.</p>

<h2>Adding the Widget to Your Pages</h2>

<p>The feedback widget is automatically included in CARL's default templates. If you've built a custom template and want the widget on those pages, add the feedback include call to your template file. Any page using a template that includes the widget will display reactions. Pages on templates without it won't, which gives you control over where feedback appears without needing to configure it page by page.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-feedback-system-works-visitor-reactions-on-every--1780403613.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Built-In Analytics Work | Visitor Tracking Without Third-Party Services | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/analytics/how-carls-built-in-analytics-work.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/analytics/how-carls-built-in-analytics-work.php</guid>
    <pubDate>Tue, 02 Jun 2026 08:31:34 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-built-in-analytics-work-visitor-tracking-without--1780403413.webp" alt="How CARL&#039;s Built-In Analytics Work | Visitor Tracking Without Third-Party Services | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Built-In Analytics Work</h1>

<p>CARL includes a built-in analytics system that tracks visitor activity directly on your server. No third-party service required, no data leaving your hosting account, no cookie consent banner forced on you by a tracking platform you don't control. The data is yours, and it stays on your server.</p><p><img src="/images/how-carl-s-built-in-analytics-work-visitor-tracking-without--1780403413.webp" alt="How CARL's Built-In Analytics Work" class="img-fluid" style=""><br></p>

<h2>How Tracking Works</h2>

<p>When a visitor loads a CARL page, a lightweight JavaScript beacon fires in the background and sends pageview data to a tracking endpoint on your server. The beacon captures the page URL, page title, referrer, and any UTM parameters present in the URL. A second beacon fires when the visitor leaves the page to record time on page. Both happen silently without affecting page load performance.</p>

<p>Bots are filtered automatically. CARL parses the visitor's user agent on every request and discards any hits identified as crawlers, spiders, or monitoring bots. Your view counts reflect real human visitors, not Google's crawl activity inflating your numbers.</p>

<h2>What Gets Recorded</h2>

<p>Each pageview record includes the URL, page title, referrer URL, referrer domain, and referrer type (organic search, direct, social, referral, or email). UTM parameters are captured and stored individually when present, so campaign traffic is clearly broken out. Device type, browser, operating system, and approximate geographic location are recorded for each visit.</p>

<p>Sessions are tracked using a short-lived cookie with a 30-minute inactivity window. A separate visitor cookie with a 30-day window identifies returning visitors. IP addresses are hashed before storage and never saved in plain text, so the system gives you meaningful audience data without retaining personally identifiable information.</p>

<h2>The Analytics Dashboard</h2>

<p>In the CARL admin panel, the Analytics dashboard shows an overview of pageviews, unique visitors, sessions, new visitor count, average time on page, and bounce rate for the selected period. Period filters cover the last 1, 7, 30, 90, or 365 days, with a custom date range option for anything in between. A trend indicator compares the current period against the previous equivalent period so you can see at a glance whether traffic is moving up or down.</p>

<p>Beneath the overview cards, the dashboard breaks down traffic by top pages, referrer sources, countries, devices, and browsers. Each breakdown lets you see where your traffic is coming from and which content is performing. The data updates in real time as new visits come in.</p>

<h2>What It Doesn't Do</h2>

<p>CARL's built-in analytics covers traffic patterns and content performance. It doesn't replace a full analytics platform if you need conversion funnels, goal tracking, or event-level reporting. For those use cases, adding GA4 through your <code>header_scripts.txt</code> include file runs both systems in parallel without any conflict. See <a href="/wiki/analytics/how-carl-integrates-with-google-analytics.php">How CARL integrates with Google Analytics</a> for the setup steps.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-built-in-analytics-work-visitor-tracking-without--1780403413.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Integrates With Google Analytics | Add GA4 to Every Page Through One Include File | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/analytics/how-carl-integrates-with-google-analytics.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/analytics/how-carl-integrates-with-google-analytics.php</guid>
    <pubDate>Tue, 02 Jun 2026 08:02:53 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-integrates-with-google-analytics-add-ga4-to-every-p-1780401265.webp" alt="How CARL Integrates With Google Analytics | Add GA4 to Every Page Through One Include File | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Integrates With Google Analytics</h1>

<p>CARL doesn't have a dedicated Google Analytics settings field. It doesn't need one. Because every page CARL generates includes your site-wide header scripts automatically, adding GA4 to your entire site takes about 30 seconds: paste your tracking snippet into one include file, and every page you've published picks it up.</p><p><img src="/images/how-carl-integrates-with-google-analytics-add-ga4-to-every-p-1780401265.webp" alt="How CARL Integrates With Google Analytics" class="img-fluid" style=""><br></p>

<h2>How It Works</h2>

<p>Every CARL page template includes a file called <code>header_scripts.txt</code> from your <code>site_includes</code> folder. Anything in that file gets injected into the <code></code> section of every published page on your site. This is the same mechanism used for custom fonts, custom CSS links, and any other site-wide head code. Google Analytics is just one more thing that belongs there.</p>

<h2>Adding Your GA4 Tag</h2>

<p>In your Google Analytics account, go to Admin, then Data Streams, select your web stream, and copy the Google tag snippet. In the CARL admin panel, go to Includes and open <code>header_scripts.txt</code>. Paste the snippet in and save. That's the entire process.</p>

<p>Because CARL pages are static PHP files, the tag is already embedded when a visitor loads each page. There's no CMS runtime assembling the head section on request. The tag is written into every generated file at publish time, so it fires reliably on every page load without relying on any JavaScript framework or plugin being active.</p>

<h2>Regenerating After Adding the Tag</h2>

<p>Updating <code>header_scripts.txt</code> doesn't automatically update pages that are already published on disk. To push the new tag to all existing pages, run a <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> after saving the include file. New pages published after that point will automatically include the tag, since they're generated fresh from the current include files.</p>

<h2>Verifying It's Working</h2>

<p>After regenerating, open any published page and view its source. Search for your GA measurement ID (it looks like <code>G-XXXXXXXXXX</code>) and confirm it appears in the head section. In Google Analytics, the Realtime report should show activity within a minute of you visiting the page. If you don't see the tag in the source, check that <code>header_scripts.txt</code> was saved correctly and that the bulk regenerate completed without errors.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-integrates-with-google-analytics-add-ga4-to-every-p-1780401265.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Change Your CARL Admin Password | Update or Reset Your Login Credentials | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/site-management/how-to-change-your-carl-admin-password.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/site-management/how-to-change-your-carl-admin-password.php</guid>
    <pubDate>Tue, 02 Jun 2026 06:37:21 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-change-your-carl-admin-password-update-or-reset-your--1780396566.webp" alt="How to Change Your CARL Admin Password | Update or Reset Your Login Credentials | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Change Your CARL Admin Password</h1>

<p>CARL gives you two ways to update your admin password: through the Settings panel when you're logged in, and through the password recovery flow when you're not. Both methods update the same bcrypt-hashed password record in the database.</p><p><img src="/images/how-to-change-your-carl-admin-password-update-or-reset-your--1780396566.webp" alt="How to Change Your CARL Admin Password" class="img-fluid" style=""><br></p>

<h2>Changing Your Password While Logged In</h2>

<p>In the CARL admin panel, go to Settings and find the password change section. Enter your new password, confirm it, and save. CARL hashes the new password using bcrypt and updates the admin account record immediately. Your current session stays active after the change, so you won't be logged out. The new password takes effect on the next login.</p>

<h2>Resetting Your Password When Locked Out</h2>

<p>If you can't log in, CARL's password recovery flow uses a recovery key instead of email. The recovery key is a value you set in Settings under General while you still have access. If you haven't set one yet, <i><b>do it now before you need it</b></i>.</p>

<p>To reset your password, go to <code>admin/forgot-password.php</code> on your site. The process runs in two steps. First, enter your recovery key. CARL verifies it against the stored value using a timing-safe comparison, so there's no way to probe for valid keys through response timing. If the key matches, you're moved to the second step. Enter your new password, confirm it, and submit. CARL updates the password and redirects you to the login page.</p>

<h2>Password Requirements</h2>

<p>Passwords must be at least 8 characters. There are no complexity requirements beyond that, but a longer passphrase is more secure than a short one with mixed characters. CARL stores passwords as bcrypt hashes. The plain-text password is never stored anywhere on the server.</p>

<h2>If You've Lost Your Recovery Key</h2>

<p>If you're locked out and have no recovery key set, you'll need to update the password directly in the database via phpMyAdmin. Generate a bcrypt hash of your new password using an online bcrypt tool or a quick PHP script, then update the <code>password</code> column in the <code>users</code> table with the hash. Log in with the new password, then immediately set a recovery key in Settings so you're not in this position again.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-change-your-carl-admin-password-update-or-reset-your--1780396566.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Admin Authentication | Secure Login With No Public Attack Surface | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/site-management/how-carl-handles-admin-authentication.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/site-management/how-carl-handles-admin-authentication.php</guid>
    <pubDate>Tue, 02 Jun 2026 06:27:52 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-admin-authentication-secure-login-with-no-p-1780395923.webp" alt="How CARL Handles Admin Authentication | Secure Login With No Public Attack Surface | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Admin Authentication</h1>

<p>CARL's admin panel is protected by a straightforward session-based login system. Username and password are verified against a single admin account stored in the database, and access is blocked at the PHP level on every admin page until a valid session exists.</p><p><img src="/images/how-carl-handles-admin-authentication-secure-login-with-no-p-1780395923.webp" alt="How CARL Handles Admin Authentication" class="img-fluid" style=""><br></p>

<h2>The Login Process</h2>

<p>When you submit your credentials at <code>admin/login.php</code>, CARL queries the database for a matching username and verifies the submitted password against the stored bcrypt hash using PHP's <code>password_verify()</code>. If verification passes, PHP regenerates the session ID before writing the authenticated session flag. This prevents session fixation attacks, where an attacker who obtained a pre-login session ID could try to use it after authentication.</p>

<p>On every successful login, CARL also silently refreshes your license token against the licensing server. If the license has been revoked (trial expired), the session is destroyed immediately and login is blocked. If the licensing server is unreachable, the grace period stored in the local auth file covers continued access so a temporary network issue doesn't lock you out of your own site.</p>

<h2>Session Protection on Every Admin Page</h2>

<p>Every admin page calls <code>requireLogin()</code> at the top of the file. If no valid authenticated session exists, the visitor is redirected to the login page immediately, before any admin content is rendered or any admin action is executed. There is no admin page that can be accessed without a valid session, and there is no way to bypass this check by manipulating the URL.</p>

<h2>CSRF Protection</h2>

<p>Every form in the CARL admin includes a CSRF token, a 64-character random hex string generated by PHP's <code>random_bytes()</code> and stored in the session. When a form is submitted, CARL verifies the submitted token against the session token using <code>hash_equals()</code>, which prevents timing attacks. Any POST request that arrives without a valid matching token is rejected with a 403 before any action is taken. AJAX requests can pass the token via the <code>X-CSRF-Token</code> header instead of a form field.</p>

<h2>The Single Admin Account</h2>

<p>CARL operates with one admin account. There's no user roles system, no multi-admin setup, and no permission levels to manage. The admin account username and password are set during installation and can be changed at any time from inside the admin panel. For the password change process, see <a href="/wiki/site-management/how-to-change-your-carl-admin-password.php">How to change your CARL admin password</a>.</p>

<h2>No Public Login Page Exposure</h2>

<p>Unlike WordPress, CARL has no well-known login URL that automated scanners can target. The admin directory lives at <code>/admin/</code> on your server, which you can rename or restrict via <code>.htaccess</code> if you want an additional layer of obscurity. The login page has no username enumeration vulnerability: the same error message is returned whether the username doesn't exist or the password is wrong, so there's no way to confirm valid usernames through trial submissions.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-admin-authentication-secure-login-with-no-p-1780395923.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>What CARL's Database Actually Contains | Admin Data Only, Never Involved in Page Delivery | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/what-carls-database-actually-contains.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/what-carls-database-actually-contains.php</guid>
    <pubDate>Tue, 02 Jun 2026 06:12:42 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/what-carl-s-database-actually-contains-admin-data-only-never-1780318042.webp" alt="What CARL&#039;s Database Actually Contains | Admin Data Only, Never Involved in Page Delivery | Carl Riedel" style="max-width:100%;height:auto;">
<h1>What CARL's Database Actually Contains</h1>
<p>If you've used WordPress, you're used to a CMS where the database is involved in everything. Every page load, every menu render, every widget and all of it hits the database in real time. CARL's database plays a completely different role. It's an admin tool, not a delivery mechanism. Understanding that distinction changes how you think about the whole system.</p><p><img src="/images/what-carl-s-database-actually-contains-admin-data-only-never-1780318042.webp" alt="What CARL's Database Actually Contains" class="img-fluid" style=""><br></p>
<h2>The Database Is for the Admin Panel Only</h2>
<p>CARL's database is queried in one context: when you're logged into the admin panel. When you create a page, edit a page, change a setting, or manage a product, that data lives in the database. The admin panel reads from it and writes to it constantly.</p>
<p>When a visitor lands on one of your published pages, the database is not involved at all. The page was already <a href="/wiki/architecture/what-happens-when-you-publish-a-page.php">built and written to disk</a> when you published it. The visitor gets a pre-built file. No database connection, no query, no result set to render. The database sits completely idle during every live page request.</p>
<h2>What's Actually Stored in the Database</h2>
<p>CARL's database contains your page records (titles, slugs, content, meta fields, template assignments, status), your site settings, your module data (subscribers, members, products, purchases, links, popups, CTAs), and your admin user account. It's the working data that powers the admin panel.</p>
<p>None of that is exposed to the public. The database credentials live in <code>admin/config.php</code>, which sits inside the admin directory. A visitor browsing your site has no path to the database and no reason to look for one. The published pages contain no database calls.</p>
<h2>MariaDB 11.4 on Standard Shared Hosting</h2>
<p>CARL runs on MariaDB 11.4, the default database engine for most modern cPanel shared hosting accounts. You don't need a managed database service, a separate database server, or any special configuration. If your host provides cPanel with MySQL or MariaDB support, the database side of CARL is already covered.</p>
<p>The database is created during installation through the setup wizard, and all tables are managed by CARL's migration system. You never need to write SQL by hand or manage the schema manually. When a new version of CARL introduces new tables or columns, the migration runner handles the update automatically.</p>
<h2>The Tables and What They Do</h2>
<p>CARL's database schema is straightforward. The <code>pages</code> table holds your content and page metadata. The <code>settings</code> table holds every key-value pair from the Settings panel. The <code>users</code> table holds admin accounts. Module tables handle their own data: <code>subscribers</code> for email signups, <code>members</code> and <code>member_sessions</code> for the members' area, <code>downloads_products</code> and <code>download_tokens</code> and <code>download_purchases</code> for the Downloads module, <code>links</code> and <code>link_groups</code> for the affiliate tracker, <code>popups</code> and <code>ctas</code> for the content tools.</p>
<p>Every table has a single, clear job. There's no tangled dependency chain, no options table storing serialized PHP objects, no post meta sprawl. If you open the database in phpMyAdmin, you can read exactly what's in it without needing a decoder ring.</p>
<h2>What Happens If the Database Goes Down</h2>
<p>This is where the architecture pays off in a way that surprises most people coming from WordPress. If your database becomes unavailable for any reason (a hosting issue, a maintenance window, anything) your published pages keep working. Visitors keep reading your content. Google keeps crawling your site.</p>
<p>The only thing that stops working is the admin panel, because that's the only part of CARL that actually needs the database. The public-facing site is completely unaffected. That's a resilience level that a database-driven CMS simply cannot match, regardless of how much caching you throw at it.</p>
<h2>Backups Are Simple</h2>
<p>Because the database contains only admin and module data, not the live site itself, backing it up is a contained, straightforward task. <b>CARL's built-in backup system handles this for you</b>, but a manual export via phpMyAdmin is also possible. The published pages are already on disk in <code>public_html</code> and can be backed up independently via cPanel's file backup tools.</p>
<p>You end up with two clean backup targets: the database for your admin data and the file system for your published pages. No overlap, no confusion about what's in which backup, and no single point of failure that takes everything down at once.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/what-carl-s-database-actually-contains-admin-data-only-never-1780318042.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Scheduler Works | Publish Pages Automatically at Any Date and Time | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/site-management/how-carls-scheduler-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/site-management/how-carls-scheduler-works.php</guid>
    <pubDate>Tue, 02 Jun 2026 06:02:47 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-scheduler-works-publish-pages-automatically-at-an-1780394491.webp" alt="How CARL&#039;s Scheduler Works | Publish Pages Automatically at Any Date and Time | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Scheduler Works</h1>

<p>CARL's scheduler lets you set any page to go live at a specific date and time. Set the schedule, save the page, and CARL handles the rest. The page file is generated and written to disk automatically when the scheduled time arrives, with no manual action required on your part.</p><p><img src="/images/how-carl-s-scheduler-works-publish-pages-automatically-at-an-1780394491.webp" alt="How CARL's Scheduler Works" class="img-fluid" style=""><br></p>

<h2>How Scheduling Is Triggered</h2>

<p>CARL checks for due scheduled pages on every admin panel page load. When you open any page in the admin, CARL silently queries the database for pages whose scheduled time is at or before the current UTC time and publishes any it finds. This means scheduling works reliably on any shared hosting account without requiring a cron job, though a cron job can be added for more precise timing on high-volume sites.</p>

<p>For installations where consistent timing matters regardless of admin activity, CARL includes a dedicated cron endpoint at <code>admin/modules/scheduler/run.php</code>. This can be called via cPanel's Cron Jobs tool on a schedule you define, or triggered via HTTP using a scheduler key set in Settings. Either method works. The admin-trigger approach is sufficient for most sites.</p>

<h2>Timezone Handling</h2>

<p>CARL converts the date and time you enter from your configured admin timezone to UTC for storage. This means the page publishes at the correct moment regardless of where your server is located. Your admin timezone is set in Settings under General. For a walkthrough of setting a schedule on a specific page, see <a href="/wiki/page-management/how-to-schedule-a-page-for-future-publishing.php">How to schedule a page for future publishing</a>.</p>

<h2>What Happens at Publish Time</h2>

<p>When the scheduler finds a page that's due, it runs the same generation process as a manual publish. CARL builds the complete PHP file from the page record, writes it to disk at the correct path, and updates the page status to published. The scheduled time field is cleared from the database record. After each successful publish, CARL pings Google with your sitemap URL so the new page is picked up for indexing as quickly as possible.</p>

<p>If a page fails to generate, the error is logged and the page remains in scheduled status rather than being silently dropped. On the next scheduler run, CARL will attempt it again. Any generation errors are written to your server's PHP error log for diagnosis.</p>

<h2>Preventing Overlapping Runs</h2>

<p>CARL uses a database lock to prevent two scheduler runs from executing simultaneously. If a cron job and an admin-triggered check happen to fire at the same moment, the second run detects the lock and exits immediately. The lock is released as soon as the first run completes, so there's no risk of it blocking future runs if the process finishes normally.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-scheduler-works-publish-pages-automatically-at-an-1780394491.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Restore a CARL Backup | Recover Your Site Files and Database in One Step | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/site-management/how-to-restore-a-carl-backup.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/site-management/how-to-restore-a-carl-backup.php</guid>
    <pubDate>Tue, 02 Jun 2026 05:47:38 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-restore-a-carl-backup-recover-your-site-files-and-dat-1780393273.webp" alt="How to Restore a CARL Backup | Recover Your Site Files and Database in One Step | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Restore a CARL Backup</h1>

<p>Restoring a CARL backup overwrites your current site files and database with the contents of the selected backup file. The entire process runs from inside the admin panel. You don't need shell access, FTP, or phpMyAdmin to complete a restore.</p><p><img src="/images/how-to-restore-a-carl-backup-recover-your-site-files-and-dat-1780393273.webp" alt="How to Restore a CARL Backup" class="img-fluid" style=""><br></p>

<h2>Before You Restore</h2>

<p>A restore is irreversible. Whatever is on your server right now, including any pages published, settings changed, or purchases recorded since the backup was taken, will be replaced. If there's any chance you need that data, create a fresh backup first before triggering the restore.</p><p>The Backup panel lets you create a new backup and restore an older one in the same session.</p>

<h2>How to Trigger a Restore</h2>

<p>In the CARL admin panel, go to Backup. The Backup History panel lists every available backup with its filename, size, and creation timestamp. Find the backup you want to restore and click the Restore button next to it. A confirmation modal appears showing the exact filename and creation date, along with a warning that the action cannot be undone. Click Restore Now to proceed.</p>

<p>The restore button disables itself once clicked and shows a progress state. Do not close the tab while the restore is running. On a large site with many files, the process may take a minute or two to complete.</p><p><img src="/images/backup-restore.webp" alt="How to Trigger a Restore" class="img-fluid" style=""><br></p>

<h2>What Happens During a Restore</h2>

<p>CARL opens the backup ZIP and extracts it to a temporary directory on the server. If the backup contains a database export, CARL imports it using a direct PDO connection, running each SQL statement in sequence with foreign key checks temporarily disabled.</p><p>If the backup contains <code>secrets.php</code>, it is restored to its correct location above <code>public_html</code> and its file permissions are set to 0600. If the backup contains site files, they are copied back into <code>public_html</code>, recreating the original directory structure.</p>

<p>Once the restore is complete, the temporary directory is deleted. The confirmation message on the Backup page tells you exactly which components were restored: database, site files, and <code>secrets.php</code>.</p><p>If any SQL statements produced warnings during the import, those are shown as well so you can assess whether anything needs attention.</p>

<h2>After the Restore</h2>

<p>Your site should be fully operational immediately after the restore completes. Published pages are static PHP files that were written back to disk as part of the file restore, so visitors see the restored content without any additional steps. The admin panel reconnects to the restored database automatically on the next page load.</p>

<p>If the backup was taken some time ago, run a <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health check</a> after restoring to confirm the state of your pages and catch any issues introduced by the rollback. Any pages published after the backup date will be gone, so check your page list and republish anything that's missing.</p>

<h2>Restoring to a Different Server</h2>

<p>CARL backups are self-contained. If you're migrating to a new host rather than recovering from an incident, download the backup ZIP from the Backup panel to your local machine, complete the fresh CARL installation on the new server, then use the Backup panel on the new install to restore from the downloaded file. The database credentials in <code>secrets.php</code> will need updating to match the new server's database settings after the restore.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-restore-a-carl-backup-recover-your-site-files-and-dat-1780393273.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Backup System Works | Full Site Backup Above the Web Root | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/site-management/how-carls-backup-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/site-management/how-carls-backup-system-works.php</guid>
    <pubDate>Tue, 02 Jun 2026 05:38:59 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-backup-system-works-full-site-backup-above-the-we-1780392231.webp" alt="How CARL&#039;s Backup System Works | Full Site Backup Above the Web Root | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Backup System Works</h1>

<p>CARL's backup system creates a single ZIP file containing your site files, your database, and your <code>secrets.php</code> configuration. Everything your site needs to run is in one file, stored in a location that no browser can reach.</p><p><img src="/images/how-carl-s-backup-system-works-full-site-backup-above-the-we-1780392231.webp" alt="How CARL's Backup System Works" class="img-fluid" style=""><br></p>

<h2>Where Backups Are Stored</h2>

<p>Backups go above your web root, in a folder called <code>carl-backups</code> sitting one level above <code>public_html</code>. On a standard cPanel account, that means the folder is at <code>/home/yourusername/carl-backups/</code>. A visitor browsing your site has no URL they could use to access it. Downloads are served exclusively through the admin panel, with login required.</p>

<p>This is a deliberate architectural decision. Storing backups inside <code>public_html</code> would make them publicly accessible by URL, which is a serious security risk on any site that handles member data, purchase records, or private configuration. CARL keeps them out of the web root by default, with no configuration required on your part.</p>

<h2>What Gets Included</h2>

<p>When you create a backup, you can choose to include site files, the database, or both. <code>secrets.php</code> is always included regardless of what you select, because the site cannot connect to the database without it. A backup that contains the database but not <code>secrets.php</code> would be unusable during a restore.</p>

<p>The site files option packages everything in <code>public_html</code> into the ZIP under a <code>public_html/</code> path prefix, excluding the backup directory itself. The database option exports every table and all data using CARL's pure-PHP export function. There's no dependency on <code>mysqldump</code>, which means it works on every shared hosting account regardless of whether shell access is available.</p>

<h2>The Backup File</h2>

<p>Each backup is named <code>carl-backup-YYYYMMDD-HHmmss.zip</code>, with the timestamp embedded in the filename so you always know exactly when it was created. The backup screen shows the estimated size of your site files and database before you run the backup, alongside the available free disk space. If space is tight, a warning appears before you proceed.</p>

<p>Large sites may take a minute or two to package. CARL extends PHP's execution time limit for the duration of the backup and keeps the process running even if the browser tab goes idle. You don't need to keep the tab in focus, but you should leave it open until the confirmation message appears.</p>

<h2>Managing Your Backup History</h2>

<p>The Backup History panel lists every existing backup with its filename, size, and creation timestamp in UTC. From there you can download any backup directly to your computer, trigger a restore, or delete backups you no longer need. Backups flagged as over 30 days old are highlighted so you can decide whether to keep or remove them.</p>

<p>Deleting old backups regularly is good practice. Each full-site backup includes your entire <code>public_html</code> directory, so on a large site they accumulate disk usage quickly. Keep at least 2 recent backups on the server at any time and download a copy to your local machine or cloud storage for offsite redundancy.</p>

<h2>Running a Backup</h2>

<p>In the CARL admin panel, navigate to Backup. Select whether to include site files, the database, or both, then click Create Backup Now. The button disables itself during the process to prevent double submissions. When the backup completes, the new file appears immediately in the Backup History panel with its size confirmed. For how to use a backup to restore your site, see <a href="/wiki/site-management/how-to-restore-a-carl-backup.php">How to restore a CARL backup</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-backup-system-works-full-site-backup-above-the-we-1780392231.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Set Up CARL's Site Search | Add Search to Any Page in Two Minutes | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-set-up-carls-site-search.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-set-up-carls-site-search.php</guid>
    <pubDate>Mon, 01 Jun 2026 20:54:13 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-set-up-carl-s-site-search-add-search-to-any-page-in-t-1780361581.webp" alt="How to Set Up CARL&#039;s Site Search | Add Search to Any Page in Two Minutes | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Set Up CARL's Site Search</h1>

<p>CARL's site search is ready to use the moment you install it. The search bar widget is already included in the Bootstrap Blog sidebar template, so if you're using that template for your articles, search is already working on those pages. For any other template or page type, adding search takes about two minutes.</p><p><img src="/images/how-to-set-up-carl-s-site-search-add-search-to-any-page-in-t-1780361581.webp" alt="How to Set Up CARL's Site Search" class="img-fluid" style=""><br></p>

<h2>Search on the Bootstrap Blog Template</h2>

<p>The Bootstrap Blog template loads <code>search_bar.txt</code> automatically as part of its sidebar. Every page using this template has a working search bar in the sidebar, with no additional setup. If you've published articles using the Bootstrap Blog template and visit one of those pages, the search bar is already there. Type a query, and it works.</p>

<h2>Adding Search to a Specific Page</h2>

<p>For pages using a different template, or for pages where you want to search in a non-sidebar position, add it through the PHP Snippet field. Open the page in the CARL editor and scroll to the PHP Snippet field. Click the Include Picker, find <code>search_bar.txt</code> in the list, and click Insert. CARL adds the include line to the snippet field. Click Save Draft, then Save and Generate File. The search bar is now on that page at the position where the snippet renders.</p>

<p>The PHP Snippet renders at the <code>{{SNIPPET}}</code> token position in your template. If you want the search appearing in a specific part of the layout, check where that token is placed in the template file and adjust accordingly.</p>

<h2>Adding Search to a Custom Template</h2>

<p>If you've built a <a href="/wiki/page-management/how-to-build-a-custom-template.php">custom template</a> and want search available on every page using it, add the include directly to the template file rather than adding it to each page individually. Open the template file via Include Files in the admin, add the following line at the position where you want the search bar to appear:</p>

<code><!--?php include $_SERVER['DOCUMENT_ROOT'] . '/admin/site_includes/search_bar.txt'; ?--></code>

<p>Save the template and run <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regeneration</a>. Every page using that template will have the search bar on the next regeneration pass.</p>

<h2>Verifying Search Is Working</h2>

<p>After adding the search bar and regenerating the page, visit the live URL and enter a search query. Results should appear immediately. If the search bar renders but returns no results for queries you'd expect to find content for, confirm those pages are published and have been generated. CARL's search indexes published pages: draft pages and scheduled pages don't appear in search results until they're live.</p>

<h2>Monitoring Search Queries</h2>

<p>Once the search is live on your site, go to Tools, then Search in the CARL admin to review the queries visitors are entering. Check this dashboard periodically: repeated searches for topics you haven't covered are some of the most reliable content signals you'll find. Your visitors are telling you exactly what they want to read. For a full overview of what the search dashboard shows and how the search system works under the hood, see <a href="/wiki/installation/how-carls-site-search-works.php">How CARL's Site Search works</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-set-up-carl-s-site-search-add-search-to-any-page-in-t-1780361581.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Use CARL's AI Schema Generator | One Click for a Complete SEO Head Section | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-use-carls-ai-schema-generator.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-use-carls-ai-schema-generator.php</guid>
    <pubDate>Mon, 01 Jun 2026 20:44:12 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-use-carl-s-ai-schema-generator-one-click-for-a-comple-1780360962.webp" alt="How to Use CARL&#039;s AI Schema Generator | One Click for a Complete SEO Head Section | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Use CARL's AI Schema Generator</h1>

<p>The AI Schema Generator is one of CARL's most powerful features. Click one button, and Claude reads your page content, writes an optimized meta description, generates a complete Article JSON-LD schema with Thing entities, produces Open Graph tags, Twitter Card tags, a keywords tag, and a canonical URL tag, then delivers the entire block directly into the Head Injection field. One click covers everything that belongs in your page's,</p><p><img src="/images/how-to-use-carl-s-ai-schema-generator-one-click-for-a-comple-1780360962.webp" alt="How to Use CARL's AI Schema Generator" class="img-fluid" style=""><br></p>

<h2>Setting Up Your Claude API Key</h2>

<p>The AI Schema Generator runs on Claude, Anthropic's AI. To use it, you need to add a Claude API key to CARL's settings. Go to console.anthropic.com, create a free Anthropic account if you don't have one, navigate to API Keys, and click Create Key. Name it something recognizable like "CARL" and copy the key. In CARL, go to Settings, click the AI Settings tab, paste the key into the Claude API Key field, and click Save All Settings.</p>

<p>The API key starts with <code>sk-ant-</code>. You're billed directly by Anthropic for usage, not through CARL. The cost is a fraction of a cent per page. Most users spend under $1 per month on API costs, even running active content sites.</p>

<h2>Before You Click Generate Schema</h2>

<p>The Generate Schema button works best when your page is as complete as possible before you run it. Write your full page content first. Set your OG Image using the OG/TW button in the Quick Image Insert panel: Claude includes the image URL in the Open Graph and Twitter Card tags it generates. Fill in your OG Description, a one to two-sentence summary of the page for social sharing. Add any SameAs URLs in the SameAs field, one per line: your social profiles, relevant Wikipedia pages, or other authority sources that relate to the page topic. The more context Claude has, the better the output.</p>

<h2>Running Generate Schema</h2>

<p>Once your content is written and the OG fields are filled in, click the Generate Schema button. CARL sends your full page content, title, OG image URL, and SameAs URLs to Claude. Claude generates the complete head block, and CARL appends it automatically to the Head Injection field.</p><p>A green success message confirms the transfer. The whole process takes a few seconds.</p>

<p>After the schema lands in Head Injection, the Override Auto-Schema checkbox ticks itself automatically. Leave it ticked.</p><p>This suppresses CARL's built-in schema generation, so there's no conflict between the AI-generated block and any default schema CARL would otherwise output. This is the correct state, and you don't need to change it.</p>

<h2>After Generate Schema Runs</h2>

<p>Click Save Draft immediately after the schema is generated. The Head Injection content is part of the page record and needs to be saved before you publish or navigate away. If you close the editor without saving, the generated schema is lost, and you'll need to run it again.</p>

<p>Once saved, click Save and Generate File to publish the page. The complete head block, including the meta description, schema, Open Graph tags, Twitter Cards, keywords, and canonical tag, gets baked into the published PHP file. Every visitor and every crawler gets the full structured data package on every page request, served from a static file with no runtime cost.</p>

<h2>Schema Presets for Different Page Types</h2>

<p>The default Generate Schema instructions produce the Article schema, which is correct for most content pages. For pages that need a different schema type, CARL's Schema Preset system lets you create named sets of instructions for different content types.</p><p>An FAQ page preset that generates FAQPage schema, a video page preset that generates VideoObject schema, a product review preset that generates Review schema: you create these in Settings under AI Settings, then select the relevant preset from the dropdown in the page editor before clicking Generate Schema.</p>

<p>For pages that need schema types the AI generator doesn't cover, write the JSON-LD manually and paste it into the Head Injection field. The Head Injection field accepts any valid HTML or script content and writes it verbatim into the published file's <code></code> section.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-use-carl-s-ai-schema-generator-one-click-for-a-comple-1780360962.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Configure Organization Settings in CARL | Set Up Your Brand's Knowledge Graph Data | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-configure-organization-settings.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-configure-organization-settings.php</guid>
    <pubDate>Mon, 01 Jun 2026 20:38:14 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-configure-organization-settings-in-carl-set-up-your-b-1780360620.webp" alt="How to Configure Organization Settings in CARL | Set Up Your Brand&#039;s Knowledge Graph Data | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Configure Organization Settings in CARL</h1>

<p>The Organization tab in CARL's Settings panel powers your site's Organization JSON-LD schema. This is the structured data Google uses to understand your brand: who you are, what you do, where you're based, and how your online presence connects together. Fill it in completely during initial setup. It takes five minutes, and the SEO benefit compounds across every page on your site.</p><p><img src="/images/how-to-configure-organization-settings-in-carl-set-up-your-b-1780360620.webp" alt="How to Configure Organization Settings in CARL" class="img-fluid" style=""><br></p>

<h2>What Organization Schema Does</h2>

<p>When Google crawls your site, it reads the Organization schema and uses it to build a knowledge graph entry for your brand. This is what drives the branded search results panel that appears when someone searches for your business by name, the logo that sometimes appears in search results, and the data connections that establish your site as an authoritative source rather than an anonymous collection of pages. It's also what Google uses to verify that the author of your content is a real, identifiable entity.</p>

<h2>The Organization Fields</h2>

<p>Organization Name is your business or brand name exactly as you want it to appear in search results. Alternate Name is a shorter version or common abbreviation if one exists: "C.A.R.L." alongside "Content Automation Ranking Launchpad", for example. Description is one to two sentences describing what your site or business does. Write it clearly and factually: this is data for Google, not a tagline.</p>

<p>The Email field is your public contact email address. Logo URL is the full URL to your logo image, for example <code>https://yoursite.com/images/logo.png</code>. Upload your logo to CARL's Image Manager first, copy the URL, and paste it here. Google uses this URL to display your logo in branded search results, so make sure the image is at a stable, permanent URL that won't change.</p>

<h2>Founder Information</h2>

<p>The Founder Name field is your name or the name of the business founder. The Founder LinkedIn URL links the Organization schema to a verified professional profile, which strengthens the entity connection Google can establish between your brand and a real person. If you have a LinkedIn profile, add it. If you don't, you can leave the field blank, but a linked profile adds credibility to the schema.</p>

<h2>Address Fields</h2>

<p>Fill in your business address: street, city, postcode, and country. The country field takes a two-letter ISO code: US for the United States, GB for the United Kingdom, AU for Australia, NL for the Netherlands, and so on. If you operate as a solo online business without a formal street address, use the city and country where you're based. Google doesn't require a full street address for every business type, but having city and country in place is better than leaving the fields empty.</p>

<h2>Saving and Propagating</h2>

<p>Click Save All Settings when all fields are filled in. The Organization schema is written into every page CARL generates from this point forward. For pages already published before you completed the Organization settings, run <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> to push the updated schema across your entire site in one pass. Every page that goes through a regeneration will carry the complete Organization data in its head section.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-configure-organization-settings-in-carl-set-up-your-b-1780360620.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>What Hosting Does CARL Require | Standard cPanel Shared Hosting Is All You Need | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/what-hosting-does-carl-require.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/what-hosting-does-carl-require.php</guid>
    <pubDate>Mon, 01 Jun 2026 20:24:14 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/what-hosting-does-carl-require-standard-cpanel-shared-hostin-1780359697.webp" alt="What Hosting Does CARL Require | Standard cPanel Shared Hosting Is All You Need | Carl Riedel" style="max-width:100%;height:auto;">
<h1>What Hosting Does CARL Require</h1>

<p>CARL is built for standard cPanel shared hosting. You don't need a VPS, a dedicated server, a managed cloud platform, or any specialist infrastructure. If your host provides cPanel with PHP 8.1 or higher and a MySQL or MariaDB database, CARL will run on it. Most shared hosting accounts available today meet these requirements without any special configuration.</p><p><img src="/images/what-hosting-does-carl-require-standard-cpanel-shared-hostin-1780359697.webp" alt="What Hosting Does CARL Require" class="img-fluid" style=""><br></p>

<h2>The Core Requirements</h2>

<p>CARL requires PHP 8.1 or higher. The installer checks your PHP version automatically during the pre-flight stage and will tell you if your server falls short. Most cPanel hosts running modern infrastructure default to PHP 8.1 or 8.2, and many offer 8.3. If your host is still running PHP 7.x, it's time to either upgrade the PHP version in cPanel's MultiPHP Manager or switch hosts.</p>

<p>CARL also requires six PHP extensions to be enabled: <code>curl</code>, <code>mbstring</code>, <code>pdo</code>, <code>pdo_mysql</code>, <code>json</code>, and <code>zip</code>. These are standard extensions that come enabled by default on virtually every cPanel shared hosting account. The installer checks for all of them and flags any that are missing. If one is missing, contact your hosting provider and ask them to enable it.</p>

<h2>Database Requirements</h2>

<p>CARL stores all your content, settings, and module data in a MySQL or MariaDB database. Any cPanel host with MySQL Databases in the control panel supports this. Create the database and user in cPanel before running the installer; the setup wizard creates all tables automatically. You don't need a managed database service or a separate database server.</p>

<p>MariaDB 11.4 is what CARL is developed and tested against, but MySQL 5.7 and above work without issues. The specific database engine your host provides is generally not something you need to research: if your cPanel account has MySQL Databases, it's compatible.</p>

<h2>Server and Directory Requirements</h2>

<p>Your document root directory needs to be writable with standard 755 permissions. This is the default on cPanel shared hosting and shouldn't require any manual configuration. The installer checks this during the pre-flight stage. If the directory isn't writable, the installer will tell you before attempting anything.</p>

<p>During installation, your server needs to be able to make outbound HTTPS connections to the CARL licensing server to verify your license and download the CARL package. Most shared hosting accounts allow this by default. If the installer fails at the download stage, contact your host and ask them to allow outbound HTTPS connections to external servers.</p>

<h2>What You Don't Need</h2>

<p>CARL doesn't require root server access, SSH, a command line, or any server administration beyond what's available in cPanel. You don't need to configure PHP-FPM, adjust server memory limits, install server-side software, or touch anything outside File Manager, MySQL Databases, and the Cron Jobs section of cPanel. Everything CARL needs is available through the standard cPanel interface.</p>

<p>You also don't need a fast or expensive hosting plan. Because CARL pages are static PHP files written to disk at publish time, the server does almost no work on each page request. A basic shared hosting account that would struggle to serve a WordPress site at any meaningful traffic volume handles a CARL site without breaking a sweat.</p>

<h2>Recommended Hosts</h2>

<p>Any reputable cPanel shared hosting provider works. The things worth checking when choosing a host are: PHP 8.1 or higher available in MultiPHP Manager, MySQL Databases in the control panel, outbound HTTPS allowed, and reasonably fast SSD storage. Beyond those basics, CARL isn't demanding. The host that serves your files quickly is the right host, and for static PHP files, the bar is not high.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/what-hosting-does-carl-require-standard-cpanel-shared-hostin-1780359697.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Install CARL on cPanel Shared Hosting | From Download to Live Site in One Session | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/installation/how-to-install-carl-on-cpanel.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/installation/how-to-install-carl-on-cpanel.php</guid>
    <pubDate>Mon, 01 Jun 2026 19:15:24 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-install-carl-on-cpanel-shared-hosting-from-download-t-1780355659.webp" alt="How to Install CARL on cPanel Shared Hosting | From Download to Live Site in One Session | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Install CARL on cPanel Shared Hosting</h1>

<p>Installing CARL takes three stages: create a database in cPanel, run the CARL installer, and complete the setup wizard. The installer handles almost everything automatically. Your job is to follow the steps in order and have your database credentials ready before you start.</p><p><img src="/images/how-to-install-carl-on-cpanel-shared-hosting-from-download-t-1780355659.webp" alt="How to Install CARL on cPanel Shared Hosting" class="img-fluid" style=""><br></p>

<h2>What You Need Before You Start</h2>

<p>You need a cPanel shared hosting account with PHP 8.1 or higher, access to MySQL Databases in cPanel, and your CARL purchase confirmation email. The confirmation email contains the installer file download and the email address and password you'll use to verify your license. Have those credentials ready before you begin.</p>

<h2>Step 1: Create Your Database</h2>

<p>In cPanel, go to MySQL Databases. Create a new database and give it a recognizable name, something like <code>yoursite_carl</code>. Then create a new database user with a strong password. Add that user to the database and grant all privileges. Write down the database name, username, and password immediately and store them somewhere safe. The setup wizard needs all three.</p>

<h2>Step 2: Upload and Run the Installer</h2>

<p>Download the installer file from your CARL purchase confirmation email. In cPanel File Manager, navigate to your domain's <code>public_html/</code> folder and upload the installer file there. Then open a new browser tab and navigate to the installer URL shown in your purchase confirmation.</p>

<p>The installer opens with a pre-flight check. It verifies your server meets the requirements: PHP 8.1 or higher, the required PHP extensions (curl, mbstring, pdo, pdo_mysql, json, zip), and a writable directory. All green means you're good to proceed. If anything fails, contact your hosting provider and ask them to enable the missing requirement.</p>

<p>Enter the email address and password from your CARL purchase confirmation, then click Verify License and Install CARL. The installer contacts the CARL licensing server, verifies your license, downloads the full CARL package, runs a SHA-256 integrity check, and extracts everything into your document root. This usually takes 30 to 90 seconds. Don't click anything or close the browser while it runs.</p>

<p>When it finishes, a green success message appears. The installer self-deletes at this point as a security measure. Click Continue to CARL Setup.</p>

<h2>Step 3: Complete the Setup Wizard</h2>

<p>On the setup page, click Fresh Install. Fill in your database connection details: <code>DB_HOST</code> (leave as <code>localhost</code> unless your host specifies otherwise,) <code>DB_NAME</code>, <code>DB_USER</code>, and <code>DB_PASS</code> using exactly what you set up in cPanel.</p>

<p>The <code>SITE_ROOT</code> field is the most common source of installation errors. It must be the full absolute server path to your <code>public_html</code> folder, starting with <code>/home/</code>, with no trailing slash. Look at the address bar in cPanel File Manager while you're in your <code>public_html</code> folder: that path is your SITE_ROOT. It looks like <code>/home/yourusername/public_html</code>. Not a URL, not a relative path.</p>

<p>Choose your admin username and a strong password, then click Run Fresh Install. CARL creates all database tables and confirms success. Click Go to Login.</p>

<h2>Your First Login</h2>

<p>Go to <code>https://yoursite.com/admin/</code> and log in with the credentials you just set. Bookmark this URL immediately. Once you're in, go to Settings and complete your initial configuration before publishing anything. For a full walkthrough of what to set up on first login, see <a href="/wiki/installation/how-to-configure-carl-general-settings.php">How to configure CARL's general settings</a>.</p>

<h2>If Something Goes Wrong</h2>

<p>License verification failures are almost always due to incorrect credentials: use the exact email and password from the purchase confirmation, not your hosting login. Database errors in the setup wizard are almost always a credentials mismatch or missing privileges: double-check the database name, username, and password against what you created in cPanel, and confirm the user has all privileges on that database. SITE_ROOT errors mean the path format is wrong: copy it directly from the cPanel File Manager address bar.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-install-carl-on-cpanel-shared-hosting-from-download-t-1780355659.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Add a Favicon in CARL | Set Your Site Icon Once, Done | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-to-add-a-favicon.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-to-add-a-favicon.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:39:52 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-add-a-favicon-in-carl-set-your-site-icon-once-done-ca-1780353494.webp" alt="How to Add a Favicon in CARL | Set Your Site Icon Once, Done | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Add a Favicon in CARL</h1>

<p>A favicon is the small icon that appears in browser tabs, bookmarks, and search results next to your page title. It's a minor detail that makes a site look finished and professional. Adding one in CARL takes about two minutes: you prepare the icon file, upload it to your server, and add the reference tag to your template. Every page CARL generates from that template will automatically include it.</p><p><img src="/images/how-to-add-a-favicon-in-carl-set-your-site-icon-once-done-ca-1780353494.webp" alt="How to Add a Favicon in CARL" class="img-fluid" style=""><br></p>

<h2>Preparing Your Favicon File</h2>

<p>The most widely supported favicon format is a 32x32 pixel PNG file named <code>favicon.png</code>, or an ICO file named <code>favicon.ico</code>. Modern browsers also support SVG favicons, which scale cleanly at any size. For maximum compatibility across browsers and devices, a 32x32 PNG is the simplest choice. If you also want a larger icon for iOS home screens and Android bookmarks, prepare a second file at 180x180 pixels for the Apple Touch Icon.</p>

<p>Keep the design simple. A favicon is tiny. A detailed logo that looks sharp at full size becomes an unreadable smudge at 32 pixels. Use a simplified version of your logo, a single letter, or a distinctive symbol that reads clearly at small sizes.</p>

<h2>Uploading the File</h2>

<p>Upload your favicon file to the root of your domain's <code>public_html/</code> directory. Placing it at the root means browsers can find it automatically even without an explicit link tag, but adding the link tag in your template is still best practice. Upload via cPanel's File Manager or any FTP client. The file should be accessible at <code>yourdomain.com/favicon.png</code> once uploaded.</p>

<h2>Adding the Tag to Your Template</h2>

<p>Open your site's template file and add the favicon link tag inside the <code></code> section. For a PNG favicon, the tag looks like this:</p>

<code><link rel="icon" type="image/png" href="/favicon.png"></code>

<p>If you're also adding an Apple Touch Icon for mobile home screens, add a second tag:</p>

<code><link rel="apple-touch-icon" href="/apple-touch-icon.png"></code>

<p>Save the template. The tags are now part of the template structure that CARL uses when generating pages. Every page using that template will include the favicon reference in its <code></code> from the next regenerate onward.</p>

<h2>Pushing the Change to Published Pages</h2>

<p>Adding the tag to the template doesn't update pages that are already on disk. Those files were generated before the template change and don't yet contain the favicon tag. Run <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> to push the updated template across every published page in one pass. After the regeneration completes, every page on your site will have the favicon tag in its head section.</p>

<h2>Checking It Works</h2>

<p>Open any published page in a browser and look at the tab. Your favicon should appear next to the page title. If it doesn't show immediately, do a hard refresh: browsers cache favicons aggressively and sometimes hold onto an old version or a blank state for longer than you'd expect. In Chrome, you can force a favicon refresh by opening DevTools, going to the Application tab, finding the favicon in the cache, and clearing it. A full browser cache clear also works.</p>

<p>Once the favicon is showing correctly in one browser, check it in another to confirm it's not a caching issue. If it's missing in all browsers after a hard refresh, double-check that the file is at the correct path on your server and that the link tag in the template points to the right filename and extension.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-add-a-favicon-in-carl-set-your-site-icon-once-done-ca-1780353494.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Fix a Canonical Mismatch in CARL | Correct the Tag, Regenerate, Done | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-to-fix-a-canonical-mismatch.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-to-fix-a-canonical-mismatch.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:35:54 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-fix-a-canonical-mismatch-in-carl-correct-the-tag-rege-1780353269.webp" alt="How to Fix a Canonical Mismatch in CARL | Correct the Tag, Regenerate, Done | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Fix a Canonical Mismatch in CARL</h1>

<p>A canonical mismatch means the canonical tag in your published PHP file points to a different URL than the page actually lives at. It usually happens when a page's slug or directory is changed after the page has already been published, leaving the old URL declared as authoritative in the file while the page is now accessible at a new one. CARL's <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health checker</a> surfaces these automatically. The fix is straightforward.</p><p><img src="/images/how-to-fix-a-canonical-mismatch-in-carl-correct-the-tag-rege-1780353269.webp" alt="How to Fix a Canonical Mismatch in CARL" class="img-fluid" style=""><br></p>

<h2>Why Canonical Mismatches Happen</h2>

<p>The most common cause is a slug change. You publish a page, later decide the slug should be different, update it in the editor, and regenerate. The new file is at the new URL, but if the Head Injection field still contains the canonical tag from the previous Generate Schema run, that tag still points to the old URL. The file is in the right place, but it's declaring the wrong address as its canonical.</p>

<p>The same thing happens with directory changes. Move a page from <code>blog/</code> to <code>articles/</code>, regenerate, and the canonical in the Head Injection field still references the old path unless you update it before regenerating.</p>

<h2>Finding the Mismatch</h2>

<p>Run a Site Health audit from the CARL admin panel. Any page with a canonical mismatch appears in the results with the issue flagged. The audit compares the canonical URL specified in the Head Injection field with the page's actual URL, determined by its current slug and directory settings. If they don't match, the page is flagged.</p>

<p>You can also spot a mismatch manually by viewing the source of a published page and checking the canonical tag in the <code></code> section. If the URL in the <code>href</code> attribute doesn't match the page's actual URL, you have a mismatch.</p>

<h2>Fixing the Mismatch</h2>

<p>Open the flagged page in the CARL editor. Go to the Head Injection field and find the canonical tag. It looks like this:</p>

<p><code><link rel="canonical" href="https://yourdomain.com/old-directory/old-slug.php"></code></p>

<p>Update the URL to match the page's current slug and directory:</p>

<p><code><link rel="canonical" href="https://yourdomain.com/current-directory/current-slug.php"></code></p>

<p>Save the page and click Generate. The updated canonical tag is written into the new PHP file at the correct URL. The mismatch is resolved.</p>

<h2>Using Generate Schema to Fix It Cleanly</h2>

<p>If you'd rather not edit the Head Injection field manually, the cleanest approach is to clear the Head Injection field entirely and run Generate Schema again. Claude will produce a fresh head block with the canonical tag pointing to the current URL, derived from the page's current slug and directory settings. Check the generated output to confirm the canonical is correct before saving and regenerating.</p>

<p>This approach also refreshes the rest of the head block: the meta description, schema, Open Graph tags, and Twitter Card tags all get rewritten based on the current content. If the page has been significantly updated since the last Generate Schema run, this is the better option.</p>

<h2>After the Fix</h2>

<p>Once the page is regenerated with the correct canonical, run Site Health again to confirm the mismatch is cleared. If you changed the slug or directory as part of the same update, remember that the old file is still on disk at the old path. Delete it via cPanel's File Manager to avoid having a second copy of the content at an unintended URL. A stale file at an old URL with a canonical pointing to the new URL is better than a stale file with no canonical at all, but removing it entirely is the cleanest outcome.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-fix-a-canonical-mismatch-in-carl-correct-the-tag-rege-1780353269.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Site Health Checker Works | Catch SEO Problems Before Google Does | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carls-site-health-checker-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carls-site-health-checker-works.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:26:45 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-site-health-checker-works-catch-seo-problems-befo-1780352720.webp" alt="How CARL&#039;s Site Health Checker Works | Catch SEO Problems Before Google Does | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Site Health Checker Works</h1>

<p>Site Health is CARL's built-in SEO audit tool. It scans every published page on your site, checks each one against a set of SEO and content quality criteria, and surfaces anything that needs attention.</p><p>Missing meta descriptions, canonical mismatches, pages without schema, titles that are too long or too short: Site Health finds them and lists them so you can fix them without having to open every page individually.</p><p><img src="/images/how-carl-s-site-health-checker-works-catch-seo-problems-befo-1780352720.webp" alt="How CARL's Site Health Checker Works" class="img-fluid" style=""><br></p>

<h2>What Site Health Checks</h2>

<p>Site Health runs through a checklist on every published page. It checks for the presence and length of the page title, the meta description, and the H1. It verifies that a canonical tag is present and that the canonical URL matches the page's actual URL. It checks whether the Head Injection field contains content and flags pages where Generate Schema hasn't been run. It also checks for pages that have been edited in the database but not regenerated, meaning the disk file is out of sync with the current record.</p>

<p>The checks cover the most common SEO issues that accumulate on a site over time: pages published before the Generate Schema workflow was established, pages where the slug was changed without updating the canonical URL, and pages where the meta description was left blank because the content felt self-explanatory at the time. Site Health finds all of them in one pass.</p>

<h2>How to Run a Site Health Check</h2>

<p>In the CARL admin panel, navigate to Site Health. Click the audit button, and CARL works through every published page record, running each check in sequence. The results appear as a list of pages with issues, grouped by issue type. A page can appear in multiple groups if it has more than one problem. Pages that pass all checks don't appear in the results, so the list shows only what needs attention.</p>

<p>On a large site, the audit takes a few seconds longer than on a small one, but it runs entirely on the server. You don't need to keep the tab active while it processes. The results stay available in the admin until you run the next audit.</p>

<h2>Acting on the Results</h2>

<p>Each flagged page in the results links directly to that page's editor. Click through, fix the issue, run Generate Schema if the page needs a head block, and regenerate. For pages flagged as out of sync between the database and the file on disk, the fix is a regeneration with no other changes needed. For pages with missing or oversized titles and descriptions, you'll need to edit the content or the Head Injection field before regenerating.</p>

<p>For issues that affect a large number of pages at once, such as a batch of pages published without running Generate Schema, the most efficient approach is to open each one, run Generate Schema, save, then use <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> to push all the updates to disk in a single pass rather than regenerating each page individually.</p>

<h2>Running Site Health Regularly</h2>

<p>Site Health is most useful as a regular check rather than a one-time fix. Run it after any batch of new pages to confirm everything was published correctly. Run it after a template change and bulk regenerate to verify no pages were missed. Run it periodically on a mature site to catch any drift that's accumulated since the last audit.</p>

<p>The issues Site Health finds aren't catastrophic individually, but they compound. A site where half the pages are missing schema, a quarter have canonical mismatches, and a handful have titles Google is rewriting because they're too long is a site leaving ranking signals on the table. Site Health keeps the baseline clean so those signals go where they should.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-site-health-checker-works-catch-seo-problems-befo-1780352720.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles RSS Feeds | Syndicate Your Content Without a Plugin | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-handles-rss-feeds.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-handles-rss-feeds.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:20:29 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-rss-feeds-syndicate-your-content-without-a--1780352332.webp" alt="How CARL Handles RSS Feeds | Syndicate Your Content Without a Plugin | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles RSS Feeds</h1>

<p>An RSS feed is a machine-readable file that lists your most recently published content. Feed readers, news aggregators, podcast apps, and other services subscribe to it and pull updates automatically when new content appears. For SEO purposes, RSS feeds also give Google a fast signal that new content exists on your site. CARL generates and maintains your RSS feed automatically, no plugin required.</p><p><img src="/images/how-carl-handles-rss-feeds-syndicate-your-content-without-a--1780352332.webp" alt="How CARL Handles RSS Feeds" class="img-fluid" style=""><br></p>

<h2>How CARL Builds the Feed</h2>

<p>CARL generates your RSS feed from your published page records and serves it at the standard location: <code>yourdomain.com/rss.xml</code>. The feed updates each time you publish or regenerate pages, pulling the most recently published content into the file. You don't trigger the feed update manually. It happens as part of the normal publishing process.</p>

<p>The feed follows the RSS 2.0 specification, which is the format feed readers and aggregators expect. Each entry includes the page title, URL, publication date, and description. The description field pulls from the page's meta description, which is another reason the Generate Schema step matters: a well-written meta description serves double duty as the feed excerpt that subscribers and aggregators see.</p>

<h2>RSS and Google Indexing</h2>

<p>Google crawls RSS feeds as part of its discovery process. A new page that appears in your RSS feed can be picked up and queued for indexing faster than a page Google finds only by following internal links. Combined with a submitted <a href="/wiki/seo/how-carl-generates-xml-sitemaps.php">XML sitemap</a>, the RSS feed gives Google two separate signals that new content has been published. On an actively updated site, that combination keeps crawl frequency high.</p>

<p>CARL pages already have a structural advantage here. The static PHP files load fast, the structured data is in place, and there's nothing for Google's crawler to trip over during execution. The RSS feed simply ensures new content gets noticed sooner rather than waiting for Google to find it through internal links alone.</p>

<h2>Feed Readers and Content Syndication</h2>

<p>Readers who subscribe to your RSS feed receive updates whenever you publish, without you having to notify them directly. For a content-heavy site like a wiki or a blog, this is a low-effort way to build a returning audience. Someone who subscribes via RSS is actively choosing to follow your output, which makes them a more engaged reader than someone who finds a page through search once and never returns.</p>

<p>Some content aggregators and curation platforms also automatically pull from RSS feeds. If your content is relevant to a niche community that uses feed aggregators, being present in those feeds extends your reach without any additional distribution effort on your part.</p>

<h2>Submitting Your Feed</h2>

<p>Google Search Console doesn't require a separate RSS feed submission, unlike sitemaps. Google discovers and crawls RSS feeds on its own once it's familiar with your domain. If you want to make the feed easy to find for human subscribers, add a visible RSS link in your site's navigation or footer. The URL is always <code>yourdomain.com/rss.xml. So it's consistent across every CARL install.</code></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-rss-feeds-syndicate-your-content-without-a--1780352332.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Generates XML Sitemaps | Keep Google Updated Without a Plugin | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-generates-xml-sitemaps.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-generates-xml-sitemaps.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:15:41 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-generates-xml-sitemaps-keep-google-updated-without--1780351939.webp" alt="How CARL Generates XML Sitemaps | Keep Google Updated Without a Plugin | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Generates XML Sitemaps</h1>

<p>An XML sitemap is a file that lists every page on your site you want search engines to crawl and index. It's not a ranking factor, but it's a reliable way to ensure Google knows your pages exist, particularly for newer sites that don't yet have the inbound links that would naturally lead crawlers to every page.</p><p><img src="/images/how-carl-generates-xml-sitemaps-keep-google-updated-without--1780351939.webp" alt="How CARL Generates XML Sitemaps" class="img-fluid" style=""><br></p><p>CARL generates your sitemap automatically, keeps it up to date as you publish, and serves it as a static file without any plugins.</p>

<h2>How CARL Builds the Sitemap</h2>

<p>Every time you generate a page in CARL, the sitemap is updated to include it. CARL pulls the published page records from the database, builds a valid XML sitemap file, and writes it to your server. The sitemap is available at the standard location crawlers expect: <code>yourdomain.com/sitemap.xml</code>. You don't need to trigger a separate sitemap generation step or install an SEO plugin to get a working sitemap.</p>

<p>Pages with a status of draft or scheduled are not included in the sitemap. Only published pages make it into the file. If you unpublish a page or delete its generated file, the next sitemap update removes it from the list.</p>

<h2>What the Sitemap Contains</h2>

<p>CARL's sitemap follows the standard XML sitemap protocol. Each entry includes the page's full URL of the published PHP file, and the <code><lastmod></lastmod></code> date reflecting when the page was last generated. The <code><lastmod></lastmod></code> value is useful to Google: it signals that a page has been updated recently, which can prompt a recrawl sooner than a page that shows an old modification date.</p>

<p>Priority and changefreq values are optional in the sitemap protocol and have minimal influence on how Google actually crawls your site. CARL keeps the sitemap clean and doesn't pad it with values that Google largely ignores.</p>

<h2>Submitting Your Sitemap to Google</h2>

<p>Once your site is live, submit the sitemap URL to Google Search Console. Go to the Sitemaps section, enter <code>sitemap.xml</code>, and submit. Google will crawl it, report how many URLs it found, and begin indexing your pages. On a new site with no existing backlinks, this is the fastest way to get your pages into Google's index.</p>

<p>CARL pages index quickly regardless: the static PHP files load fast, the structured data is in place from the Generate Schema step, and there's no dynamic execution for Google's crawler to trip over. Submitting the sitemap simply ensures Google has a complete list of pages to work from rather than discovering them only through internal links.</p>

<h2>The Sitemap and Bulk Regenerate</h2>

<p>When you run <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a>, the sitemap is rebuilt as part of the process. Every published page gets a fresh <code><lastmod></lastmod></code> date reflecting the regeneration time. If you've made sitewide template changes and bulk-regenerated to push them across all pages, the sitemap update occurs in the same pass. There's no separate sitemap refresh step required.</p>

<h2>Multiple Sites, Multiple Sitemaps</h2>

<p>Each CARL install generates its own sitemap for its own domain. If you're running multiple domains on the same server, each install maintains a separate <code>sitemap.xml</code> at its own domain root. There's no crossover between installs and no configuration required to keep them separate. The sitemap for each site reflects only the published pages in that site's database, as described in <a href="" target="_blank" style="background-color: rgb(255, 255, 255); font-family: " ibm="" plex="" sans",="" system-ui,="" sans-serif;"="">"How CARL handles multiple domains on one server</a>."</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-generates-xml-sitemaps-keep-google-updated-without--1780351939.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Open Graph Tags | Control How Your Pages Look When Shared on Social | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-handles-open-graph-tags.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-handles-open-graph-tags.php</guid>
    <pubDate>Mon, 01 Jun 2026 18:07:51 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-open-graph-tags-control-how-your-pages-look-1780351568.webp" alt="How CARL Handles Open Graph Tags | Control How Your Pages Look When Shared on Social | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Open Graph Tags</h1>

<p>When someone shares one of your pages on Facebook, LinkedIn, or any other platform that reads Open Graph data, those platforms don't use your page title and meta description. They use the Open Graph tags: a separate set of meta tags in the page's <code></code> that specify exactly what title, description, and image should appear in the shared preview. CARL handles these via the AI Schema Generator, with manual fields available when you need direct control.</p><p><img src="/images/how-carl-handles-open-graph-tags-control-how-your-pages-look-1780351568.webp" alt="How CARL Handles Open Graph Tags" class="img-fluid" style=""><br></p>

<h2>What the Generate Schema Button Produces</h2>

<p>When you click Generate Schema, Claude writes a complete set of Open Graph tags as part of the head block it delivers to the Head Injection field. The block includes <code>og:type</code>, <code>og:title</code>, <code>og:description</code>, <code>og:url</code>, <code>og:image</code>, and <code>og:site_name</code>.</p><p>These are written based on your page content and title, formatted correctly, and appended to Head Injection automatically alongside the schema, Twitter Card tags, meta description, and canonical. One click covers the full set.</p>

<p>The <code>og:image</code> tag uses the featured image you've assigned to the page in CARL. If a featured image is present, Claude includes its URL in the Open Graph block. Social platforms pull that image when the page is shared, giving your links a visual presence in feeds rather than appearing as text-only previews.</p>

<h2>Twitter Card Tags</h2>

<p>Generate Schema also produces Twitter Card meta tags in the same pass. The Twitter Card block includes <code>twitter:card</code> set to <code>summary_large_image</code>, along with <code>twitter:title</code>, <code>twitter:description</code>, and <code>twitter:image</code>. These control how your pages appear when shared on X, using the large image format that gives shared links significantly more visual weight in the timeline than a plain text link.</p>

<p>Open Graph and Twitter Cards serve the same purpose on different platforms. Generate Schema handles both in one operation, so there's nothing to configure separately for social sharing once the head block is in place.</p>

<h2>The Manual Override Fields</h2>

<p>CARL's page editor includes manual fields for the Open Graph title, description, and image. These exist for cases where the AI-generated Open Graph copy isn't quite right for a specific page: a page where the social sharing framing should differ noticeably from the search framing, or a page where you want to test specific social copy. If you fill in the manual fields, those values override the generated ones when you regenerate.</p>

<p>For most pages, the manual fields stay empty. Generate Schema writes better-optimized Open Graph copy faster than filling in fields by hand, and the result is consistent with the rest of the head block it produces.</p>

<h2>Open Graph Images</h2>

<p>The image that appears in social shares is one of the highest-impact elements of a shared link. A relevant, well-composed image stops the scroll. A missing or generic image doesn't. CARL's Open Graph image tag points to the page's featured image, so the image you choose in the page editor is the one that appears when the page is shared.</p>

<p>Social platforms cache Open Graph data aggressively. If you update the featured image on a published page and regenerate, the new image tag will be in the file immediately, but platforms that have already cached the old data may continue showing the old image for a period. Facebook's Sharing Debugger and LinkedIn's Post Inspector both let you force a cache refresh if you need the update to appear immediately.</p>

<h2>Baked In at Generation Time</h2>

<p>Like every other element of CARL's SEO output, the Open Graph tags are written into the static PHP file at generation time. There's no dynamic tag assembly happening on each page request. When a social platform crawls your page to generate a preview, it reads the tags directly from the file. The tags are always present, always correctly formed, and served instantly from the static file without any PHP execution required to produce them.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-open-graph-tags-control-how-your-pages-look-1780351568.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Generates JSON-LD Schema Markup | AI-Powered Structured Data on Every Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/seo/how-carl-generates-json-ld-schema-markup.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/seo/how-carl-generates-json-ld-schema-markup.php</guid>
    <pubDate>Mon, 01 Jun 2026 17:52:24 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-generates-json-ld-schema-markup-ai-powered-structur-1780350511.webp" alt="How CARL Generates JSON-LD Schema Markup | AI-Powered Structured Data on Every Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Generates JSON-LD Schema Markup</h1>

<p>JSON-LD schema markup is structured data that tells search engines exactly what your page is about: who wrote it, when it was published, what entities it covers, how it relates to other things on the web.</p><p><img src="/images/how-carl-generates-json-ld-schema-markup-ai-powered-structur-1780350511.webp" alt="How CARL Generates JSON-LD Schema Markup" class="img-fluid" style=""><br></p><p>Google uses it to build knowledge graph connections, power rich results, and understand content with more precision than it can from HTML alone. CARL generates it automatically through its AI Schema Generator, powered by Claude.</p>

<h2>How the AI Schema Generator Works</h2>

<p>Once you've written your page content, click the Generate Schema button in the page editor. CARL sends your content to Claude along with a set of instructions that specify exactly what to produce: an Article schema, Thing entities for knowledge graph enhancement, Twitter Card tags, Open Graph tags, a canonical tag, an optimized meta description, and a keywords tag, all delivered in a consistent order.</p><p>Claude reads the content, generates the complete head block, and CARL appends the result directly to the Head Injection field. You don't paste anything manually. The transfer is automatic.</p>

<p>When you click Generate to publish the page, the entire head block gets baked into the <code></code> section of the generated PHP file. From that point, every visitor and every crawler gets the full structured data package on every page request, served from a static file with no runtime overhead.</p>

<h2>What the Schema Block Contains</h2>

<p>The Article schema Claude produces includes the page headline, description, author details and a URL pointing to <code>/about.php</code>, publisher information, <code>datePublished</code> and <code>dateModified</code> formatted with the correct timezone, <code>isAccessibleForFree: true</code>, and a <code>mainEntityOfPage</code> reference. It also includes Thing entities under the <code>about</code> property, each describing a key concept the page covers.</p><p>These Thing entities enable CARL pages to build knowledge graph connections beyond the basic Article type.</p>

<p>Where relevant, Claude adds <code>sameAs</code> links connecting entities to their corresponding entries on Wikipedia, Wikidata, or other authoritative sources. This considerably strengthens the knowledge graph signal.</p><p>A page about a named psychological effect, a historical business story, or a well-documented technical concept benefits significantly from sameAs links that anchor those entities to established knowledge graph nodes.</p>

<h2>Why JSON-LD Over Other Schema Formats</h2>

<p>Google supports three schema formats: JSON-LD, Microdata, and RDFa. JSON-LD is the one Google recommends, and for good reason. It sits in the <code></code> as a self-contained script block, completely separate from the page's HTML markup.</p><p>You can read, edit, and validate it without touching the content. Microdata and RDFa require you to weave schema attributes into the HTML itself, which makes the markup harder to read and schema changes harder to make cleanly. CARL uses JSON-LD exclusively.</p>

<h2>Manual Schema for Specific Cases</h2>

<p>The AI Schema Generator handles the Article schema for standard content pages. For pages that need a different schema type, such as a LocalBusiness schema for a location page, an FAQ schema for a questions page, or a Product schema for a sales page, you write the JSON-LD manually and paste it into the Head Injection field alongside or instead of the AI-generated block. The Head Injection field accepts any valid HTML or script content and writes it verbatim into the page's <code></code>.</p>

<p>This is the only case where manual schema entry makes sense: when the page type genuinely calls for a schema type that the Article generator doesn't cover. For everything else, Generate Schema produces better-structured, more consistent markup faster than writing it by hand.</p>

<h2>Validating Your Schema</h2>

<p>After generating a page, you can validate its schema using Google's Rich Results Test at search.google.com/test/rich-results. Paste the page URL, run the test, and Google shows you exactly what structured data it found and whether it's valid. CARL's <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health checker</a> also flags pages with missing head injection content, catching pages published before Generate Schema was run or pages where the Head Injection field was accidentally cleared before regeneration.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-generates-json-ld-schema-markup-ai-powered-structur-1780350511.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Set Up PayPal in CARL | Connect PayPal and Start Selling Digital Downloads | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-to-set-up-paypal-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-to-set-up-paypal-in-carl.php</guid>
    <pubDate>Mon, 01 Jun 2026 15:36:25 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-set-up-paypal-in-carl-connect-paypal-and-start-sellin-1780342417.webp" alt="How to Set Up PayPal in CARL | Connect PayPal and Start Selling Digital Downloads | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Set Up PayPal in CARL</h1>

<p>CARL's Downloads module uses PayPal to process payments. Setting it up requires a PayPal Business account, API credentials from the PayPal developer dashboard, and a few minutes in CARL's settings panel. Once it's connected, the entire purchase-to-delivery flow runs automatically without any third-party storefront involved.</p><p><img src="/images/how-to-set-up-paypal-in-carl-connect-paypal-and-start-sellin-1780342417.webp" alt="How to Set Up PayPal in CARL" class="img-fluid" style=""><br></p>

<h2>What You Need Before You Start</h2>

<p>You need a PayPal Business account. A personal PayPal account won't give you access to the API credentials CARL requires. If you don't have a Business account yet, upgrading from a personal account is straightforward inside PayPal's account settings and doesn't require a new email address or a new account.</p>

<p>You also need access to the PayPal Developer Dashboard at developer.paypal.com. This is where you create the app that generates your API credentials and where you register your webhook URL. Both steps happen here before you touch anything in CARL.</p>

<h2>Creating Your PayPal App</h2>

<p>In the PayPal Developer Dashboard, navigate to My Apps and Credentials. Create a new app under your Business account. Give it a name that identifies it as your CARL install, something like your domain name, so it's easy to find later. Once the app is created, PayPal provides a Client ID and a Secret Key.</p><p>These are the credentials you'll paste into CARL's settings panel.</p>

<p>PayPal provides both Sandbox and Live credentials. Sandbox credentials are for testing: payments go through a simulated environment, and no real money moves. Live credentials connect to real PayPal accounts and process real payments. Start with Sandbox to verify the full purchase flow works correctly, then switch to Live credentials when you're ready to accept real payments.</p>

<h2>Entering Your Credentials in CARL</h2>

<p>In CARL's admin panel, go to Settings and find the PayPal configuration section. Enter your Client ID and Secret Key from the PayPal Developer Dashboard. Select whether you're running in Sandbox or Live mode to match the credentials you've entered. Save the settings.</p>

<p>CARL stores these credentials in the database and uses them to verify incoming webhook notifications against PayPal's API. Keep them private. They're not exposed in any generated page file, but you should treat them with the same care as any other API key.</p>

<h2>Registering the Webhook URL</h2>

<p>Back in the PayPal Developer Dashboard, find the Webhooks section for your app. Add a new webhook and enter the endpoint URL from CARL's settings panel. This is the URL PayPal will post payment notifications to when a purchase is completed.</p>

<p>Select the payment completion event types you want PayPal to notify you about. For CARL's Downloads module, you need, at minimum, the completed payment event so CARL knows when a purchase has been confirmed. Save the webhook in PayPal's dashboard. From this point, completed payments will automatically trigger a notification to your CARL install.</p>

<h2>Testing the Connection</h2>

<p>With Sandbox credentials active, use PayPal's test buyer accounts to complete a test purchase on one of your product pages. Walk through the full flow: click the buy button, complete the PayPal checkout, confirm the purchase, check that the download email arrives, verify the download link works.</p><p>If every step completes correctly, the integration is working.</p>

<p>Once you've confirmed the Sandbox flow works end-to-end, switch CARL's settings to Live mode, update to your Live credentials, and update the webhook URL in the PayPal Developer Dashboard to point to your live endpoint. Then you're ready to take real payments. For a full picture of what happens after a payment is confirmed, see <a href="/wiki/downloads/how-the-paypal-webhook-works-in-carl.php">How the PayPal webhook works in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-set-up-paypal-in-carl-connect-paypal-and-start-sellin-1780342417.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How the PayPal Webhook Works in CARL | Automate Purchase Confirmation and File Delivery | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-the-paypal-webhook-works-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-the-paypal-webhook-works-in-carl.php</guid>
    <pubDate>Mon, 01 Jun 2026 15:27:20 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-the-paypal-webhook-works-in-carl-automate-purchase-confi-1780341699.webp" alt="How the PayPal Webhook Works in CARL | Automate Purchase Confirmation and File Delivery | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How the PayPal Webhook Works in CARL</h1>
<p>When a buyer completes a purchase on PayPal, CARL needs to be notified immediately so it can generate a download token and send the buyer their download link. That notification comes through a PayPal webhook: an automated HTTP request that PayPal sends to your server the moment a payment is confirmed. Understanding how that process works helps you set it up correctly and troubleshoot it if something goes wrong.</p><p><img src="/images/how-the-paypal-webhook-works-in-carl-automate-purchase-confi-1780341699.webp" alt="How the PayPal Webhook Works in CARL" class="img-fluid" style=""><br></p>
<h2>What a Webhook Is</h2>
<p>A webhook is a server-to-server notification. Instead of your site polling PayPal repeatedly to check whether a payment has come through, PayPal takes the initiative and posts a notification directly to a URL you specify. The moment a payment is confirmed on PayPal's end, their system sends a POST request to your webhook URL with the transaction details. Your server receives it, processes it, and acts on it.</p>
<p>The entire exchange takes seconds and requires no action from the buyer. They complete the payment, PayPal fires the webhook, CARL processes it, and the buyer receives their download email before they've had time to wonder what happens next.</p>
<h2>What CARL Does When the Webhook Fires</h2>
<p>When PayPal's webhook hits CARL's endpoint, CARL verifies the notification's authenticity by checking it against PayPal's verification API. A notification that fails verification is discarded. This prevents anyone from spoofing a payment confirmation to trigger a download without actually paying.</p>
<p>Once the notification is verified, CARL checks the payment details against the product record, confirms the purchase in the database, generates a <a href="/wiki/downloads/how-carl-download-tokens-work.php">unique download token</a>, and sends the buyer an email with their download link. All of that happens automatically on the server side, with no manual step required from you.</p>
<h2>The Webhook URL</h2>
<p>Your CARL install has a dedicated webhook endpoint that PayPal posts to. You register this URL in your PayPal developer account when you <a href="/wiki/downloads/how-to-set-up-paypal-in-carl.php">set up PayPal in CARL</a>. The URL follows a consistent pattern based on your domain and your CARL install location. CARL's settings panel shows you the exact URL to use.</p>
<p>The webhook URL needs to be publicly accessible. PayPal's servers are posting to it from outside your network, so it can't be behind a login, a firewall rule that blocks external requests, or a development environment that isn't reachable from the internet. A live cPanel hosting account on a public domain works without any additional configuration.</p>
<h2>What Happens If the Webhook Fails</h2>
<p>If PayPal's webhook notification doesn't reach your server, or if CARL returns an error response, PayPal will retry the notification several times over a period of hours before giving up. Most transient failures, brief server downtime, and temporary network issues resolve themselves before PayPal exhausts its retries.</p>
<p>If a buyer completes a payment but doesn't receive their download email, the first place to check is the webhook log in your PayPal developer dashboard. It shows every notification PayPal attempted to send, the response your server returned, and whether the delivery succeeded. Cross-reference that with the purchase records in CARL's admin to identify exactly where the process stopped.</p>
<h2>Keeping the Webhook Secure</h2>
<p>CARL verifies every incoming webhook notification against PayPal's API before acting on it. That verification step is non-optional and happens before any purchase record is created or any token is issued. Beyond that, your webhook URL should be treated like any other sensitive endpoint: don't share it publicly, keep your PayPal API credentials securely stored in CARL's settings, and ensure your admin directory is protected as described in <a href="/wiki/site-management/how-carl-handles-admin-authentication.php">How CARL handles admin authentication</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-the-paypal-webhook-works-in-carl-automate-purchase-confi-1780341699.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Download Tokens Work | One-Time Secure Access After Every Purchase | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/downloads/how-carl-download-tokens-work.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/downloads/how-carl-download-tokens-work.php</guid>
    <pubDate>Mon, 01 Jun 2026 15:18:04 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-download-tokens-work-one-time-secure-access-after-1780341264.webp" alt="How CARL&#039;s Download Tokens Work | One-Time Secure Access After Every Purchase | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Download Tokens Work</h1>

<p>Every purchase made through CARL's Downloads module generates a unique download token. That token is the buyer's key to their file. It's tied to a specific purchase, valid for a limited time, and usable a limited number of times. Once it expires or is used up, it stops working. The file stays protected, and access stays controlled.</p><p><img src="/images/how-carl-s-download-tokens-work-one-time-secure-access-after-1780341264.webp" alt="How CARL's Download Tokens Work" class="img-fluid" style=""><br></p>

<h2>What a Token Actually Is</h2>

<p>A download token is a randomly generated string stored in CARL's database alongside the purchase record to which it belongs. When a buyer clicks their download link, the URL contains that token as a parameter. CARL's delivery script reads the token, looks it up in the database, checks its validity, and either streams the file or returns an error. The token is the only thing standing between the request and the file.</p>

<p>Because the token is random and generated fresh for each purchase, there's no way to guess or construct a valid one. A buyer can't share their link with someone else and expect it to work indefinitely because the token has an expiry and a usage limit.</p>

<h2>Expiry and Use Limits</h2>

<p>When you set up a product in CARL's Downloads module, you configure how long the token remains valid and how many times it can be used. A token might be valid for 48 hours and allow three downloads, giving the buyer enough flexibility to download on multiple devices without leaving access open indefinitely. Once the expiry time passes or the use count is reached, whichever comes first, the token stops working.</p>

<p>These limits are practical rather than restrictive. Most buyers download a file once or twice immediately after purchase. The limits exist to prevent a download link from circulating as permanent free access to your product, not to create friction for legitimate customers.</p>

<h2>What Happens When a Token Is Checked</h2>

<p>When the delivery script receives a download request, it runs through a short sequence of checks: does this token exist in the database, is it linked to a completed purchase, has it expired, has it exceeded its use limit? If every check passes, the script increments the use count, streams the file to the browser, and completes the download. If any check fails, the request stops there.</p>

<p>The file never moves to a public URL at any point in this process. It stays in its <a href="/wiki/downloads/how-carl-delivers-files-securely.php">protected directory</a> on the server and is streamed directly to the buyer by the delivery script. The token is consumed in the transaction; the file is not.</p>

<h2>Tokens and the Purchase Record</h2>

<p>Every token is attached to a purchase record in CARL's database. That record contains the buyer's details, the product they purchased, the PayPal payment confirmation, and the issued token. If a buyer contacts you with a download problem, you can look up their purchase record, check the token status, and issue a replacement if needed. The full history is there.</p>

<p>For a complete picture of how tokens fit into the purchase flow from start to finish, see <a href="/wiki/downloads/how-selling-digital-downloads-works.php">How selling digital downloads works in CARL</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-download-tokens-work-one-time-secure-access-after-1780341264.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Preview System Works | Check Your Page Before It Goes Live | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-carls-preview-system-works.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-carls-preview-system-works.php</guid>
    <pubDate>Mon, 01 Jun 2026 12:42:05 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-preview-system-works-check-your-page-before-it-go-1780331874.webp" alt="How CARL&#039;s Preview System Works | Check Your Page Before It Goes Live | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Preview System Works</h1>

<p>Because CARL pages are static PHP files written to disk at publish time, there's no live rendering happening in the background that you can peek at before committing. The preview system bridges that gap: it lets you see what a page will look like before the file is served from your public document root, so you can check layout, content, and formatting without putting an unfinished page in front of visitors or search engines.</p><p><img src="/images/how-carl-s-preview-system-works-check-your-page-before-it-go-1780331874.webp" alt="How CARL's Preview System Works" class="img-fluid" style=""><br></p>

<h2>How the Preview Is Generated</h2>

<p>When you trigger a preview in the CARL editor, CARL runs the same generation process it uses for a full publish. It takes your current content, runs it through the <a href="/wiki/page-management/how-to-build-a-custom-template.php">assigned template</a>, compiles the complete HTML output, and renders it in a preview context. The difference is where the output goes: instead of writing a file to your public directory, CARL renders it in a way that only you can see from inside the admin session.</p>

<p>What you see in the preview is a faithful representation of the published page. The template, the meta tags, the content formatting, the layout: all of it reflects exactly what would be generated if you clicked the publish button. There's no simplified preview mode that strips parts of the template to speed up rendering.</p>

<h2>Checking Before You Publish</h2>

<p>Preview is particularly useful when you're working with a new <a href="/wiki/page-management/how-to-build-a-custom-template.php">custom template</a> for the first time. Building a template involves some iteration: you write the structure, check how the content fits within it, adjust spacing or markup, and check again. Being able to do that without generating live files each time keeps your public directory clean during the development process.</p>

<p>It's also useful for content-heavy pages where formatting matters: long articles, product pages, comparison tables. Seeing the page at full width in its actual template before publishing catches layout issues that aren't obvious in the editor's content field.</p>

<h2>Preview Doesn't Write to Disk</h2>

<p>This is the key distinction. A preview doesn't create a file. Nothing is written to <code>public_html/</code>, no URL becomes accessible, and Google can't crawl it. The preview exists only during your admin session. Once you close the preview or log out, there's nothing left on the server from that preview operation.</p>

<p>The only way to put a page on disk is to click Generate. Preview and Generate are separate actions with separate consequences. You can preview a page as many times as you like, without those previews affecting your live site.</p>

<h2>After the Preview Looks Right</h2>

<p>Once the preview confirms the page looks the way you want it, click Generate to write the file to disk and make the page live. If you've set a <a href="https://carlriedel.com/wiki/page-management/how-to-schedule-a-page-for-future-publishing.php">scheduled publish date</a>, the file will be written automatically at that time instead. Either way, what gets published is exactly what you saw in the preview: the same content, the same template, the same output.</p>

<p>The preview system removes the guesswork from publishing. You write, you check, you generate. The page on disk matches what you approved in the preview, with no surprises when you visit the live URL.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-preview-system-works-check-your-page-before-it-go-1780331874.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Bulk-Regenerate Pages in CARL | Update Every Page on Your Site in One Pass | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-bulk-regenerate-pages.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-bulk-regenerate-pages.php</guid>
    <pubDate>Mon, 01 Jun 2026 12:39:37 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-bulk-regenerate-pages-in-carl-update-every-page-on-yo-1780331600.webp" alt="How to Bulk-Regenerate Pages in CARL | Update Every Page on Your Site in One Pass | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Bulk-Regenerate Pages in CARL</h1>

<p>Every page in CARL is a static PHP file written to disk at publish time. That's what makes them fast and secure, but it also means that when something changes sitewide, like a template update, a navigation change, or a footer edit, the existing files on disk don't update themselves. Bulk regenerate is how you push those changes across every page on your site in a single operation.</p><p><img src="/images/how-to-bulk-regenerate-pages-in-carl-update-every-page-on-yo-1780331600.webp" alt="How to Bulk-Regenerate Pages in CARL" class="img-fluid" style=""><br></p>

<h2>When You Need It</h2>

<p>The most common trigger for a bulk regenerate is a change to a template. If you've updated your site's navigation, changed the footer, added a sitewide script, or modified the layout in any way, the pages already on disk still contain the old template. They won't reflect the update until they're regenerated. Bulk regenerate handles all of them at once rather than requiring you to open and regenerate each page individually.</p>

<p>Other situations that call for a bulk regenerate include updating your site's global settings that feed into page output, changing the default schema structure, or recovering from a situation where files on disk have become out of sync with the records in the database. Any time you need the entire site to reflect a change made in the admin, use bulk regenerate.</p>

<h2>How It Works</h2>

<p>Bulk regenerate runs through every published page record in your database and triggers the generation process for each one in sequence. CARL takes each page's content, runs it through its assigned <a href="/wiki/page-management/how-to-build-a-custom-template.php">template</a>, and overwrites the existing PHP file on disk with a freshly generated version. Pages that haven't changed come out identical to before. Pages affected by the template or settings change come out updated.</p>

<p>The process runs server-side, so you don't need to keep the browser tab open for the entire duration on a small site. On a larger site with hundreds of pages, give it time to complete before navigating away from the admin panel.</p>

<h2>What It Doesn't Do</h2>

<p>Bulk regenerate updates existing pages. It doesn't clean up orphaned files left behind by <a href="https://carlriedel.com/wiki/page-management/how-carl-handles-url-slugs-and-directories.php">slug or directory changes</a>. If you renamed a page's slug before running bulk regenerate, the new file gets written at the new path but the old file at the old path remains on disk. File cleanup after structural changes is a manual step via cPanel's File Manager.</p>

<p>It also doesn't regenerate pages in draft or scheduled status. Only published pages are included in the bulk regenerate pass. Scheduled pages will be generated at their publish time as normal.</p>

<h2>Making It Part of Your Workflow</h2>

<p>Getting into the habit of running a bulk regenerate after any sitewide change removes the risk of pages falling out of sync with your current template or settings. It takes a few seconds on a small site and a few minutes on a large one. The cost is low; the alternative, discovering that half your pages are showing an outdated layout because you forgot to regenerate them, is considerably more frustrating.</p>

<p>For sites built around a content publishing schedule, a bulk regeneration after each batch of edits is a clean way to confirm that everything on disk matches everything in the admin. Think of it as a final consistency check before you consider a work session done.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-bulk-regenerate-pages-in-carl-update-every-page-on-yo-1780331600.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Create a Page in CARL | From Blank Editor to Live PHP File | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-create-a-page.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-create-a-page.php</guid>
    <pubDate>Mon, 01 Jun 2026 12:31:22 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-create-a-page-in-carl-from-blank-editor-to-live-php-f-1780324152.webp" alt="How to Create a Page in CARL | From Blank Editor to Live PHP File | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Create a Page in CARL</h1>

<p>Creating a page in CARL is a linear process: fill in the fields, click Generate, and a finished PHP file lands on your server. There's no draft mode to navigate, no publish/update confusion, and no plugin adding fields you didn't ask for. You fill in what matters, generate the file, and the page is live.</p><p><img src="/images/how-to-create-a-page-in-carl-from-blank-editor-to-live-php-f-1780324152.webp" alt="How to Create a Page in CARL" class="img-fluid" style=""><br></p>

<h2>Open the Page Editor</h2>

<p>From the CARL admin panel, go to Pages and click Add New Page. The page editor opens with a set of fields organized into two areas: the main content fields and the SEO/meta fields. Everything that ends up in the published PHP file gets set here.</p>

<h2>The Core Fields</h2>

<p>The Title field sets the page's <code><title></code> tag and is what appears in browser tabs and search results. The H1 field is separate, so your search-optimized title and your on-page heading can differ if you need them to. Fill both deliberately: the title is for Google, the H1 is for the reader arriving on the page.</p>

<p>The Slug field sets the filename. If you type <code>my-page</code>, CARL generates <code>my-page.php</code>. The Directory field sets where that file is placed on your server. A slug of <code>best-php-cms</code> with a directory of <code>blog</code> produces a file at <code>public_html/blog/best-php-cms.php</code>, accessible at <code>yourdomain.com/blog/best-php-cms.php</code>.</p>

<p>The Template field sets which layout the page uses. CARL ships with a Bootstrap full-width template, and you can <a href="/wiki/page-management/how-to-build-a-custom-template.php">build custom templates</a> for different page types. Whichever template you assign, its structure is baked into the generated file at publish time.</p>

<h2>The Content Field</h2>

<p>The main content area accepts HTML. You write or paste your content directly, with full control over the markup. There's no block editor abstracting things behind a visual interface. What you enter in the content field appears in the generated file, with nothing added or removed by a plugin.</p>

<h2>The Meta Fields</h2>

<p>Below the main content, you'll find fields for the meta description, canonical URL, and Open Graph tags. CARL writes these directly into the <code><head></code> of the generated file. You can also use the AI Schema Generator to produce JSON-LD structured data for the page, which gets embedded in the same file at generation time.</p>

<h2>Generating the Page</h2>

<p>When you click Generate, CARL takes everything in the editor, runs it through the assigned template, and writes a complete PHP file to the directory you specified. The file is immediately accessible at its URL. There's no cache to warm, no CDN to purge, and no propagation delay. The file exists on disk, and your server serves it.</p>

<p>If you go back and edit the page later, clicking Generate again overwrites the existing file with the updated version. The old file is replaced. The URL stays the same. That's the full cycle: create, generate, edit, regenerate.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-create-a-page-in-carl-from-blank-editor-to-live-php-f-1780324152.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>What Happens When You Publish a Page in CARL | See Exactly What the Generator Does | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/what-happens-when-you-publish-a-page.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/what-happens-when-you-publish-a-page.php</guid>
    <pubDate>Mon, 01 Jun 2026 12:29:18 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/what-happens-when-you-publish-a-page-in-carl-see-exactly-wha-1780317174.webp" alt="What Happens When You Publish a Page in CARL | See Exactly What the Generator Does | Carl Riedel" style="max-width:100%;height:auto;">
<h1>What Happens When You Publish a Page in CARL</h1>
<p>Most CMS platforms are vague about what "publishing" actually does. You click a button, the page appears on the site, and the internals stay hidden. CARL works differently. Publishing triggers a real, traceable build process with a concrete output you can find on your server. Here's exactly what happens.</p><p><img src="/images/what-happens-when-you-publish-a-page-in-carl-see-exactly-wha-1780317174.webp" alt="What Happens When You Publish a Page in CARL" class="img-fluid" style=""><br></p>
<h2>Step 1: CARL Reads Your Page Record</h2>
<p>When you hit Publish, CARL pulls your page data from the database. That includes the title, slug, directory, content, meta description, canonical override (if set), Open Graph fields, schema type, schema data, PHP snippet, head snippet, and the template you've assigned to the page.</p>
<p>This is the only moment the database is involved in the process. Everything after this point works from the data that was just retrieved.</p>
<h2>Step 2: CARL Loads the Template</h2>
<p>CARL opens the template file you selected for the page. Templates live in <code>admin/tpl/</code> and carry the <code>.tpl</code> extension. The template is the structural wrapper for your page: the HTML document, the Bootstrap grid, the PHP includes for your header, footer, and navigation. It contains placeholder tokens where your content will be inserted.</p>
<p>The available tokens are <code>{{TITLE}}</code>, <code>{{META}}</code>, <code>{{BODY}}</code>, <code>{{HEAD_EXTRA}}</code>, and <code>{{SNIPPET}}</code>. Each one marks a specific injection point in the template.</p>
<h2>Step 3: CARL Builds the Meta Block</h2>
<p>Before substituting tokens, CARL assembles the full meta block for the page. This includes the meta description tag, the canonical URL tag, Open Graph tags (og:title, og:description, og:image, og:type), the Twitter card tag, and the JSON-LD schema block. The schema is built from the schema type and the data stored for the page, using your organization settings in the Settings panel to fill in publisher details.</p>
<p>The entire meta block is generated as a single string and injected at the <code>{{META}}</code> token position in the template. This is what makes every CARL page SEO-complete out of the box, with no SEO plugin required.</p>
<h2>Step 4: CARL Substitutes the Tokens</h2>
<p>With the meta block ready, CARL runs through the template, replacing each token with its corresponding value. The page title goes in at <code>{{TITLE}}</code>. The meta block goes in at <code>{{META}}</code>. Your page content goes in at <code>{{BODY}}</code>. Any head snippet you added goes in at <code>{{HEAD_EXTRA}}</code>. Your PHP snippet goes in at <code>{{SNIPPET}}</code>.</p>
<p>The result is a complete, valid HTML/PHP document containing everything the page needs. No tokens remain in the output. No placeholders. Just finished code.</p>
<h2>Step 5: CARL Writes the File to Disk</h2>
<p>CARL takes the assembled output and writes it to your web root as a <code>.php</code> file. The file path is determined by the directory and slug you set in the page editor. A page with directory <code>wiki/architecture</code> and slug <code>what-happens-when-you-publish-a-page</code> gets written to exactly that path on your server.</p>
<p>That file is now live. The next visitor to hit that URL receives the prebuilt file directly from the server. No build step, no database query, no runtime assembly. The work is already done.</p>
<h2>Step 6: CARL Updates the Database Record</h2>
<p>After writing the file, CARL updates the page record in the database to mark it as generated, stores the file path, and records the generation timestamp. This is what allows the <a href="/wiki/seo/how-carls-site-health-checker-works.php">Site Health checker</a> to verify that the file on disk matches what's expected and flag any drift.</p>
<h2>What "Regenerate" Does</h2>
<p>Regenerating a page runs the exact same process from the top. CARL reads the current page record, builds the meta block, substitutes the tokens, and overwrites the existing file on disk. This is useful after changing your site template, updating your organization settings, or editing any include file that affects the page output.</p>
<p>The <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> option in Site Structure does this for every published page in one pass. It's the right tool any time a sitewide change needs to propagate across all your pages at once.</p>
<h2>Nothing Is Left to Chance</h2>
<p>Every published page on a CARL site is the direct output of a deterministic build process. You can open any generated file in a text editor and read exactly what was produced. The meta tags are there. The schema is there. The content is there. What you see in the file is what Google indexes and what your visitors receive.</p>
<p>That transparency is part of what makes CARL reliable. There's no runtime behavior to guess at, no plugin interaction to worry about, and no mystery between what you published and what ends up on the page.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/what-happens-when-you-publish-a-page-in-carl-see-exactly-wha-1780317174.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Use the PHP Snippet Field in CARL | Add Custom PHP Logic to Any Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-use-the-php-snippet-field.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-use-the-php-snippet-field.php</guid>
    <pubDate>Mon, 01 Jun 2026 12:01:14 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-use-the-php-snippet-field-in-carl-add-custom-php-logi-1780327527.webp" alt="How to Use the PHP Snippet Field in CARL | Add Custom PHP Logic to Any Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Use the PHP Snippet Field in CARL</h1>

<p>The PHP Snippet field lets you add custom PHP code to an individual page without touching the template. Whatever you enter in this field is written directly into the generated PHP file at a specific position and executed when the page loads. It's a per-page escape hatch for logic that belongs on a single page, keeping that logic out of the template, where it would affect every page using that layout.</p><p><img src="/images/how-to-use-the-php-snippet-field-in-carl-add-custom-php-logi-1780327527.webp" alt="How to Use the PHP Snippet Field in CARL" class="img-fluid" style=""><br></p>

<h2>Where the Snippet Appears in the Generated File</h2>

<p>When CARL generates a page with a PHP snippet attached, the snippet code is written into the PHP file at the top of the page, before the HTML output begins. This means the code runs before anything is sent to the browser, which is the correct position for any PHP that needs to set variables, check conditions, handle redirects, or interact with sessions. Code that needs to run before output belongs here.</p>

<p>The rest of the <a href="https://carlriedel.com/wiki/page-management/how-carls-page-templates-work.php">page template</a> and content render normally below it. The snippet and the template are independent: changing the snippet on one page has no effect on any other page, even if they share the same template.</p>

<h2>What You Can Use It For</h2>

<p>The PHP Snippet field is useful anywhere you need page-specific server-side logic. A common use is access control: checking whether a visitor is logged in as a member before rendering the page content, then redirecting to a login page if not. Another use is to set custom variables that are referenced elsewhere in the template or content. You can also use it to include additional PHP files specific to that page, load custom configuration, or run any other server-side logic the page needs.</p>

<p>For pages in the <a href="/wiki/members/how-carls-members-area-works.php">Members Area</a>, the PHP snippet is where member authentication checks typically live. The snippet runs first, confirms the visitor has a valid session, and either lets the page render or sends them elsewhere. All of that happens before a single line of HTML is output.</p>

<h2>What to Keep Out of the Snippet Field</h2>

<p>The PHP Snippet field is for logic, not for content or markup. Don't use it to output HTML: that belongs in the content field. Don't use it for sitewide functionality that every page needs: that belongs in the template or in an include file. The snippet is deliberately scoped to a single page. Using it for things that should be global creates maintenance work when you need to change that logic later, because you'd have to update it on every page individually.</p>

<p>Keep snippets short, specific, and purposeful. A snippet that runs to dozens of lines is usually a sign the logic belongs in a dedicated include file that the snippet calls with a single <code>require</code> or <code>include</code> statement. That way, the logic lives in one place, and the snippet stays readable.</p>

<h2>Snippets Are Baked In at Generation</h2>

<p>Like everything else in CARL, the PHP snippet is written into the generated file at publish time. If you update the snippet and want the change to take effect, you need to regenerate the page. The updated code will be in the new file; the old file had the old code. This is the same behavior as <a href="https://carlriedel.com/wiki/page-management/how-carls-page-templates-work.php">template changes</a>: nothing updates on disk until you generate.</p>

<p>For pages where the snippet contains access control logic tied to the <a href="/wiki/members/how-carls-members-area-works.php">members' system</a>, this means the protection is part of the static file itself. There's no dynamic lookup on each visit to check permissions against a ruleset. The check is in the file, runs immediately when the page is requested, and adds negligible overhead compared to a database-driven permission system.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-use-the-php-snippet-field-in-carl-add-custom-php-logi-1780327527.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Build a Custom Template in CARL | Full Layout Control Without a Theme System | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-build-a-custom-template.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-build-a-custom-template.php</guid>
    <pubDate>Mon, 01 Jun 2026 11:47:05 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-build-a-custom-template-in-carl-full-layout-control-w-1780326559.webp" alt="How to Build a Custom Template in CARL | Full Layout Control Without a Theme System | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Build a Custom Template in CARL</h1>

<p>CARL's default Bootstrap template covers a lot of ground, but at some point you'll want a layout that's entirely your own. Building a custom template in CARL is straightforward: you write a PHP file, drop in CARL's variables where the dynamic content belongs, save it to the templates directory, and it's available immediately in the page editor. There's no theme system to register with, no framework to extend, no activation step.</p><p><img src="/images/how-to-build-a-custom-template-in-carl-full-layout-control-w-1780326559.webp" alt="How to Build a Custom Template in CARL" class="img-fluid" style=""><br></p>

<h2>What a Template File Looks Like</h2>

<p>A CARL template is a standard PHP file containing your full HTML document structure. The <code></code> declaration, the <code></code> section, navigation, content area, footer: all of it lives in the template. CARL provides a set of PHP variables that get populated with page-specific data at generation time. You place those variables where you want them in the markup.</p>

<p>The core variables you'll use in almost every template are <code>$page_title</code> for the title tag, <code>$meta_description</code> for the meta description, <code>$canonical_url</code> for the canonical tag, <code>$h1</code> for the page heading, and <code>$content</code> for the main body content. Schema markup, Open Graph tags, and other head elements have their own variables that slot into the <code></code> section the same way.</p>

<h2>A Basic Template Structure</h2>

<p>At minimum, a working CARL template needs a valid HTML document structure with the title variable in the head, the content variable in the body, and the meta variables in their correct positions. Everything else, including navigation, sidebars, footers, external CSS, and JavaScript, is markup you write yourself. CARL doesn't impose any structure beyond the variable conventions.</p>

<p>This means you can build templates that look nothing like the default Bootstrap layout. A minimal single-column article template, a landing page with no navigation, and a product page with a custom sidebar: all of these are just PHP files with different HTML structures that wrap the same CARL variables.</p>

<h2>Saving the Template</h2>

<p>Once your template file is written, save it to the <code>templates/</code> directory inside your CARL install. The filename becomes the template name that appears in the dropdown in the page editor. Name it something descriptive: <code>landing-page.php</code>, <code>wiki-article.php</code>, <code>members-content.php</code>. The name is for your reference, so pick something that makes the purpose obvious when you're selecting it from a list.</p>

<p>No restart, no cache clear, no admin setting to update. Save the file, and it's available immediately.</p>

<h2>Updating a Custom Template</h2>

<p>When you need to change the layout, open the template file, make your edits, and save. The change will appear on all pages using that template the next time those pages are regenerated. For sitewide changes, use <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> to push the updated layout across every affected page in one pass.</p>

<p>Because the template is baked into the generated PHP file at publish time, pages that haven't been regenerated will still show the old layout. That's not a bug: it's the direct consequence of pages being static files rather than assembled on demand. The bulk regenerate tool exists precisely for this situation.</p>

<h2>Reusing Templates Across Installs</h2>

<p>A template file has no dependencies on a specific CARL install beyond the variable conventions. If you build a template you're happy with on one site, copy the file to the templates directory of any other CARL install, and it works. Over time, you'll build a library of templates you can carry from project to project, which is a significant time saver if you're building sites for clients regularly.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-build-a-custom-template-in-carl-full-layout-control-w-1780326559.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles URL Slugs and Directories | Clean URLs Built Into Every Page | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-carl-handles-url-slugs-and-directories.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-carl-handles-url-slugs-and-directories.php</guid>
    <pubDate>Mon, 01 Jun 2026 11:21:57 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-url-slugs-and-directories-clean-urls-built--1780327029.webp" alt="How CARL Handles URL Slugs and Directories | Clean URLs Built Into Every Page | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles URL Slugs and Directories</h1>

<p>Every page you create in CARL has two fields that determine where it lives on your server and what its URL looks like: the Slug field and the Directory field. Get these right and your site structure is clean, logical, and exactly what you intended. They're simple fields with significant consequences, so it's worth understanding precisely what each one does.</p><p><img src="/images/how-carl-handles-url-slugs-and-directories-clean-urls-built--1780327029.webp" alt="How CARL Handles URL Slugs and Directories" class="img-fluid" style=""><br></p>

<h2>The Slug Field</h2>

<p>The slug is the filename of the generated PHP file, without the extension. If you type <code>best-php-cms</code> in the Slug field, CARL generates a file called <code>best-php-cms.php</code>. That file is what visitors and search engines access. The slug should be lowercase, hyphen-separated, and descriptive of the page content. No spaces, no special characters, no uppercase letters.</p>

<p>The slug also forms the last segment of the page's URL. A slug of <code>best-php-cms</code> in a directory of <code>blog</code> produces the URL <code>yourdomain.com/blog/best-php-cms.php</code>. Choose slugs the same way you'd choose them for any SEO-conscious site: lead with the keyword, keep it concise, make it readable.</p>

<h2>The Directory Field</h2>

<p>The Directory field sets the folder path where CARL writes the <a href="/wiki/architecture/how-carl-generates-static-php-files.php">generated PHP file</a>. If you leave it empty, the file goes into <code>public_html/</code> at the root of your domain. If you type <code>blog</code>, the file goes into <code>public_html/blog/</code>. Type <code>wiki/page-management</code> and the file lands in <code>public_html/wiki/page-management/</code>.</p>

<p>CARL creates the directory if it doesn't already exist. You don't need to create folders in cPanel before generating pages into them. The first page you generate into a new directory creates the folder automatically.</p>

<h2>How Slug and Directory Combine</h2>

<p>The full URL of any CARL page is straightforward to predict: it's your domain, followed by the directory path, followed by the slug with the <code>.php</code> extension. A page with slug <code>how-carl-handles-url-slugs</code> in directory <code>wiki/page-management</code> lives at <code>yourdomain.com/wiki/page-management/how-carl-handles-url-slugs.php</code>.</p>

<p>There's no URL rewriting involved, no permalink settings to configure, no .htaccess rules required for clean URLs. The file exists at exactly the path described by the URL. What you set in the editor is what you get on <a href="/wiki/architecture/how-carl-stores-pages-on-your-server.php">disk</a>.</p>

<h2>Changing a Slug After Publishing</h2>

<p>If you change a slug after a page has already been generated, CARL creates a new file at the new path when you regenerate. The old file stays on disk at the original path. CARL doesn't delete it automatically because deleting files without explicit confirmation is a risk no CMS should take on your behalf.</p>

<p>If you're changing a slug on a live page, the practical steps are: update the slug in the editor, regenerate the page, then manually delete the old file via cPanel's File Manager. If other pages link to the old URL, update those links and use <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> to apply the changes across all affected pages at once. The process is deliberate and a little manual, which is appropriate: changing a live URL has SEO consequences that warrant caution.</p>

<h2>Planning Your Directory Structure</h2>

<p>The Directory field gives you full control over how your site is organized. Group related content into consistent directories from the start. A wiki goes in <code>wiki/</code>, blog posts in <code>blog/</code>, use case pages in <code>use-cases/</code>. A clean directory structure makes the site easier to manage and navigate, and clearer to search engines about how your content is organized.</p>

<p>Changing a directory structure after a site is live carries the same implications as changing slugs: old URLs break unless you handle the file cleanup. Plan the structure before you start generating pages, and you won't need to revisit it. For a deeper look at how files are physically organized on your server, see <a href="/wiki/architecture/how-carl-stores-pages-on-your-server.php">How CARL stores pages on your server</a>.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-url-slugs-and-directories-clean-urls-built--1780327029.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL's Page Templates Work | Separate Your Layout from Your Content | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-carls-page-templates-work.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-carls-page-templates-work.php</guid>
    <pubDate>Mon, 01 Jun 2026 10:50:24 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-s-page-templates-work-separate-your-layout-from-you-1780325344.webp" alt="How CARL&#039;s Page Templates Work | Separate Your Layout from Your Content | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL's Page Templates Work</h1>

<p>Every page in CARL is built from two things: the content you write in the editor and the template you assign to it. The template is the layout wrapper. It controls everything outside the content area: the HTML structure, the head section, navigation, footer, and any surrounding markup. When you generate a page, CARL combines your content with the assigned template and writes the result to disk as a single, self-contained PHP file.</p><p><img src="/images/how-carl-s-page-templates-work-separate-your-layout-from-you-1780325344.webp" alt="How CARL's Page Templates Work" class="img-fluid" style=""><br></p>

<h2>What a Template Actually Is</h2>

<p>A CARL template is a PHP file stored in the templates directory on your server. It contains your site's standard HTML structure with designated areas where CARL injects page-specific data at generation time: the title, meta description, canonical URL, schema markup, Open Graph tags, the H1, and the main content. The template provides the frame; the page editor provides everything that goes inside it.</p>

<p>This separation keeps your site consistent without any extra effort. Change the template once, and every page using it gets the updated layout the next time you regenerate. You're not hunting through individual pages to update a nav link or a footer copyright year.</p>

<h2>The Default Template</h2>

<p>CARL ships with a Bootstrap full-width template ready to use from the moment you install it. For many sites, particularly content-heavy pages and wiki-style articles, this template covers everything you need without modification. The Bootstrap framework is loaded from the CDN, so there's no extra file weight on your server, and the layout is responsive out of the box.</p>

<h2>Assigning a Template to a Page</h2>

<p>In the page editor, the Template field is a dropdown that lists every template available on your installation. You select the one you want for that page, and it gets baked in at the time of generation. Different pages can use different templates. A homepage, a blog article, a product page, and a members-only content page can each use a different layout while sharing the same CARL install and database.</p>

<p>Template assignment is per-page, so there's no site-wide default you're locked into. If you want every page in the <code>wiki/</code> directory using a sidebar layout and every page in <code>blog/</code> using a full-width layout, you assign templates accordingly when creating each page.</p>

<h2>Templates Are Baked In at Generation</h2>

<p>This is the part that catches people coming from WordPress. In WordPress, a template change takes effect immediately on every page because the template is applied at request time. In CARL, the template is applied at the time of generation. The generated PHP file already contains the full layout.</p><p>There's no template lookup happening when a visitor lands on the page.</p>

<p>The practical implication: if you update a template, pages using that template don't automatically reflect the change. You need to regenerate them. For sitewide template changes, CARL's <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> tool handles this in one pass. It's an extra step, but it's the direct cost of having pages that load without any runtime overhead.</p>

<h2>Building Custom Templates</h2>

<p>If the default template doesn't fit your design, you can <a href="/wiki/page-management/how-to-build-a-custom-template.php">build your own</a>. A custom template is just a PHP file following CARL's variable conventions. You write the HTML structure you want, drop in the CARL variables where your dynamic content belongs, save the file to the templates directory, and it appears in the Template dropdown immediately. No registration step, no plugin activation, no theme system to navigate.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-s-page-templates-work-separate-your-layout-from-you-1780325344.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How to Schedule a Page for Future Publishing | Set It and Let CARL Publish Automatically | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/page-management/how-to-schedule-a-page-for-future-publishing.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/page-management/how-to-schedule-a-page-for-future-publishing.php</guid>
    <pubDate>Mon, 01 Jun 2026 10:46:12 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-to-schedule-a-page-for-future-publishing-set-it-and-let--1780324978.webp" alt="How to Schedule a Page for Future Publishing | Set It and Let CARL Publish Automatically | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How to Schedule a Page for Future Publishing</h1>

<p>CARL's scheduled publishing lets you write a page today and have it go live at a specific date and time, even if you're not at your desk. The scheduler is built into CARL — no plugin, no cron job you have to configure manually, no third-party service standing between your content and your server.</p><p><img src="/images/how-to-schedule-a-page-for-future-publishing-set-it-and-let--1780324978.webp" alt="How to Schedule a Page for Future Publishing" class="img-fluid" style=""><br></p>

<h2>How to Set a Scheduled Date</h2>

<p>In the page editor, you'll find the scheduling field alongside the other publish options. Set the date and time you want the page to go live, then save the page without clicking Generate. CARL stores it in the database with a scheduled status and the target publish time attached to the record.</p>

<p>Until that time arrives, the PHP file doesn't exist on disk. The page isn't live, the URL returns nothing, visitors can't reach it, and search engines have nothing to crawl. It exists only as a record in your admin panel, waiting for its publish time.</p>

<h2>What Happens at Publish Time</h2>

<p>CARL's <a href="/wiki/site-management/how-carls-scheduler-works.php">built-in scheduler</a> runs in the background, checking for pages that are due to go live. When the scheduled time arrives, it triggers the same generation process as a manual publish: your content gets pulled from the database, run through the assigned template, compiled into a complete PHP file, then written to the directory you specified.</p>

<p>From that point, the page is live at its URL. You don't need to be logged into the admin for this to happen. The scheduler runs independently of any active session, so your page publishes whether you're working, sleeping, or on a flight with no wifi.</p>

<h2>Editing a Scheduled Page Before It Goes Live</h2>

<p>A scheduled page isn't locked. You can open it in the editor, make changes, update the publish date, or cancel the schedule entirely at any point before generation happens. If you change the content, those changes will be reflected in the version generated at publish time. Whatever is saved in the record when the scheduler fires is what ends up on disk.</p>

<p>If you decide you want the page to go live immediately rather than wait, click Generate. The file is written to disk immediately; the scheduled date becomes irrelevant, and the page behaves like any other published page from that point forward.</p>

<h2>After the Page Goes Live</h2>

<p>Once a scheduled page has been generated, it's just a PHP file on your server. The scheduling mechanism played its role and stepped back entirely. You can edit the page, regenerate it, link to it from other pages, submit it to Google Search Console, and include it in your sitemap. Nothing about how it was published affects what it is now.</p>

<p>This is worth stating clearly because some CMS platforms attach metadata or status flags to scheduled content even after it has been published. In CARL, the generated file has no memory of how it got there. It's a static PHP file, identical to one you published manually five minutes after writing it.</p>

<h2>Why This Matters for Content Planning</h2>

<p>Scheduled publishing is most useful when you're building content in batches. If you're preparing for a site launch, you can write thirty pages in a week, assign each one a publish date, then let CARL roll them out over the following month while you move on to other work. If you're running a campaign where pages need to appear on specific dates, you set the dates once, and the campaign runs on its own.</p>

<p>It also removes the pressure of writing to a deadline. You can finish a page days before it needs to go live, schedule it, close the tab, and trust that it will appear exactly when it should. The content is done. The timing is handled. There's nothing left to manage.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-to-schedule-a-page-for-future-publishing-set-it-and-let--1780324978.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Handles Multiple Domains on One Server | Multi-Domain Setup on cPanel | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/how-carl-handles-multiple-domains-on-one-server.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/how-carl-handles-multiple-domains-on-one-server.php</guid>
    <pubDate>Mon, 01 Jun 2026 10:36:25 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-handles-multiple-domains-on-one-server-multi-domain-1780322757.webp" alt="How CARL Handles Multiple Domains on One Server | Multi-Domain Setup on cPanel | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Handles Multiple Domains on One Server</h1>

<p>If you run more than one website, chances are they all live on the same cPanel hosting account. CARL is built for exactly that setup. You install a separate CARL instance for each domain, each completely independent and managing its own site without touching the others.</p><p><img src="/images/how-carl-handles-multiple-domains-on-one-server-multi-domain-1780322757.webp" alt="How CARL Handles Multiple Domains on One Server" class="img-fluid" style=""><br></p>

<h2>One Install Per Domain</h2>

<p>CARL runs inside a domain's document root. For your primary domain, that's <code>public_html/</code>. For addon domains on cPanel, each one gets its own document root, typically something like <code>public_html/clientdomain.com/</code> or a directory at the same level as <code>public_html/</code>. You install CARL into that directory the same way you install it anywhere: upload the files, create a database, run the setup wizard.</p>

<p>Each install has its own database, admin panel, settings, and published pages on disk. There's no shared state between installs. If you're managing five domains, you have five CARL installs, each completely self-contained.</p>

<h2>Why Separation Matters</h2>

<p>Isolation between sites is a feature, not a limitation. When sites share a single application instance (or, worse, a single WordPress multisite install), a problem in one site can affect every site on the account. CARL's per-domain approach keeps every site in its own compartment.</p>

<p>The same logic applies to the <a href="/wiki/architecture/how-carl-generates-static-php-files.php">static file architecture</a>. Each domain's published pages are pre-built files sitting in that domain's document root. A visitor to domain A never triggers any code that touches domain B. The sites don't interact at the application level at all.</p>

<h2>cPanel Handles the Domain Routing</h2>

<p>CARL doesn't manage domains. cPanel does. You add addon domains through cPanel's Addon Domains tool, assign each one a document root, point the domain's DNS to your server, and CARL is installed into that root. From CARL's perspective, it simply knows where it lives on the server and operates within that directory.</p>

<p>Subdomains work the same way. If you want <code>blog.yourdomain.com</code> running as a separate CARL site from <code>yourdomain.com</code>, you create the subdomain in cPanel, give it its own document root, and install CARL into it. The setup is identical to any other domain.</p>

<h2>One License, Unlimited Installs</h2>

<p>A single CARL license covers unlimited domain installs. You pay once and run CARL on as many domains as you manage. There's no per-site fee, no tier that unlocks additional domains, and no annual renewal to maintain your right to run it.</p>

<p>For freelancers and agency builders who maintain sites for multiple clients, that pricing structure changes the math significantly. The <a href="/wiki/architecture/how-carl-stores-pages-on-your-server.php">files belong to each domain</a>, so you can hand off a completed site to a client, walk away, and the pages keep running without any ongoing platform costs.</p>

<h2>Admin Access Per Site</h2>

<p>Each CARL install has its own admin panel at <code>yourdomain.com/admin/</code> (or wherever you place the admin directory during setup). Logging into the admin for one domain gives you no access to any other domain's admin. Credentials are per-install, stored in that install's <code>admin/config.php</code>.</p>

<p>If you regularly manage several domains, you'll have several admin URLs bookmarked. That's the practical reality of the per-install model. The upside is that each site's admin is completely separate; there's no global dashboard where a misconfiguration can affect every domain at once.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-handles-multiple-domains-on-one-server-multi-domain-1780322757.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Stores Pages on Your Server | Your Files, Your Server, No Platform Lock-In | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/how-carl-stores-pages-on-your-server.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/how-carl-stores-pages-on-your-server.php</guid>
    <pubDate>Mon, 01 Jun 2026 09:19:15 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-stores-pages-on-your-server-your-files-your-server--1780317535.webp" alt="How CARL Stores Pages on Your Server | Your Files, Your Server, No Platform Lock-In | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Stores Pages on Your Server</h1>
<p>Most website platforms store your content in their databases, on their servers, and within their infrastructure. You get a login, a dashboard, and a URL. But the actual files? Those belong to the platform. CARL does the opposite. Every page you publish is a real file on your server, in your web root, under your control.</p>
<p>That's not a marketing claim. It's a direct consequence of <a href="/wiki/architecture/how-carl-generates-static-php-files.php">how CARL generates pages</a>. Understanding where those files live and how they're organized helps you manage your site with confidence.</p><p><img src="/images/how-carl-stores-pages-on-your-server-your-files-your-server--1780317535.webp" alt="How CARL Stores Pages on Your Server" class="img-fluid" style=""><br></p>
<h2>Your Web Root Is the Filing Cabinet</h2>
<p>On a standard cPanel hosting account, your public web root is <code>public_html</code>. That's the folder your domain points to. When CARL generates a page, it writes the finished file into that folder at the exact path defined by the page's directory and slug settings.</p>
<p>There's no hidden layer, no abstraction, no proprietary file format. Open your cPanel File Manager, browse to the&nbsp;<code>public_html directory</code>, and every published CARL page is right there as a <code>.php</code> file. You can read it, download it, copy it, or move it just like any other file on your server.</p>
<h2>How the Directory Structure Works</h2>
<p>CARL mirrors your page's directory and slug settings directly into the file path. If you set a page's directory to <code>wiki/architecture</code> and its slug to <code>how-carl-stores-pages-on-your-server</code>, CARL creates the necessary folders and writes the file to:</p>
<p><code>public_html/wiki/architecture/how-carl-stores-pages-on-your-server.php</code></p>
<p>The URL your visitors use maps directly to that file path. No routing table, no URL rewriting required for page delivery. The structure you see in your File Manager is the structure your visitors navigate.</p>
<h2>Subdirectories Are Created Automatically</h2>
<p>You don't need to create folders manually before publishing a page into a new directory. CARL checks whether the target directory exists and creates it if it doesn't. Set the directory field, publish the page, and the folder structure appears on disk.</p>
<p>This makes building out nested content structures, such as a wiki silo, straightforward. Create the pages in the editor with the correct directory settings, and CARL handles the folder creation as part of the publish process.</p>
<h2>The Admin Panel Is Separate</h2>
<p>It's worth being clear about what lives where. Your published pages live in <code>public_html</code>, accessible to the world. The CARL admin panel lives in <code>public_html/admin</code>, protected by login. The database holds your page records, settings, and module data, but it's never involved in serving a live page to a visitor.</p>
<p>These three layers are deliberately separate. The admin panel can go offline, and your published pages keep working. The database can be unavailable, and your published pages keep working. The files on disk are self-contained and independent of everything else.</p>
<h2>What This Means If You Ever Leave</h2>
<p>This is one of the most important practical consequences of how CARL stores pages. If you ever decide to move to a different host, switch to a different system, or simply shut down the admin panel, every page you've ever published keeps working exactly as it did before. The files are on your server. Copy them to a new host, and they work there too.</p>
<p>There is no export process, no data migration, no platform asking you to pay for access to your own content. Your site is already a collection of files. You already own them.</p>
<h2>Finding Your Files in cPanel</h2>
<p>If you want to verify where a specific page lives, log in to cPanel and open the File Manager. Navigate to <code>public_html</code> and browse to the directory matching the page's directory setting. The file will be there with the page's slug as the filename and <code>.php</code> as the extension.</p>
<p>You can also check the generated path directly in the CARL admin. Open any page for editing, and the stored file path is shown in the page record. That path is what CARL wrote to disk when you last published or regenerated the page.</p>
<h2>A Note on Regeneration</h2>
<p>If you rename a page's slug or move it to a different directory, CARL writes the new file at the new path. The old file at the old path is not automatically deleted. It's good practice to manually remove the old file via cPanel File Manager after moving a page, and to set up a redirect from the old URL to the new one so existing links and any indexed URLs don't land on a dead page.</p>
<p>The <a href="/wiki/page-management/how-to-bulk-regenerate-pages.php">bulk regenerate</a> tool handles the rebuild but not the cleanup of old paths. That part stays in your hands, which is exactly where it should be.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-stores-pages-on-your-server-your-files-your-server--1780317535.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Why CARL Pages Load So Fast | Pre-Built Files, Zero Runtime Overhead | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/why-carl-pages-load-so-fast.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/why-carl-pages-load-so-fast.php</guid>
    <pubDate>Mon, 01 Jun 2026 09:17:30 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/why-carl-pages-load-so-fast-pre-built-files-zero-runtime-ove-1780315578.webp" alt="Why CARL Pages Load So Fast | Pre-Built Files, Zero Runtime Overhead | Carl Riedel" style="max-width:100%;height:auto;">
<h1>Why CARL Pages Load So Fast</h1>
<p>Speed on the web usually comes down to one thing: how much work does the server have to do before it can send anything to the browser? For most CMS platforms, that answer is "quite a lot." For CARL, the answer is "almost nothing."</p>
<p>That difference is not a configuration trick or a hosting upgrade. It's a fundamental architectural choice baked into how CARL works from the ground up.</p><p><img src="/images/why-carl-pages-load-so-fast-pre-built-files-zero-runtime-ove-1780315578.webp" alt="Why CARL Pages Load So Fast" class="img-fluid" style=""><br></p>
<h2>What WordPress Does on Every Visit</h2>
<p>When someone visits a WordPress page, the server starts from scratch. It connects to the database, runs a query to fetch the post content, loads PHP, fires dozens of hooks, executes every active plugin, assembles the HTML, and then finally sends the result to the browser. Every single visit triggers that entire chain.</p>
<p>On a quiet site with fast hosting, this might take 800ms to 1.5 seconds. Add a few more plugins, a shared server under load, or a traffic spike, and that number climbs fast. Caching plugins exist to paper over this problem by storing a pre-built version of the output. Which is, ironically, exactly what CARL does by default.</p>
<h2>What CARL Does Instead</h2>
<p>CARL builds the page once, when you publish it, and saves the finished file to disk. When a visitor arrives, the server just hands them that file. No database connection, no PHP execution, no plugin stack. The heavy lifting was done at publish time, not at visit time.</p>
<p>This is what <a href="/wiki/architecture/how-carl-generates-static-php-files.php">CARL's static file generation</a> produces: a ready-to-serve PHP file that lives in your web root. Your server's job on each visit is trivially simple, and trivially simple jobs are fast.</p>
<h2>What "Under a Second" Actually Looks Like</h2>
<p>On a standard shared cPanel hosting plan, a CARL page typically delivers its first byte in under 100 milliseconds. Full-page load, including images and assets, takes well under a second for most pages. That's not on a dedicated server with a CDN in front of it. That's on ordinary shared hosting that costs a few dollars a month.</p>
<p>The reason is simple physics. Serving a file from disk is one of the most optimized operations a web server performs. Apache and Nginx have been doing it for decades. Give either of them a static file and they will deliver it faster than any dynamically assembled page can match.</p>
<h2>Google Notices</h2>
<p>Page speed is a confirmed ranking signal. Core Web Vitals (Largest Contentful Paint, Interaction to Next Paint, Cumulative Layout Shift) are part of how Google evaluates page experience. A slow page doesn't just frustrate visitors; it costs you in the rankings.</p>
<p>CARL pages score well on Core Web Vitals without any additional optimization work. No lazy loading plugins, no minification plugins, no image CDN required to hit a passing score. The baseline is already fast because the architecture eliminates the overhead that slows other platforms in the first place.</p>
<h2>No Caching Plugin Required</h2>
<p>One of the recurring costs of running WordPress is the caching plugin subscription. WP Rocket, W3 Total Cache, and WP Super Cache exist because WordPress is slow by default and needs help generating static output. You pay monthly to fix a problem that shouldn't exist.</p>
<p>CARL doesn't have that problem. Every published page is already the equivalent of a perfectly cached static file. There is nothing to configure, no cache to warm, no cache to invalidate when you make a change. You publish, the file updates, and visitors get the new version immediately.</p>
<h2>Shared Hosting Handles It Comfortably</h2>
<p>A common assumption is that fast sites need expensive hosting. With a database-driven CMS, that's largely true. Each visitor puts a load on the database and the PHP interpreter, so traffic spikes translate directly into server strain.</p>
<p>With CARL, traffic spikes are much less of a concern. The server is delivering pre-built files, which is lightweight work. A shared hosting account that would buckle under a WordPress traffic spike will handle CARL traffic without breaking a sweat. You get performance headroom without paying for a VPS or dedicated server.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/why-carl-pages-load-so-fast-pre-built-files-zero-runtime-ove-1780315578.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How CARL Generates Static PHP Files | No Database Queries on Live Pages | Carl Riedel</title>
    <link>https://carlriedel.com/wiki/architecture/how-carl-generates-static-php-files.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wiki/architecture/how-carl-generates-static-php-files.php</guid>
    <pubDate>Mon, 01 Jun 2026 07:59:06 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-carl-generates-static-php-files-no-database-queries-on-l-1780314528.webp" alt="How CARL Generates Static PHP Files | No Database Queries on Live Pages | Carl Riedel" style="max-width:100%;height:auto;">
<h1>How CARL Generates Static PHP Files</h1>

<p>Every time you publish a page in CARL, something happens that most CMS platforms never do: CARL builds a real file and saves it to your server. Not a database record. Not a cached version of a database record. An actual PHP file, sitting on disk, ready to serve the next visitor without touching the database at all.</p><p><img src="/images/how-carl-generates-static-php-files-no-database-queries-on-l-1780314528.webp" alt="How CARL Generates Static PHP Files" class="img-fluid" style=""><br></p>

<p>That's the core of how CARL works, and once you understand it, a lot of other things start to make sense. Why do pages load so fast? Why does the site keep working even if the admin panel is offline? Why does security look so different compared to WordPress?</p>

<h2>What "Static PHP" Actually Means</h2>

<p>The term static can be confusing because PHP is typically associated with dynamic, database-driven pages. Here's the distinction: in CARL, <b>the <em>generation</em> is dynamic, but the <em>output</em> is static</b>. When you hit Publish, CARL runs a build process that pulls your content, template, meta tags, schema, and settings, assembles them into a finished HTML/PHP file, and writes it to your web root.</p>

<p>From that point on, the file just sits there. When a visitor hits that URL, your server delivers the file directly. No PHP execution, no database query, no plugin stack assembling the page from scratch on every request. The work was done once, at publish time, not on every single visit.</p>

<p>The PHP part means your site still executes. Your header, footer, and navigation are PHP includes that run at serve time. But the page content itself is already baked in. It's the best of both worlds: the speed of a static file with the flexibility of live includes.</p>

<h2>The Build Process, Step by Step</h2>

<p>When you publish a page, CARL's generator works through a clear sequence. It loads your chosen template file from <code>admin/tpl/</code>. It substitutes the template tokens (<code>{{TITLE}}</code>, <code>{{META}}</code>, <code>{{BODY}}</code>, <code>{{HEAD_EXTRA}}</code>, and <code>{{SNIPPET}}</code>) with the actual content from your page record. It builds the full meta block: description, canonical URL, Open Graph tags, Twitter card, and JSON-LD schema. Then it writes the finished file to the correct path on disk based on your slug and directory settings.</p>

<p>That generated file is what your visitors see. It's also what Google indexes. The URL a search engine crawls is the URL of the file on your server, which serves pre-built content in under a second.</p>

<h2>Where the Files Live</h2>

<p>Generated pages are written directly into your web root, following the slug and directory you set in the page editor. A page with directory <code>wiki/architecture</code> and slug <code>how-carl-generates-static-php-files</code> gets written to <code>/wiki/architecture/how-carl-generates-static-php-files.php</code> on your server. The path is predictable, clean, and entirely under your control.</p>

<p>This also means you own every page permanently. If you ever remove the CARL admin panel entirely, every page you've published keeps working. The files are on your server. Nothing is held inside a platform.</p>

<h2>What Happens When You Edit a Page</h2>

<p>Editing a page in CARL updates the database record, but the live file on disk doesn't change until you regenerate it. This is intentional. It means you can draft changes without affecting the live page. When you're ready, hit Publish (or Regenerate) and CARL overwrites the file with the updated version.</p>

<p>If you ever need to update every page at once, say after changing your site header or footer, CARL's Site Structure screen lets you bulk-regenerate your entire site in one click. Every file gets rebuilt fresh from the current template and settings.</p>

<h2>Why This Approach Matters</h2>

<p>Most CMS platforms build the page in real time on every visit. WordPress queries the database, loads PHP, runs hooks, executes plugins, and assembles the HTML before sending anything to the browser. That process happens every single time, for every single visitor. Caching plugins exist specifically to work around this limitation.</p>

<p>CARL doesn't need a caching plugin because there's nothing to cache. The page is already built. A standard shared hosting server can handle far more traffic than you'd expect, because serving a pre-built file is one of the simplest things a web server does.</p>

<p>It also means there's no CMS runtime exposed on your live pages. No admin files, no plugin files, no database credentials involved in serving a visitor. The attack surface shrinks dramatically, resulting in a faster, quieter, more secure site without installing a single security plugin.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-carl-generates-static-php-files-no-database-queries-on-l-1780314528.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Build Local Business Websites for Clients | Hand It Over and Walk Away | CR</title>
    <link>https://carlriedel.com/use-cases/build-local-business-websites-for-clients.php</link>
    <guid isPermaLink="true">https://carlriedel.com/use-cases/build-local-business-websites-for-clients.php</guid>
    <pubDate>Sat, 30 May 2026 18:02:38 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/build-local-business-websites-for-clients-hand-it-over-and-w-1780178495.webp" alt="Build Local Business Websites for Clients | Hand It Over and Walk Away | CR" style="max-width:100%;height:auto;">
<h1>Build Local Business Websites for Clients - Hand It Over and Walk Away</h1>

<p>When you build local business websites for clients on WordPress, the project doesn't end at handoff. The invoice gets paid, the site goes live, and six months later, a plugin update breaks something. Or their hosting bill doubles because WordPress needs more server resources to stay functional.</p><p>Or Google flags the site for malware. And who do they call? You. Even if it's not your fault, it's your problem, because you built it.</p>

<p>That dynamic is baked into how WordPress works, and most freelancers and agencies accept it as a cost of doing business. It doesn't have to be.</p>

<h2>The Maintenance Trap</h2>

<p>Every WordPress site you build for a client is a liability you carry indefinitely. The plugin stack needs updating. The login page is being probed around the clock. The security plugin that's supposed to handle all of this is itself a piece of third-party code that runs database queries on every page load.</p><p>You can stay on top of it, but it requires ongoing attention: attention you're either giving away for free or charging for at a rate that makes the relationship feel like a subscription neither party particularly wanted.</p>

<p>Multiply that across 8 or 10 clients, and the math gets uncomfortable fast. More clients should mean more revenue. On WordPress, it also means more surface area to manage, more potential incidents, and more late-night calls from people whose site just went down.</p><p>The overhead compounds with every client you add.</p>

<p>The <a href="https://carlriedel.com/wordpress/wordpress-plugin-costs.php">plugin cost problem</a> is well-documented, but the hidden cost is the time. Every hour spent on maintenance is an hour not spent building the next project.</p>

<h2>What a Clean Handoff Looks Like</h2>

<p>When you build a client site in CARL and hand it over, the conversation is simple. Here's your site. Here's your cPanel login. The pages are PHP files sitting on your server. If you want to move hosts, everything moves with you. Nobody owns your content except you.</p>

<p>There's no plugin ecosystem to inherit, no login page getting brute-forced, no xmlrpc.php being probed at 3 am. The site runs on pre-built files. A visitor arrives, the server hands them a file, and that's it. Nothing executes, nothing queries a database, nothing can be exploited through a plugin vulnerability.</p><p>The site just works: quietly, fast, and without requiring anyone's attention.</p>

<p>For clients who've been burned by a previous web designer (or by WordPress itself), this lands differently. Ownership means the files exist on their server independently of any platform. That's worth explaining clearly, because clients who understand it tend to value it.</p>

<h2>Scaling Your Client Portfolio</h2>

<p>The practical difference becomes obvious once you're managing several clients at once. On WordPress, each new client adds a new plugin stack to maintain, a new potential security incident, and a new point of failure that can ring your phone at the worst possible time. On CARL, each new client adds a site with pre-built pages, no plugin ecosystem, and hosting requirements light enough to run comfortably on standard shared cPanel hosting.</p>

<p>You can take on more work without the maintenance overhead scaling proportionally. That's the difference between a client portfolio that grows your revenue and one that grows your stress.</p>

<h2>The Build Process</h2>

<p>CARL generates complete PHP files and writes them directly to your server. Include files (navigation, footer, schema, CSS) are set up once and inherited automatically by every page you generate after that. Change the nav once, and it updates across the entire site. No regenerating every page, no uploading individual files, no "did I get all of them" anxiety.</p>

<p>The AI Schema Generator handles local SEO markup in one click.</p><p>On a service page, you can stack Local Business schema with FAQ schema in the same pass, which used to take 10-15 minutes of manual copy-paste work per page. For a client site with 15 service pages, that time adds up quickly.</p>

<p>For the full step-by-step process of building a local business site in CARL, from include file setup through to sitemap submission, the <a href="https://carlriedel.com/use-cases/local-business-website-builder.php">local business website builder guide</a> covers it in detail.</p>

<h2>The Business Case</h2>

<p>Building local business websites for clients is a straightforward service business. The problem WordPress introduces is that it turns a project-based service into an ongoing support obligation. The site never really leaves your hands because it's never really self-sufficient.</p>

<p>CARL changes that. You build it properly, you hand it over, and you walk away to the next project. The client gets a fast, secure site on their own server that they genuinely own. You get a clean exit and a portfolio that scales without dragging you back into maintenance work you stopped billing for months ago.</p>

<p>That's a better deal for both sides of the relationship.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/build-local-business-websites-for-clients-hand-it-over-and-w-1780178495.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Lead Generation Website for Local Service Business | Own the Leads, Own the Domain | Carl Riedel</title>
    <link>https://carlriedel.com/use-cases/lead-generation-website-for-local-service-business.php</link>
    <guid isPermaLink="true">https://carlriedel.com/use-cases/lead-generation-website-for-local-service-business.php</guid>
    <pubDate>Sat, 30 May 2026 17:45:35 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/lead-generation-website-for-local-service-business-own-the-l-1780177227.webp" alt="Lead Generation Website for Local Service Business | Own the Leads, Own the Domain | Carl Riedel" style="max-width:100%;height:auto;">
<h1>Lead Generation Website for Local Service Business</h1>

<p>The model is simple enough. Pick a local service in a specific city. Build a website targeting that search traffic. Rank it. Then sell the leads to a contractor who'd rather take phone calls than think about Google. A roofing site for Tucson. A plumbing site for Memphis. A moving company site for Lynchburg. You own the domain, you own the leads, and you collect a referral fee every time the phone rings.</p><p><img src="/images/lead-generation-website-for-local-service-business-own-the-l-1780177227.webp" alt="Lead Generation Website for Local Service Business" class="img-fluid" style=""><br></p>

<p>Simple model. Execution is where most people run into trouble, and almost all of it stems from the platform they build on.</p>

<h2>Why WordPress Is the Wrong Platform for This</h2>

<p>If you're running 10 lead gen sites on WordPress, you're also running 10 plugin stacks that need updating. Ten login pages are being brute-forced at 3 am. Ten potential security incidents that can take a site down on a Saturday when your contractor's phone stops ringing, and they start asking questions.</p>

<p>The maintenance overhead isn't a minor inconvenience. It compounds.</p><p>Every site you add increases the surface area you're responsible for. And the plugin costs alone (SEO, security, caching, and backup plugins) can run £400- £600 per year across a portfolio of 10 sites. That's money leaving your pocket before a single lead is sold.</p>

<p>If you want to understand exactly what those <a href="https://carlriedel.com/wordpress/wordpress-plugin-costs.php">WordPress plugin costs</a> add up to over the course of a year, I've broken it down in detail.</p>

<h2>What a Lead Gen Site Actually Needs</h2>

<p>A lead generation website for a local service business doesn't need much. It needs to be fast, because page speed affects both rankings and the conversion rate of the visitors who do land. It needs accurate local schema so Google understands the service area and the business type.</p><p>It needs a clean contact form or a click-to-call setup. And it needs to stay up because a site that goes down stops sending leads.</p>

<p>A blog, a membership area, a plugin with 14 features you'll never use: none of that moves the needle. You need a fast, clean, rankable page that converts visitors into phone calls.</p>

<h2>How CARL Handles This at Scale</h2>

<p>CARL generates real PHP files and writes them directly to your server. When someone lands on a page, the server hands them a pre-built file. The database, the plugin chain, the CMS runtime: none of it is involved. The page loads in under a second on standard shared hosting, and that speed is a product of the architecture rather than something you configure.</p>

<p>Security holds up for the same reason. The public-facing WordPress login page doesn't exist in a CARL install. Neither does xmlrpc.php. And because pages are static files rather than assembled on request, there's no plugin chain executing with database access on every visit. The <a href="https://carlriedel.com/wordpress/wordpress-hacked.php">WordPress hacking problem</a> is an architecture problem, and CARL addresses it at that level.</p>

<p>Running 15 lead gen sites on CARL is not materially harder than running 3.</p><p>The pages are files on a server. There's no plugin ecosystem to patch across every install. Hosting requirements stay light even as traffic grows, which means more sites on less infrastructure and more of the margin stays in your pocket.</p>

<h2>Schema and Local SEO Without the Manual Work</h2>

<p>Local SEO lives and dies on schema. Google needs to know the service type, city, phone number, and service area before it can rank a page for local-intent searches. On a single site, writing that by hand is tedious. Across 15 sites, it becomes a real problem.</p>

<p>CARL's AI Schema Generator reads the page and builds the structured markup in one click. On a service page, you can stack Local Business schema with FAQ schema in the same pass. The Organization schema sits in your <code>header_scripts.txt</code> include file and appears on every page automatically, so Google gets consistent NAP data across every URL on the site without you touching it again after setup.</p>

<p>For a full walkthrough of building a local business site in CARL from the ground up, including the include file setup and the step-by-step generation process, see the <a href="https://carlriedel.com/use-cases/local-business-website-builder.php">local business website builder guide</a>.</p>

<h2>Tracking Your Leads</h2>

<p>CARL has an Affiliate Link Tracker built in. Every click is logged with full source and attribution data. For lead gen work, that means you can show a contractor exactly where their calls are coming from and back it up with numbers. Clean reporting keeps client relationships clean. It also keeps your numbers honest when you're deciding which sites to expand and which to let go of.</p>

<p>The lead generation website for the local service business model works. The math is straightforward, the barrier to entry is low, and local contractors are genuinely happy to pay for calls they didn't have to earn themselves. Maintenance drag, compounding plugin costs, and weekend security incidents on the wrong platform are what eat the margin. CARL removes that overhead at the architecture level.</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/lead-generation-website-for-local-service-business-own-the-l-1780177227.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>The Hidden Business Model Behind Free WordPress Plugins</title>
    <link>https://carlriedel.com/wordpress/wordpress-plugin-danger.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-plugin-danger.php</guid>
    <pubDate>Sat, 30 May 2026 10:40:21 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/hidden-business-model-behind-free-wordpress-plugins.webp" alt="The Hidden Business Model Behind Free WordPress Plugins" style="max-width:100%;height:auto;">
<h1>The Hidden Business Model Behind Free WordPress Plugins</h1>

<p><strong>By Carl Riedel</strong> — Website Builder, WordPress Survivor</p>



<p>There is a question every WordPress site owner should ask, but almost nobody does:</p>

<p><em>Why would a developer spend months building a plugin and give it away for free?</em></p>

<p>Sometimes the answer is genuinely altruistic. Sometimes it's to build a reputation or attract consulting clients. But sometimes — and this is the part that should keep you up at night — the answer is that the plugin itself isn't the product.</p>

<p><strong>Your website is.</strong></p>

<h2>How the Scheme Works</h2>

<p>Here's a business model that actually exists and has been documented in detail by the WordPress security firm Wordfence.</p>

<p>A developer in Pakistan or India builds a useful WordPress plugin — a CAPTCHA, a carousel, a contact form. They publish it on the WordPress repository. Over time, through organic discovery, it accumulates 5,000, 10,000, 20,000 active installations. The developer maintains it, keeps it working, and answers support tickets. It's a legitimate, useful plugin.</p><p><img src="/images/hidden-business-model-behind-free-wordpress-plugins.webp" alt="Hidden Business Model Behind Free WordPress Plugins" class="img-fluid" style=""><br></p>

<p>Then someone approaches them with an offer to buy it.</p>

<p>To a developer earning local wages, being offered £1,000 to £3,000 for a free plugin they built in their spare time feels like an extraordinary windfall. They accept immediately. The transaction looks completely normal — plugin acquisitions happen all the time in the WordPress ecosystem.</p>

<p>What happens next is where it gets criminal.</p>

<h2>The Weaponized Update</h2>

<p>Once the buyer controls the plugin, they push an update through the official WordPress repository. To the average site owner, an update notification is a good thing — you're supposed to keep plugins updated. Security experts tell you this constantly.</p>

<p>But this update contains a backdoor.</p>

<p>In December 2017, Wordfence documented exactly this happening with the Captcha plugin, which had over 300,000 active installations at the time. The update contained code that allowed the new owner to silently gain administrative access to every site running the plugin. The backdoor would then delete itself, leaving no trace it had ever been there.</p>

<p>In this particular case, the goal was invisible backlinks — hidden links injected into victims' sites pointing to the attacker's own properties, artificially boosting their search engine rankings. Site owners had no idea. Their sites kept working normally. Their visitors saw nothing wrong.</p>

<p>The attacker was getting free SEO at the expense of hundreds of thousands of unwitting website owners.</p>

<p>You can read Wordfence's full technical breakdown here: <a href="https://www.wordfence.com/blog/2017/12/backdoor-captcha-plugin/" target="_blank" rel="noopener">Backdoor in Captcha Plugin Affects 300K WordPress Sites</a>.</p>

<h2>This Is Not a Rare Edge Case</h2>

<p>The Captcha case was caught and documented. Most aren't.</p>

<p>The WordPress plugin repository contains over 60,000 plugins. The review process for updates is not comprehensive. A malicious update can go live and reach hundreds of thousands of sites before anyone notices something is wrong — if they ever notice at all.</p>

<p>The attack surface is enormous and it grows every time you install another plugin.</p>

<p>Think about what the average WordPress site is running: a theme framework, a page builder, an SEO plugin, a caching plugin, a contact form plugin, a backup plugin, a security plugin, a CAPTCHA. That's eight separate codebases from eight separate developers, each with their own update pipeline, each representing a potential acquisition target for someone with the right business model.</p>

<p>You are trusting eight strangers — whose identity, location, and financial circumstances you know nothing about — with administrative access to your website.</p>

<h2>What Happened to Me</h2>

<p>In 2017, I had <a href="https://carlriedel.com/wordpress/wordpress-hacked.php">18 WordPress sites</a> on a single server. One of them had a carousel plugin installed — the kind of lightweight, harmless-looking plugin that gets installed without a second thought.</p>

<p>That plugin was the open door. All 18 sites were compromised in a single attack. My hosting provider sent a Cease and Desist because the hacked sites were sending spam at a volume the registrar had flagged. I deleted everything and took two weeks off to recover.</p>

<p>I never touched WordPress again after that.</p>

<h2>CARL Has No Plugins</h2>

<p>This is not a feature I added as an afterthought. It's the entire point.</p>

<p>CARL generates static PHP files and writes them directly to your server. There is no plugin ecosystem. There is no update pipeline from unknown third-party developers. There is no WordPress repository with 60,000 codebases of varying quality and ownership.</p>

<p>Every line of code running on a CARL site was written by one person — me — and is under my direct control. There are no acquisitions, no weaponized updates, no silent backdoors hiding in a carousel plugin your daughter installed for a university project.</p>

<p>When someone asks me why I built CARL without a plugin system, this is the answer.</p>



<p><strong>Want to build a site with no plugin vulnerabilities, no third-party update pipelines, and no attack surface for opportunistic buyers?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL →</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/hidden-business-model-behind-free-wordpress-plugins.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>How One WordPress Plugin Wiped Out 18 of My Websites in a Single Day</title>
    <link>https://carlriedel.com/wordpress/wordpress-hacked.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-hacked.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/how-one-wordpress-plugin-wiped-out-18-of-my-websites-in-a-si-1775177016.webp" alt="How One WordPress Plugin Wiped Out 18 of My Websites in a Single Day" style="max-width:100%;height:auto;">
<h1>How One WordPress Plugin Wiped Out 18 of My Websites in a Single Day</h1>

<p><strong>By Carl Riedel</strong> — Builder of CARL, Recovering WordPress Survivor</p>



<p>It must've been around 2017 or early 2018. I had just moved from London to Szentkirály, Hungary. Life was good. My daughter was at university, and as part of her curriculum, she had to build a website. I, being the "internet marketer Dad," was more than proud when she asked if I could help.</p>

<p>I didn't think twice. I set up a cPanel account for her on my WHM server, installed WordPress, and gave her some pointers — how to install plugins, that sort of thing. Then I went back to fighting the Google algorithm and never gave it a second thought.</p>

<h2>The Phone Call</h2>

<p>About a month later, a client called from the UK. Her website was down.</p>

<p>I did a quick check, thinking it was probably some overnight update that had broken the CSS. I logged into the server and opened File Manager.</p>

<p>That's when I saw them. Dozens of tiny files in the root directory that I knew for certain didn't belong there.</p>

<p>You may be familiar with that sinking feeling in the pit of your stomach when you realize something horrible has happened. That's exactly how I felt right then. Instinctively, in that single moment, I knew this was no CSS update. I had been hacked.</p><p><img src="/images/how-one-wordpress-plugin-wiped-out-18-of-my-websites-in-a-si-1775177016.webp" alt="One WordPress Plugin Wiped Out 18 of My Websites in a Single Day" class="img-fluid" style=""><br></p>

<h2>The Cleanup</h2>

<p>I purged the account immediately. Deleted everything — uninstalled WordPress, wiped the database, removed every file. Then I reinstalled WordPress and restored a backup from the week before. I was just about finished when an email arrived from my hosting provider.</p>

<p>A Cease and Desist order.</p>

<p>Turns out, I had 17 other WordPress sites on that same WHM server. Every single one of them had been hacked. And they had been silently sending spam emails at a rate so alarming that the registrar had noticed the bandwidth drain coming from my account.</p>

<p>Eighteen websites. Gone. In one day. From one plugin.</p>

<h2>What I Did Next</h2>

<p>I got so furious that I deleted every single account from WHM — I didn't even bother uninstalling WordPress first. Just gone. All of it.</p>

<p>Then I booked a flight to the Netherlands and took a two-week holiday.</p>

<p>Later, I found out what had caused it. My daughter had installed a carousel plugin for her university project. That plugin had a vulnerability. That vulnerability was the open door that took down an entire server.</p>

<p>One plugin. Installed by a student. On a university project. Eighteen professional websites destroyed.</p>

<h2>Why I Built CARL</h2>

<p>That day changed how I think about websites permanently. WordPress's entire model — install plugins, trust third-party developers, hope nothing conflicts or gets exploited — is a structural vulnerability. It isn't a question of <em>if</em> you get hacked. It's a question of <em>when</em>, and how much it costs you when it happens.</p>

<p>CARL generates static PHP files and writes them directly to your server. There is no WordPress runtime executing on every page load. There is no plugin ecosystem to exploit. There is no xmlrpc.php, no REST API endpoint, no login page being brute-forced at 3am.</p>

<p>When a visitor arrives at a CARL page, they get a file. That's it. A clean, fast, pre-built file with nothing to attack.</p>

<p>My daughter's carousel plugin can't touch it.</p>



<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/how-one-wordpress-plugin-wiped-out-18-of-my-websites-in-a-si-1775177016.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Alternative | Own Your Site, Drop the Platform | CARL</title>
    <link>https://carlriedel.com/wordpress/wordpress-alternative.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-alternative.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-alternative-own-your-site-drop-the-platform-carl-1774904678.webp" alt="WordPress Alternative | Own Your Site, Drop the Platform | CARL" style="max-width:100%;height:auto;">
<h1>WordPress Alternative: What to Use When You're Done With WordPress</h1>

<p><strong>By Carl Riedel</strong> — Web Developer, Microsite Mastery Trainer</p>



<p>Most people searching for a WordPress alternative have already had the experience. A hacked site, a plugin conflict that broke everything at 2am, a hosting bill that crept up, or just the slow grind of managing a platform that seems to fight you more than it helps.</p>

<p>You don't need to be talked into leaving. You're already there.</p>

<p>What you need is a clear picture of what actually exists beyond WordPress, and what those options cost you in time, money, and control.</p><p><img src="/images/wordpress-alternative-own-your-site-drop-the-platform-carl-1774904678.webp" alt="Wordpress Alternative" class="img-fluid" style=""><br></p>

<h2>The Main Alternatives — and What They're Actually Good For</h2>

<p><strong>Static site generators (Jekyll, Hugo, Eleventy)</strong> produce fast, secure HTML files. Developers love them. If you're comfortable on the command line and don't need a content editor, they're solid. If you want a non-technical team member to publish a page, good luck.</p>

<p><strong>Website builders (Wix, Squarespace, Webflow)</strong> are fine for small brochure sites. You pay monthly, forever. Your content lives on their servers. If they change their pricing, discontinue a plan, or go under, you're rebuilding from scratch. You own the design. You don't own much else.</p>

<p><strong>Headless CMS platforms (Contentful, Sanity, Strapi)</strong> separate the content layer from the presentation layer. Powerful, flexible, and genuinely useful at scale. Also genuinely complex. You're looking at a developer project, not a solo publishing setup.</p>

<p><strong>Ghost</strong> is probably the cleanest WordPress alternative for bloggers and newsletter publishers. Faster, simpler, less bloated. Still a hosted runtime on every page load. Still a subscription fee. Still not your server.</p>

<h2>What Most Alternatives Get Wrong</h2>

<p>The problem with most WordPress alternatives is that they solve one thing while quietly reintroducing another. Static generators fix security but break content editing. Website builders fix ease of use but introduce platform dependency. SaaS CMSes fix the hosting headache but add a monthly fee that compounds for years.</p>

<p>The question most people forget to ask is: who actually owns the data?</p>

<p>If your content lives in someone else's database, on someone else's servers, accessible through someone else's admin panel — you're a tenant. A well-treated tenant, probably. But a tenant.</p>

<h2>What I Built Instead</h2>

<p>I've been building websites professionally since 2010. I've run WordPress sites, had them hacked (18 at once, same server, one carousel plugin — <a href="/wordpress/wordpress-hacked.php">that story is here</a>), and spent years managing the plugin ecosystem that WordPress requires just to function at a basic level.</p>

<p>In 2024 I started thinking and planning how to build CARL — a PHP CMS that runs on standard shared hosting and generates static PHP files directly to disk. When someone visits a CARL page, the server hands them a pre-built file. There's no database query on page load, no plugin stack executing, no WordPress runtime doing its thing.</p>

<p>The admin panel is a proper CMS — you write content, set your SEO fields, schedule posts, manage subscribers and members, track affiliate links, and generate AI schema. Then you click Generate and the page is written to disk as a clean PHP file.</p>

<p>Everything lives in your database, on your server, under your cPanel account. There's no monthly platform fee beyond your hosting. No third-party developer whose plugin you're trusting with access to your site. One codebase, one developer, direct control.</p>

<h2>Who CARL Is Right For</h2>

<p>Content publishers who want fast pages, solid SEO, and no plugin drama. Affiliate marketers who need clean link tracking built in. Anyone who's been burned by WordPress and wants a system they actually own.</p>

<p>CARL is probably not right for you if you need a complex e-commerce store, a heavily customized enterprise system, or a team of developers building something bespoke. There are better tools for those jobs.</p>

<p>But if you're running a content site, a lead generation site, an affiliate site, or a membership site — and you want to stop renting your platform and start owning it — CARL is worth a serious look.</p>

<p>The documentation is in the members' area. Free to join.</p>



<p><strong>See how CARL works before you commit to anything.</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-alternative-own-your-site-drop-the-platform-carl-1774904678.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Too Slow | Get Sub-1-Second Load Times on Shared Hosting | CARL</title>
    <link>https://carlriedel.com/wordpress/wordpress-too-slow.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-too-slow.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-is-too-slow.webp" alt="WordPress Too Slow | Get Sub-1-Second Load Times on Shared Hosting | CARL" style="max-width:100%;height:auto;">
<h1>WordPress Too Slow? Here's What's Actually Killing Your Page Speed</h1>

<p><strong>By Carl Riedel</strong> — Web Developer, Mass Page SEO Specialist</p>



<p>You've done everything right. Good hosting. Caching plugin installed. Images compressed. You even paid for a CDN.</p>

<p>Still slow.</p>

<p>There's a reason for that, and it's not your fault. It's architectural.</p>

<h2>What Actually Happens When Someone Loads a WordPress Page</h2>

<p>Most people think a web page is just a file sitting on a server waiting to be delivered. On a WordPress site, that's not what happens.</p>

<p>Every single page load triggers a chain reaction. PHP executes. WordPress bootstraps its core. Your active theme loads. Then every active plugin checks in — the security plugin, the SEO plugin, the caching plugin, the contact form plugin, all of them. Then WordPress queries the database to fetch your content. Then it assembles the page from all those moving parts and sends it back to the visitor.</p>

<p>That chain fires every time. Every visitor. Every page. Every load.</p>

<p>On a shared hosting account — which is where the overwhelming majority of WordPress sites live — you're running that chain on hardware you share with hundreds of other sites doing the exact same thing.</p><p><img src="/images/wordpress-is-too-slow.webp" alt="wordpress is too slow" class="img-fluid" style=""><br></p>

<h2>Plugins Don't Just Add Features. They Add Weight.</h2>

<p>This is the part that the "just install a caching plugin" crowd skips over.</p>

<p>A caching plugin doesn't remove the overhead of your other plugins. It stores a snapshot of the assembled page so WordPress doesn't have to rebuild it from scratch every time. That helps. But the cache has to be built first, warmed up, invalidated on updates, and rebuilt. And it doesn't help at all for logged-in users, checkout pages, search results, or anything dynamic.</p>

<p>Every plugin you add increases the baseline load time of your site  cached or not. Security plugins run on every request to check for threats. SEO plugins inject metadata. Analytics plugins fire tracking scripts. Page builder plugins load their own CSS and JS frameworks, whether you're using those elements on that page or not.</p>

<p>The average WordPress site runs 20-30 active plugins. Each one is a tax on every page load.</p>

<h2>The Database Query Problem</h2>

<p>WordPress stores everything in a database: content, settings, plugin options, and user data. A typical WordPress page load runs anywhere from 20 to 100+ database queries before it can render a single thing on screen.</p>

<p>On a well-configured server with a fast database that's fine. On shared hosting with an overloaded MySQL instance shared across hundreds of accounts, those queries stack up fast.</p>

<p>Google's Core Web Vitals measure Time to First Byte — how long before the browser gets anything back from the server. WordPress sites on shared hosting routinely fail this metric not because the content is heavy, but because the server is still running PHP and querying the database before it's sent a single byte.</p>

<h2>What I Did Instead</h2>

<p>In 2010 I was building WordPress sites for clients. By 2017, I'd had 18 of my own sites hacked in a single attack and spent years dealing with the plugin ecosystem that WordPress requires just to function.</p>

<p>I built CARL differently from the ground up. When you create a page in CARL and click Generate, the system assembles the page once and writes a clean PHP file directly to disk. That file is static. When a visitor arrives, the server hands them that file. No PHP execution chain. No plugin stack. No database queries on page load.</p>

<p>Time to First Byte on a CARL page on shared hosting beats most WordPress sites on dedicated servers. The page is already built. There's nothing to wait for.</p>

<h2>The Numbers That Actually Matter</h2>

<p>Google uses page speed as a ranking factor. It has for years. But the impact compounds — a slow site doesn't just rank lower, it converts worse. Every additional second of load time increases bounce rate. Visitors don't wait. They leave.</p>

<p>A 2023 study by Portent found that a page loading in 1 second converts 3x better than a page loading in 5 seconds. Most WordPress sites on shared hosting load in 3-6 seconds. Most CARL pages load in under 1 second on the same infrastructure.</p>

<p>Same hosting. Same server. The difference is architecture.</p>



<p><strong>See how CARL pages perform before you commit to anything.</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access →</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-is-too-slow.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Plugin Costs | Build for Less With No Annual Fees | CARL</title>
    <link>https://carlriedel.com/wordpress/wordpress-plugin-costs.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-plugin-costs.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-plugin-costs.webp" alt="WordPress Plugin Costs | Build for Less With No Annual Fees | CARL" style="max-width:100%;height:auto;">
<h1>The True Cost of WordPress Plugins: What You're Actually Paying Every Year</h1>

<p><strong>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</strong></p>



<p>WordPress is free. You've heard that a thousand times.</p>

<p>What nobody puts in the headline is what comes after the free part.</p>

<h2>The Plugin Bill Nobody Talks About</h2>

<p>A basic WordPress site needs plugins to function at a professional level. Security, SEO, backups, caching, forms, spam protection. These aren't optional extras. They're the minimum viable stack.</p>

<p>Here's what that stack actually costs:</p>

<table class="table table-bordered" style="font-size:.9rem;">
  <thead class="table-dark">
    <tr>
      <th>Plugin / Service</th>
      <th>Free Version</th>
      <th>Paid Version</th>
      <th>Annual Cost</th>
    </tr>
  </thead>
  <tbody>
    <tr><td>Wordfence Security</td><td>Limited</td><td>Premium</td><td>$119/yr</td></tr>
    <tr><td>Yoast SEO</td><td>Basic</td><td>Premium</td><td>$99/yr</td></tr>
    <tr><td>UpdraftPlus Backups</td><td>Limited</td><td>Premium</td><td>$70/yr</td></tr>
    <tr><td>WP Rocket (Caching)</td><td>None</td><td>Required</td><td>$59/yr</td></tr>
    <tr><td>WPForms (Contact Forms)</td><td>Basic</td><td>Pro</td><td>$99/yr</td></tr>
    <tr><td>Akismet Anti-Spam</td><td>Personal only</td><td>Commercial</td><td>$100/yr</td></tr>
    <tr><td>Advanced Custom Fields</td><td>Limited</td><td>Pro</td><td>$49/yr</td></tr>
    <tr><td>Elementor (Page Builder)</td><td>Very limited</td><td>Pro</td><td>$99/yr</td></tr>
    <tr class="table-warning fw-bold"><td>Total</td><td></td><td></td><td>$694/yr</td></tr>
  </tbody>
</table>

<p>That's $694 per year. Per site. Before hosting.</p>

<p>If you're running 3 sites, you're looking at over $2,000 a year just to keep WordPress functional and secure. Most of these plugins offer multi-site licenses at a discount, but the discount rarely brings it below $1,000 for 3 sites.</p><p><img src="/images/wordpress-plugin-costs.webp" alt="Wordpress plugin costs are high" class="img-fluid" style=""><br></p>

<h2>The Hidden Costs Nobody Puts in a Table</h2>

<p>The plugin bill is the visible part. There are costs that don't show up on a credit card statement.</p>

<p><strong>Update management.</strong> Every plugin needs regular updates. Updates break things. Someone has to test every update on a staging site before pushing to production. If that someone is you, that's hours of your time every month. If it's a developer, that's billable hours.</p>

<p><strong>Conflict resolution.</strong> Two plugins from two different developers update on the same day. They now conflict. Your site throws a white screen. You spend 4 hours diagnosing which plugin is the problem, rolling back, contacting support, and waiting for a fix. This is not hypothetical. This happens to WordPress sites regularly. And I know, because I've been there, done that.</p>

<p><strong>The abandoned plugin problem.</strong> Plugins get abandoned all the time. The developer moves on, gets a job, loses interest. The plugin stops getting updates. Security vulnerabilities get discovered. You're now running outdated code on your live site. Now you can either find a replacement (and migrate all your settings and data), or you accept the risk.</p>

<p><strong>Hosting upgrades.</strong> A fully-loaded WordPress site with 20-30 plugins needs more server resources than a lean site. Shared hosting that was fine 2 years ago starts struggling. You upgrade to a higher tier. That's another $10-30 per month, per site.</p>

<h2>What CARL Costs Instead</h2>

<p>CARL is a one-time license. No annual renewal. No per-site fee. No plugin stack to maintain.</p>

<p>Security is architectural, not a plugin. CARL generates static files. There's no WordPress runtime to exploit, no plugin vulnerabilities to patch, no xmlrpc.php to brute-force. The security model is built into how the system works, not bolted on afterward with a $119/yr subscription.</p>

<p>SEO is built in. Schema generation, meta tags, OG tags, canonical URLs, sitemap, RSS feed. No Yoast required.</p>

<p>Backups are your hosting provider's job. Because CARL generates files to disk and stores everything in a standard MySQL database, your normal cPanel backup covers everything. No dedicated backup plugin needed.</p>

<p>Caching is irrelevant. The page is already built. There's nothing to cache because there's no runtime generating it on demand.</p>

<p>One license. One codebase. One developer who actually uses the tool every day on his own sites.</p>

<p>That's a different category of product from "free platform plus $700 in annual plugin subscriptions."</p>



<p><strong>See what CARL includes before you renew another plugin subscription.</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access →</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-plugin-costs.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Switch to CARL | Keep Your Data, Kill the Monthly Fees | CARL</title>
    <link>https://carlriedel.com/wordpress/switch-to-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/switch-to-carl.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/switch-to-carl.webp" alt="Switch to CARL | Keep Your Data, Kill the Monthly Fees | CARL" style="max-width:100%;height:auto;">
<h1>Switch to CARL</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Picture a Monday morning where you open your laptop and just... write.</p>

<p>No plugin update notifications. No security alert from Wordfence. No email from your hosting company asking why your site is sending 40,000 spam messages from an xmlrpc.php exploit you didn't even know existed. You just open the editor, write the article, click Generate, and it's live.</p>

<p>That's what switching to CARL actually feels like.</p><p><img src="/images/switch-to-carl.webp" alt="Switch to CARL and never get hacked again" class="img-fluid" style=""><br></p>

<h2>What Disappears When You Leave WordPress</h2>

<p>The plugin tax goes first. The SEO plugin, the cache plugin, the security plugin, the backup plugin, the affiliate link plugin. Most WordPress content sites are carrying $300 to $600 a year in subscriptions just to keep the thing running safely and ranking properly.</p>

<p>CARL builds all of that in. AI schema generator, affiliate link tracker with click reporting, email subscribers, members area, scheduled publishing, CTA builder, site search. One license, everything included.</p>

<p>The security paranoia goes with it. CARL generates static PHP files and writes them to your server. When a visitor arrives, they get a file. There's no WordPress runtime executing on every page load, no plugin ecosystem to exploit, no login page getting brute-forced at 3am. I built CARL partly because I watched one student's carousel plugin take down 18 of my professional websites in a single afternoon. That doesn't happen with static files.</p>

<h2>What You Actually Own</h2>

<p>Your content lives in your own database on your own server. Every article, every subscriber, every member account. No platform can change their terms, hike their pricing, or shut you down and hold your data hostage.</p>

<p>I've watched WordPress site owners get caught completely off-guard when a hosting company migrated servers and mangled their database. Or when a plugin they'd relied on for 3 years got abandoned and started throwing PHP 8 errors on every page. With CARL, your generated pages are real files on your server. Even if you walked away from the CMS entirely, your site would keep serving pages.</p>

<p>That's ownership. The actual kind.</p>

<h2>The Switch Itself</h2>

<p>You don't migrate WordPress. You build fresh in CARL while your WordPress site keeps running. Most people have their first page live within an hour of installing it. When you're ready, you point the domain. The old site stays intact as long as you want it.</p>

<p>The AI Schema Generator takes a big chunk of the SEO setup off your plate too. Write the article, click Generate Schema, and Claude builds your full schema markup, OG tags, Twitter cards, and canonical URL in one shot. Roughly 20 minutes of manual work per article, gone.</p>

<p>The content migration is real work, I won't pretend otherwise. But it's also a natural point to revisit old articles, tighten them up, and fix the ones that were never quite right. Most WordPress sites are carrying years of thin pages. The switch is a good excuse to fix that.</p>

<h2>Get the Full Docs Free</h2>

<p>Get Free Access and you can read the complete CARL documentation inside the members area. No credit card, no trial clock ticking. A free account that gives you everything you need to decide if CARL is the right fit before you spend anything.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/switch-to-carl.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Page Generator | Your Pages Exist Even If CARL Doesn't | CARL</title>
    <link>https://carlriedel.com/features/page-generator.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/page-generator.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/page-generator.webp" alt="Page Generator | Your Pages Exist Even If CARL Doesn&#039;t | CARL" style="max-width:100%;height:auto;">
<h1>Page Generator</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Most website platforms work the same way. A visitor arrives, the CMS wakes up, queries the database, assembles the page from components, runs it through a template engine, and finally delivers something to the browser. Every single visit. Every single time.</p>

<p>WordPress does this. Squarespace does this. Ghost does this. They all do this.</p>

<p>CARL doesn't.</p><p><img src="/images/page-generator.webp" alt="Page Generator" class="img-fluid" style=""><br></p>

<h2>What Actually Happens When You Click Generate</h2>

<p>When you finish writing an article in CARL and click Generate, something different happens. CARL takes your content, pulls in your template, builds the complete page, and writes it as a real PHP file directly to your server. The file sits there on disk, complete and ready.</p>

<p>When a visitor arrives, Apache serves that file. No database query fires. No CMS runtime executes. No plugin chain runs. The visitor gets the page in milliseconds because the work was already done the moment you clicked Generate.</p>

<p>That's not a cache. A cache is a temporary copy that expires, gets invalidated, and has to be rebuilt. This is the actual file. It lives on your server permanently until you change it.</p>

<h2>The Part That Makes People Stop and Think</h2>

<p>Here's the claim that no WordPress plugin, no website builder, and no hosted CMS platform can make.</p>

<p><strong>Your pages keep working even if CARL stops existing.</strong></p>

<p>Think about what that means. If Squarespace shuts down tomorrow, your site goes with it. If WordPress releases an update that breaks your theme, your site breaks with it. If your host migrates servers and something goes wrong with the database, your site goes offline.</p>

<p>With CARL, the generated files are entirely independent of the CMS. They're PHP files on your hosting account. Apache serves them directly. If you deleted the entire CARL admin tomorrow, every single page you've ever generated would keep loading perfectly for every visitor who arrives.</p>

<p>Your content doesn't live inside a platform. It lives on your server, in files you own, under a hosting account you control. The CMS is just the tool that built them. Once the file exists, the tool is irrelevant.</p>

<h2>What This Means in Practice</h2>

<p>It means your site loads fast. Genuinely fast, on standard shared hosting, without a caching plugin, without a CDN, without a £200/year performance stack bolted on top. Shared hosting is more than capable of serving static files quickly. WordPress is slow because of what it does on every request. CARL pages are fast because they do nothing on request. The work is already done.</p>

<p>It means server migrations are painless. Move hosts, copy the files, and point the domain. The generated pages travel with your hosting account. There's no database to corrupt, no CMS to reinstall, no plugins to reactivate.</p>

<p>It means you're not dependent on anyone. No platform subscription to keep your site alive. No monthly fee that, if it lapses, takes your content offline. Your pages exist on your server. Full stop.</p>

<h2>How the Generator Works</h2>

<p>Every page in CARL has a template, a directory, and a slug. The template defines the layout. The directory and slug define the URL. When you generate, CARL resolves all the pieces: your content, your SEO fields, your schema markup, your OG tags, your GA tracking code, your sidebar includes. It writes everything to a single, clean PHP file at the exact path you specified.</p>

<p>Change the slug and regenerate, CARL automatically deletes the old file and writes the new one. No orphaned pages left behind. No stale URLs sitting on the server from 3 years ago.</p>

<p>The generator supports nested directories up to 4 levels deep. So `/herbs/chamomile/how-to-grow.php` is just as easy as `/about.php`. Create the directory structure in CARL, generate, and the folders are created on disk automatically.</p>

<h2>One Click. Live Immediately.</h2>

<p>There's no publish queue. No build process that takes 30 seconds. No deployment pipeline to manage. You click Generate and the file is on the server. Refresh the URL and your page is there.</p>

<p>That's the workflow CARL was built around. Write the content, fill in the SEO fields, generate the schema with one click, generate the file with one click. Done. Your page is live, indexed by Google, and serving visitors on shared hosting faster than most sites running on dedicated servers.</p>

<p>And it'll keep serving them long after you've moved on to whatever comes next.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/page-generator.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>AI Schema Generator | Let Claude Read Your Page, Own Your Rankings | CARL</title>
    <link>https://carlriedel.com/features/ai-schema-generator.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/ai-schema-generator.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/ai-schema-generator-let-claude-read-your-page-own-your-ranki-1775205883.webp" alt="AI Schema Generator | Let Claude Read Your Page, Own Your Rankings | CARL" style="max-width:100%;height:auto;">
<h1>AI Schema Generator</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>I used to do schema markup by hand. Every article. Open the Google schema documentation, build the JSON-LD block, fill in the title, the date, the author, the description, the publisher details, the OG tags, the Twitter cards, the canonical URL. Check it in the Rich Results Test tool. Fix whatever it flagged. Save it to the page.</p>

<p>About 20 minutes per article, minimum. More if you were being thorough.</p>

<p>That was just the technical work. It didn't include thinking about what schema type actually fit the content, whether the description was optimized for clickthrough, or whether the markup would qualify for rich results in Google. That was extra.</p>

<p>CARL made all of that disappear. Here's how.</p><p><img src="/images/ai-schema-generator-let-claude-read-your-page-own-your-ranki-1775205883.webp" alt="AI schema Generator" class="img-fluid" style=""><br></p>

<h2>What Every Other Tool Does</h2>

<p>Every SEO plugin on the market works the same way. Yoast, RankMath, Schema Pro, all of them. They look at your title, your date, your author name, and a few other fields you've filled in. Then they drop those values into a pre-built template and output the result as schema.</p>

<p>It's fast and it's better than nothing. But it has a fundamental limitation. The plugin has no idea what your article is actually about. It knows your title. It knows your meta description if you wrote one. That's it.</p>

<p>Your article could be a detailed technical guide, a personal story, a product comparison, or a recipe. The plugin generates the same template either way. It cannot read. It cannot understand context. It just fills in the blanks.</p>

<h2>What CARL Does Instead</h2>

<p>When you click Generate Schema in CARL, something entirely different happens.</p>

<p>CARL strips your article down to plain text and sends it to Claude, along with your page title, your URL, your OG image, and any authority links you've added. Claude reads the whole thing. Not a summary. Not a title. The actual content of your page.</p>

<p>Then Claude builds the schema from that understanding.</p>

<p>The description it generates reflects what the article genuinely covers. The schema type fits the actual content. The markup is structured to qualify for rich results in Google, not just to pass validation. And it comes back as a complete, ready-to-use block: Article schema, OG tags, Twitter cards, canonical URL, all in the right order, all in one shot.</p>

<p>No plugin does this. None of them. Because no plugin has Claude reading your content and reasoning about what the schema should say.</p>

<h2>What You Actually Get in Google</h2>

<p>Schema isn't just a technical checkbox. Done properly, it earns you rich results: article cards with author information, enhanced previews, and better visual presence in search results. That's real estate on the page that most sites never claim because their schema is either missing or too generic to qualify.</p>

<p>The difference between a generic schema block and a content-aware one isn't subtle. Google's systems are good at detecting whether your markup reflects the actual page or just fills in a template. Rich, accurate schema that matches the content is what gets you the enhanced results. That's exactly what Claude generates.</p>

<h2>The Workflow</h2>

<p>Write your article. Fill in the meta description and OG image. Add any authority links in the SameAs field, one per line. Click Generate Schema. Wait about 4 seconds. The complete schema block appears in the Head Injection field, the Override Auto-Schema checkbox ticks automatically, and you're done.</p>

<p>Save Draft, generate the file, and your page is live with full rich-results-ready schema markup that took you 4 seconds and cost a fraction of a penny in API fees.</p>

<p>You supply your own Claude API key from Anthropic. CARL never marks it up. You pay Anthropic directly at their standard rates, which for a typical article amounts to less than a penny per generation. That's it.</p>

<h2>Twenty Minutes Down to Four Seconds</h2>

<p>I built this because I was the person doing it manually. Every article, every time, 20 minutes minimum. I knew exactly how much time it was costing me across a site with hundreds of pages.</p>

<p>Now I click a button. Claude reads the article and does the work. The schema it produces is better than what I was writing by hand because it's built from a genuine understanding of the content, not from me trying to summarise it accurately in a hurry.</p>

<p>That's what CARL's AI Schema Generator actually is. Not a smarter template filler. A tool that reads your work and builds something that reflects it.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/ai-schema-generator-let-claude-read-your-page-own-your-ranki-1775205883.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Members Area | Free and Premium Tiers Built In | CARL</title>
    <link>https://carlriedel.com/features/members-area.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/members-area.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/free-and-premium-online-membership-area.webp" alt="Members Area | Free and Premium Tiers Built In | CARL" style="max-width:100%;height:auto;">
<!--
==================================================
CARL ADMIN SETTINGS
==================================================
Title:            Members Area | Free and Premium Tiers Built In | CARL
Slug:             members-area
Directory:        features
Template:         bootstrap-blog
Meta description: CARL includes a complete members area with free and premium tiers, session management, and subscriber integration. No membership plugin. No annual fee. Just built in.
In RSS feed:      ✅ YES
Schema:           Article (use AI Schema Generator)
==================================================
PASTE CONTENT BELOW USING CARL 📋 PASTE HTML BUTTON
==================================================
-->

<h1>Members Area Free and Premium Tiers Built In</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>A few years ago, I went looking for a membership solution for one of my sites. I knew exactly what I needed: a simple free tier to build the list, a premium tier for paying members, clean login and logout, session management that actually worked, and content protection that didn't require a computer science degree to configure.</p>

<p>What I found was a plugin market that had turned a straightforward requirement into an annual subscription racket.</p><p><img src="/images/free-and-premium-online-membership-area.webp" alt="Free and premium membership built in" class="img-fluid" style=""><br></p>

<p>MemberPress: $179 a year at the basic tier. (I actually think it's closer to $199/year now) Paid Memberships Pro: $247 a year for anything beyond the crippled free version. Restrict Content Pro: $99 a year minimum. And that's before you factor in the WooCommerce integration you'd need to take payments, which is another plugin, another potential conflict, another thing to update, maintain, and worry about.</p>

<p>Every single one of them was charging you a recurring annual fee for the privilege of locking your own content behind a login on your own website.</p>

<p>I built CARL's members area because I just didn't want to help hostage, and I just didn't need all the "Bells and Whistles".</p>

<h2>What's Included (Everything You Actually Need)</h2>

<p>CARL's members area ships as part of the core system. No addon. No upgrade. No extra invoice arriving every January. It's just there, ready to use, from the moment you install CARL.</p>

<p>You get 2 membership tiers out of the box: free and premium. Free members get access to whatever content you designate for them. Premium members get everything free members get, plus whatever additional content you put behind the premium tier. The access levels are enforced at the file level, not by JavaScript that can be bypassed in the browser.</p>

<p>Every protected page gets a guard injected automatically when CARL generates the file. A visitor without a valid session gets redirected to the login page. A free member trying to access premium content gets redirected to your upgrade page. No configuration required. It just works.</p>

<h2>Registration That Fits Your Business</h2>

<p>CARL gives you 2 registration modes and you switch between them in Settings with a single dropdown.</p>

<p><strong>Open registration</strong> creates the account instantly and marks it active. The member can log in immediately. This is the right mode for a free content site where low friction is everything. Someone lands on your page, clicks Join Free, fills in their details, and they're in. No waiting, no approval email, no friction between them and your content.</p>

<p><strong>Approval mode</strong> creates the account as pending. The member can't log in until you approve them in the CARL admin. You get a notification, you review the application, you approve or decline. This is the right mode for a high-end training programme or a paid community where you want to vet who gets in.</p>

<p>2 modes. 1 setting. Switch anytime.</p>

<h2>The Part That Took Me Years to Find Elsewhere</h2>

<p>Here's what I could never get right with WordPress membership plugins: the subscriber and member systems were always completely separate. Someone joined my email list over here. Someone created a member account over there. They were often the same person but the systems had no idea. I was managing 2 lists, 2 sets of data, 2 sources of truth.</p>

<p>CARL connects them.</p>

<p>When someone registers as a member and their email matches an existing subscriber record, CARL links the 2 automatically. Your subscriber becomes a member. Your member is already on your list. One person, one record, one system managing both relationships.</p>

<p>That means when you're building your free tier, you're building your email list at the same time. Every free member signup is a subscriber. Every subscriber who upgrades to free membership is now trackable as a member. The list and the membership grow together because in CARL they're the same thing.</p>

<p>That's not a feature I've seen any WordPress membership plugin handle cleanly. Most of them treat it as an afterthought, a Zapier integration, or a £49/year addon.</p>

<h2>Sessions That Actually Work</h2>

<p>CARL's session management doesn't use PHP sessions. It uses a dedicated member_sessions table in your database, with SHA-256 hashed tokens stored server-side and a secure http-only cookie in the browser. Sessions last 30 days. Expired sessions are cleaned up automatically.</p>

<p>The practical result: members stay logged in across visits without having to sign in every time, logout actually works (the session is destroyed server-side, not just the cookie), and there's no session data floating around in PHP's default session handler where it can cause conflicts with other parts of your site.</p>

<h4><u>NOTE:</u> I must add here though, that your actual computer settings may change this behavior. For instance on MY machine, I have Windows, clear out ALL sessions when I shut down my machine.</h4>

<p>It's the kind of session management you'd expect from a purpose-built system, not a plugin that was bolted onto WordPress as an afterthought.</p>

<h2>The Templates Are Already Built</h2>

<p>CARL ships with 5 members templates ready to go: login, register, dashboard, protected content layout, and logout. Each one handles its own logic. The login template processes the POST, validates credentials, sets the session cookie, and redirects. The logout template destroys the session and redirects to login. The dashboard template shows the member's name, their access level badge, and the members-only navigation bar.</p>

<p>You create the pages in CARL, assign the templates, generate the files, and your members area is live. The whole setup takes less than an hour on a fresh install.</p>

<h2>Your Members, Your Data, Your Revenue</h2>

<p>Every member record lives in your database on your server. Their email, their access level, their registration date, their last login. You own all of it. No platform holding your member list hostage, no percentage taken from your membership revenue, no terms of service that can change overnight and affect how you run your community.</p>

<p>When a member pays you, that's between you and your payment processor. CARL is not in that transaction. There are no platform fees, no revenue share, no per-member charges. You keep everything.</p>

<p>That's what membership should look like. Your content, your members, your rules, your revenue. Built in, from day one, at no extra cost.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/free-and-premium-online-membership-area.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Affiliate Link Tracker | Know Which Pages Make You Money | CARL</title>
    <link>https://carlriedel.com/features/affiliate-link-tracker.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/affiliate-link-tracker.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/affiliate-link-tracker-know-which-pages-make-you-money-carl-1775176298.webp" alt="Affiliate Link Tracker | Know Which Pages Make You Money | CARL" style="max-width:100%;height:auto;">
<!--
==================================================
CARL ADMIN SETTINGS
==================================================
Title:            Affiliate Link Tracker | Know Which Pages Make You Money | CARL
Slug:             affiliate-link-tracker
Directory:        features
Template:         bootstrap-blog
Meta description: CARL's built-in affiliate link tracker shows you exactly which pages and traffic sources drive your commissions. Click tracking, geo data, UTM attribution, and link cloaking. No plugin required.
In RSS feed:      ✅ YES
Schema:           Article (use AI Schema Generator)
==================================================
PASTE CONTENT BELOW USING CARL 📋 PASTE HTML BUTTON
==================================================
-->

<h1>Affiliate Link Tracker</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Most affiliate marketers have no idea which pages actually make them money.</p>

<p>They know their total commission figures. They can see how much landed in their account last month. But ask them which specific article drove the most clicks to their top offer, or which traffic source converts best on their review pages, and the honest answer is usually a shrug and a guess.</p>

<p>That's not a strategy. That's hope with a PayPal account.</p><p><img src="/images/affiliate-link-tracker-know-which-pages-make-you-money-carl-1775176298.webp" alt="Affiliate Link Tracker" class="img-fluid" style=""><br></p>

<p>CARL's affiliate link tracker fixes that. Every click on every tracked link is recorded with the page it came from, the traffic source, the geographic location, and the UTM data. You stop guessing and start knowing. I was there, so I know this firsthand.</p>

<h2>Your Links, Not a Plugin's</h2>

<p>Pretty Links costs $99 a year at its basic tier. ThirstyAffiliates is $80 a year. Oops! A quick Google search just says it's 99.60/year today for BASIC! Both are WordPress plugins that cloak your affiliate links, track your clicks, and charge you annually for the privilege of doing something that should just be part of your site.</p>

<p>CARL's link tracker is built in. Not bolted on, not a third-party integration, not a plugin that needs updating every time WordPress decides to break things. It's part of the core system, included in every license, running on your server under your domain.</p>

<p>Your links live at your domain. When a visitor clicks a tracked link on your site, CARL records the click and redirects them to your affiliate URL. The affiliate network sees a clean redirect from your domain. Your visitor sees a tidy URL. You see exactly what happened.</p>

<h2>What Gets Tracked on Every Click</h2>

<p>Each click record captures the full picture: the specific link that was clicked, the page the visitor was on when they clicked it, the referring source, the geographic location down to country level, and any UTM parameters present in the incoming URL.</p>

<p>That last one matters more than most people realise. If you're running traffic from multiple sources, UTM tracking tells you which campaign, which ad, which email, or which social post sent the buyer. You're not just tracking clicks. You're tracking the entire journey from first touch to affiliate redirect.</p>

<p>Link groups let you organise tracked links by offer, by niche, or by campaign. Reports show you click volumes over time, top performing links, geographic breakdowns, and traffic source analysis. Everything you need to make actual decisions about where to focus your content effort.</p>

<p>A typical grouping I often use is by source. So, I may have an "Amazon group", a "Clickbank group" and a "Shareasale group". But those are just personal examples. You can just do whatever makes sense to YOU!</p>

<h2>Change a URL Once, Update Every Page Instantly</h2>

<p>Here's the practical problem every affiliate marketer runs into sooner or later. An offer changes its URL. A product moves to a new landing page. An affiliate program switches networks and issues new tracking links.</p>

<p>If your links are hardcoded into 200 articles, you have a problem. You're hunting through every post, finding every instance, updating every URL manually. Miss one and you're sending traffic to a dead page.</p>

<p>With CARL's link tracker, every affiliate link has one record in the database. One destination URL. Change it in your admin area, and every page on your site that uses that link updates instantly, because every page points to the same CARL redirect. You make 1 change. Every article on the site is correct immediately.</p>

<p>That's not a minor convenience. On a site with hundreds of articles and dozens of affiliate offers, that's hours of work eliminated every time an offer changes.</p>

<h2>The Reports That Actually Matter</h2>

<p>CARL's link reports show you what you need to see without burying it in noise. Your top performing links by click volume. Your top performing pages by affiliate clicks generated. Geographic distribution of your buyers. Click trends over time so you can see whether a piece of content is gaining or losing traction.</p>

<p>Cross-reference that with your actual commission data from the affiliate network and you have a complete picture. You know which articles to update, which offers to promote harder, which traffic sources to scale, and which content is quietly sending clicks that never convert.</p>

<p>That's the difference between running an affiliate site and running an affiliate business. One is content creation on autopilot. The other is data-informed decisions compounding over time.</p>

<h2>Built In From Day One</h2>

<p>There's no setup beyond creating your first tracked link. No API to connect, no third-party account to maintain, no plugin to install. Open the Link Tracker in the CARL admin, add your affiliate URL, give it a slug, assign it to a group if you want, and your cloaked link is live.</p>

<p>Add it to any page using the slug. CARL handles the redirect, records the click, and adds it to your reports. That's the whole workflow.</p>

<p>Pretty Links, ThirstyAffiliates and all other systems/vendors will both send you a renewal invoice next January. CARL won't.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/affiliate-link-tracker-know-which-pages-make-you-money-carl-1775176298.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Email Subscribers | Your List Lives on Your Server First | CARL</title>
    <link>https://carlriedel.com/features/email-subscribers.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/email-subscribers.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/email-subscribers-your-list-lives-on-your-server-first.webp" alt="Email Subscribers | Your List Lives on Your Server First | CARL" style="max-width:100%;height:auto;">
<!--
==================================================
CARL ADMIN SETTINGS
==================================================
Title:            Email Subscribers | Your List Lives on Your Server First | CARL
Slug:             email-subscribers
Directory:        features
Template:         bootstrap-blog
Meta description: 
In RSS feed:      ✅ YES
Schema:           Article (use AI Schema Generator)
==================================================
PASTE CONTENT BELOW USING CARL 📋 PASTE HTML BUTTON
==================================================
-->

<h1>Email Subscribers - Your List Lives on Your Server First</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Your email list is the most valuable asset your website produces. More valuable than your content. More valuable than your traffic. More valuable than your search rankings, because rankings fluctuate and traffic dries up, but a list of people who asked to hear from you is something you built deliberately and own permanently.</p>

<p>Except most site owners don't actually own it.</p><p><img src="/images/email-subscribers-your-list-lives-on-your-server-first.webp" alt="Email Subscribers - Your List Lives on Your Server First" class="img-fluid" style=""><br></p>

<p>They have a Kit account, or a Mailchimp account, or an ActiveCampaign account. Their subscribers live on that platform's servers. Cancel the subscription, migrate to a different provider, or get your account suspended for reasons you didn't see coming, and suddenly your list is behind a wall you don't control.</p>

<p>CARL fixes that at the foundation.</p>

<h2>Your Database First. Always.</h2>

<p>When someone fills in the signup form on a CARL site, the subscriber record hits your database first. Before anything else happens. Before any API call fires. Before Kit knows anything about it.</p>

<p>Your subscriber is in your database, on your server, under your hosting account. That record exists independently of every email platform on the market. Cancel Kit tomorrow, migrate to a different provider next year, or do nothing at all, and every subscriber you've ever collected is still right there in your own database.</p>

<p>That's what owning your list actually means. Not having access to an export button on someone else's platform. Actually owning the data, in your own infrastructure, from the moment it was created.</p>

<h2>The Sync Problem Nobody Talks About</h2>

<p>Third party embed forms, the kind WordPress sites typically use, send your visitor's data directly to the email platform the moment they submit. If that API call fails, the subscriber is gone. No record on your end, no retry, no way to know it happened. The visitor got a success message. You got nothing.</p>

<p>This happens more than people realise. API timeouts, rate limits, temporary platform outages. Every one of those events is a subscriber your site collected and then silently lost.</p>

<p>CARL's approach eliminates that entirely. The subscriber is saved locally first. Then CARL attempts the Kit sync. If the sync succeeds, the record is marked as synced. If it fails for any reason, the subscriber is still in your database, flagged as unsynced, with a one-click retry button right in the CARL admin.</p>

<p>Nobody falls through the cracks. Every signup your site generates is captured, accounted for, and retryable if something went wrong downstream.</p>

<h2>Kit Integration, Done Properly</h2>

<p>Kit is the email platform CARL integrates with natively right now, and it's a genuinely good one. Solid deliverability, clean automation tools, a well-documented API, and an affiliate program worth mentioning (full disclosure: I'm a Kit affiliate).</p>

<p>The integration uses Kit's form subscription endpoint directly. A new subscriber on your CARL site gets added to your Kit form, tagged appropriately, and appears in your Kit dashboard within seconds. The sync is automatic, silent, and immediate on every successful signup.</p>

<p>Your Kit account handles everything it's good at: email delivery, sequences, broadcasts, automation. CARL handles everything it's good at: capturing the data, owning the record, and making sure nothing gets lost in transit.</p>

<p>More integrations are coming. Mailchimp, ActiveCampaign, and other major platforms are on the roadmap. Kit is the first because it's what I use and what I could build and test properly. When new integrations ship, they'll follow the same pattern: your database first, platform sync second, retry on failure.</p>

<h2>The Signup Form</h2>

<p>CARL's native signup form is a clean, lightweight AJAX form that lives in your sidebar as `carl_signup_form.txt`. It submits without a page reload, validates the email, runs a honeypot check to catch bots, and handles the database save and Kit sync in a single server-side request.</p>

<p>The form wording is fully customisable from Settings. Title, subtext, button label, success message. Change them once and they update everywhere on your site instantly, because the form is a file that every page pulls in dynamically.</p>

<p>No embed code from an external platform. No script loading from a third-party server on every page load. Just a clean form that works, saves your data, and syncs to Kit.</p>

<h2>Your List, Your Rules</h2>

<p>Every subscriber record in CARL includes their email, signup source (the page URL they signed up from), status, Kit sync status, and a hashed IP for spam detection. The admin shows you your full list, filterable by status, with CSV export available any time.</p>

<p>You can see exactly where your subscribers are coming from. Which pages are converting visitors into list members. Which articles are pulling their weight and which aren't. That data lives in your database, exportable whenever you want it, owned by you from the moment it was created.</p>

<p>That's how email list building should work. Your site, your data, your list. Kit is the delivery engine. CARL is where the list actually lives.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/email-subscribers-your-list-lives-on-your-server-first.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Scheduled Publishing | Publish Automatically While You Sleep | CARL</title>
    <link>https://carlriedel.com/features/scheduled-publishing.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/scheduled-publishing.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/scheduled-publishing-publish-automatically-while-you-sleep-c-1775235999.webp" alt="Scheduled Publishing | Publish Automatically While You Sleep | CARL" style="max-width:100%;height:auto;">
<!--
==================================================
CARL ADMIN SETTINGS
==================================================
Title:            Scheduled Publishing | Publish Automatically While You Sleep | CARL
Slug:             scheduled-publishing
Directory:        features
Template:         bootstrap-blog
Meta description: CARL schedules and publishes static PHP files automatically at the exact date and time you set. No other static site generator does this. Write today, publish tomorrow, sleep through it all.
In RSS feed:      ✅ YES
Schema:           Article (use AI Schema Generator)
==================================================
PASTE CONTENT BELOW USING CARL 📋 PASTE HTML BUTTON
==================================================
-->

<h1>Scheduled Publishing</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Scheduled publishing on a static file system isn't supposed to exist.</p>

<p>That's not an exaggeration. It's a fundamental constraint of how static site generators work. Jekyll, Hugo, Eleventy, Gatsby, every major static generator on the market operates the same way: you build the files, you deploy the files, they're live. There's no runtime, no background process, nothing checking a clock and deciding what to publish next. You either publish now or you publish manually later.</p>

<p>CARL does it anyway.</p><p><img src="/images/scheduled-publishing-publish-automatically-while-you-sleep-c-1775235999.webp" alt="Scheduled Publishing" class="img-fluid" style=""><br></p>

<h2>How It Actually Works</h2>

<p>When you set a page to Scheduled in CARL and pick a date and time, the page sits in the database as a draft. The generated file doesn't exist on disk yet. Nothing is live.</p>

<p>At the scheduled moment, one of 2 things fires. If you've set up a cPanel cron job (5 minutes to configure, instructions right there in Settings), the PHP scheduler runs, finds the due page, generates the file to disk, updates the status to published, and pings Google's sitemap endpoint. All of it, automatically, at the exact time you set.</p>

<p>If you haven't set up the cron job yet, the fallback kicks in. Every time any admin page loads, CARL silently checks for due scheduled pages and fires them. So even without cron, your page publishes on the next admin visit after the scheduled time.</p>

<p>2 layers of scheduling. Zero manual intervention required.</p>

<h2>Write Today. Publish at 7AM Tomorrow.</h2>

<p>Here's what that looks like in practice.</p>

<p>It's Sunday afternoon. You're in a writing flow. You knock out 3 articles in one sitting, the kind of productive session that doesn't happen every day. In WordPress you'd publish all 3 now and flood your feed, or save them as drafts and remember to come back and publish them manually across the week.</p>

<p>In CARL you set article 1 to publish Monday at 7AM. Article 2 goes Tuesday at 7AM. Article 3 goes Wednesday at 7AM. You close the laptop. Monday morning your first article is live, indexed, and already getting crawled by Google before you've had your first coffee. Tuesday and Wednesday take care of themselves.</p>

<p>That's not a small convenience. Consistent publishing is one of the most reliable signals Google uses to evaluate a site's authority. Sites that publish on a regular cadence get crawled more frequently and indexed faster than sites that publish in bursts and go quiet. I know this as fact, cause that is how I used to do it. I know NOW that wasn't too smart! CARL makes that consistency effortless because you batch the work when inspiration hits and let the scheduler handle the rest.</p>

<h2>The Google Ping Is Automatic</h2>

<p>Every time CARL publishes a scheduled page, it automatically pings Google with your updated sitemap. You don't have to remember to submit the URL to Search Console. You don't have to manually request indexing. CARL tells Google a new page just went live the moment it happens.</p>

<p>That's the full publish cycle automated. The file gets written to disk, status updated in the database, Google notified. One scheduled click in the editor, everything else handled.</p>

<h2>One Warning Worth Knowing</h2>

<p>When a page is scheduled, use Save Draft only. Do not click Save & Generate File. Generating a scheduled page publishes it immediately, bypassing the scheduler entirely. It's the one gotcha in the whole system and it's worth knowing upfront so you don't accidentally push a page live at 11PM on a Saturday when you meant it to go out Monday morning.</p>

<p>Set the schedule. Save Draft. Walk away. CARL handles the rest.</p>

<h2>Your Content Calendar, Running Itself</h2>

<p>The best content marketing systems are the ones that keep working when you're not watching. You plan the content, you write the content, you set the schedule. Then you go live your life. Travel, sleep, take a weekend off, spend a day away from the screen without your publishing calendar falling apart.</p>

<p>CARL publishes while you're on a plane. It publishes while you're asleep. It publishes on bank holidays and school runs and days when you're too busy to think about your site. The schedule you set keeps running regardless of whether you're at your desk.</p>

<p>No other static file system gives you that. CARL does.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/scheduled-publishing-publish-automatically-while-you-sleep-c-1775235999.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Canonical Tag Issues | Stop Losing Pages to Google | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-canonical-issues.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-canonical-issues.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-canonical-tag-issues.webp" alt="WordPress Canonical Tag Issues | Stop Losing Pages to Google | CARL" style="max-width:100%;height:auto;">
<h1>WordPress Canonical Tag Issues</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>I opened Google Search Console one morning and found 245 of my pages sitting in a bucket called "Alternate page with proper canonical tag." Not indexed. Not serving in search results. Just sitting there, flagged, invisible to Google.</p>

<p>245 pages. Gone from search. And WordPress had created every single one of the problems behind it.</p><p><img src="/images/wordpress-canonical-tag-issues.webp" alt="WordPress Canonical Tag Issues Stop Losing Pages to Google" class="img-fluid" style=""><br></p>

<h2>What Google Search Console Is Actually Telling You</h2>

<p>"Alternate page with proper canonical tag" sounds technical but the meaning is simple. Google found your page, crawled it, and then saw a canonical tag pointing somewhere else. The canonical tag told Google: "this isn't the real version, the real version is over there." So Google did exactly what you told it to do. It indexed the other URL and ignored this one.</p>

<p>The problem is you probably never told it to do that. WordPress did it for you, automatically, without asking.</p>

<h2>How WordPress Creates This Mess</h2>

<p>WordPress generates multiple URLs pointing at the same content by design. It's baked into how the platform works. Take a single blog post. WordPress creates:</p>

<ul>
  <li>The post URL itself: <code>/your-article/</code></li>
  <li>A category archive page that includes it: <code>/category/news/</code></li>
  <li>A tag archive page: <code>/tag/wordpress/</code></li>
  <li>An author archive: <code>/author/carl/</code></li>
  <li>A date archive: <code>/2024/03/</code></li>
  <li>Paginated versions of all of the above: <code>/category/news/page/2/</code></li>
</ul>

<p>Every one of those URLs is a version of the same content. Google crawls all of them. It sees duplicate content across multiple addresses and has to decide which one is canonical. SEO plugins try to manage this by adding canonical tags pointing back to the original post URL. When they work correctly, Google acknowledges the canonical and indexes the right page.</p>

<p>When they don't work correctly, or when the canonical signals are inconsistent, or when Google decides to disagree with your canonical declaration, you get 245 pages sitting in that GSC bucket doing nothing for your rankings.</p>

<h2>Why the Fix Is Never Quite Fixed</h2>

<p>The standard advice is to install an SEO plugin, configure canonical tags correctly, and submit your sitemap. That advice isn't wrong. But it's treating the symptom, not the cause. I compare it to mopping the floor with the tap open and overflowing.</p>

<p>The cause is that WordPress generates all those duplicate URLs in the first place. Every new post you publish creates a fresh wave of category pages, tag pages, and archive variations. Your SEO <a href="/wordpress/wordpress-plugin-danger.php">plugin</a> has to keep up with all of them, all the time, correctly, forever. One misconfiguration, one plugin conflict, one WordPress update that changes how archives are generated, and you're back in the same bucket.</p>

<p>I spent months going back and forth on this with one of my sites. Fix the canonical issues in GSC, validate the fix, watch the count drop, publish more content, watch it creep back up. It's a treadmill. You're managing a problem WordPress is continuously recreating.</p>

<h2>What CARL Does Instead</h2>

<p>CARL generates one file per page. One URL. One canonical. That's it.</p>

<p>There are no category archive URLs because CARL doesn't generate them automatically. There are no tag pages. There are no author archives. There are no paginated archive variations. You create the pages you want, CARL generates exactly those pages, and every generated file has a single canonical URL pointing to itself.</p>

<p>Google crawls your site and finds clean, distinct pages with no duplicate URL problem to resolve. The "Alternate page with proper canonical tag" bucket in your Search Console stays empty because the situation that creates it never arises.</p>

<p>My 245 pages in that bucket were on a WordPress site I'm in the process of moving to CARL. The CARL site has zero pages in that category. Not because I configured something correctly. Because the platform doesn't create the problem in the first place.</p>

<h2>The Permanent Fix</h2>

<p>You can keep managing canonical tags in <a href="/wordpress/">WordPress</a>. Configure the SEO plugin carefully, audit regularly, fix the issues as they appear, and stay on the treadmill. That's a valid approach if you're committed to staying on WordPress.</p>

<p>Or you can move to a platform that generates clean pages with single canonical URLs by design and never creates the duplicate URL problem that causes all of this in the first place.</p>

<p>One of those options ends the problem. The other manages it indefinitely.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-canonical-tag-issues.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress 4xx Errors | Google Is Crawling Pages You Never Built | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-4xx-errors.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-4xx-errors.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-4xx-errors.webp" alt="WordPress 4xx Errors | Google Is Crawling Pages You Never Built | CARL" style="max-width:100%;height:auto;">
<h1>WordPress 4xx Errors - Google Is Crawling Pages You Never Built</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>Open Google Search Console on a WordPress site and go to Page Indexing. Find the "Blocked due to other 4xx issue" report. Click into it and look at the examples.</p>

<p>What you'll see are URLs like these:</p>

<ul>
  <li><code>/wp-admin/admin-ajax.php?action=process_simple_like&post_id=124&nonce=...</code></li>
  <li><code>/wp-json/oembed/1.0/proxy</code></li>
  <li><code>/%CAtegory%/%postname%/</code></li>
  <li><code>/wp-admin/admin-ajax.php</code></li>
</ul>

<p>You didn't create any of those. WordPress did. And Google has been crawling them, hitting 4xx errors, and logging every one of them as a blocked page on your site.</p>

<h2>Pages You Never Built</h2><p><img src="/images/wordpress-4xx-errors.webp" alt="WordPress 4xx Errors Google Is Crawling Pages You Never Built" class="img-fluid" style=""><br></p>

<p>This is the part that surprises most WordPress site owners. They assume Google only crawls the pages they actually created: articles, category pages, the homepage. The reality is that WordPress exposes a substantial number of additional URLs to the public internet without telling you.</p>

<p>The REST API ships enabled by default. Every WordPress installation has a publicly accessible JSON endpoint at <code>/wp-json/</code> exposing your content, your users, and your site structure in machine-readable format. Googlebot follows it.</p>

<p>The AJAX handler at <code>/wp-admin/admin-ajax.php</code> processes plugin requests. Plugins use it for likes, shares, dynamic content loading, and dozens of other functions. Every one of those requests generates a URL with query parameters that Googlebot can follow and index, or try to, and fail.</p>

<p>The oEmbed proxy at <code>/wp-json/oembed/1.0/proxy</code> handles media embeds. The malformed category URL <code>/%CAtegory%/%postname%/</code> is an unfilled permalink template that somehow made it into the crawl. These aren't edge cases. They're standard WordPress behaviour that millions of sites are dealing with right now.</p>

<h2>What This Costs You</h2>

<p>Googlebot operates on a crawl budget. For most sites it's not unlimited. Google allocates a certain amount of crawl capacity based on your site's authority, speed, and server health. Every request Googlebot makes to your site uses a slice of that budget.</p>

<p>When Google is burning crawl requests on <code>/wp-admin/admin-ajax.php?action=process_simple_like&post_id=541&nonce=1b95f7b151</code> it is not using that same request to crawl and index your actual content. Your real articles, your product pages, your carefully written guides — they're competing for crawl attention with URLs that should never have been publicly accessible in the first place.</p>

<p>On a young site trying to establish authority, wasted crawl budget is a real cost. Pages that should be indexed within days sit in the crawl queue for weeks because Googlebot is busy hitting dead ends your platform created without asking.</p>

<h2>The Standard Advice and Why It's a Treadmill</h2>

<p>The standard fix is to add rules to <code>robots.txt</code> blocking <code>/wp-admin/</code> and <code>/wp-json/</code>, configure your SEO plugin to disable the REST API for public access, and audit your plugins to find which ones are generating the AJAX URLs showing up in GSC.</p>

<p>That advice is not wrong. But you're mopping the floor with the tap open and overflowing again. WordPress will keep generating new publicly accessible endpoints as you install plugins. Every new plugin is a potential source of fresh AJAX URLs, new REST API routes, new endpoints Googlebot will find and follow. You patch one batch, install a new plugin, and 6 months later you're back in GSC looking at a fresh set of blocked URLs.</p>

<p>My GSC report showed 8 examples across multiple dates, some going back to late 2025. First detected, still appearing months later. The platform keeps creating the problem faster than manual fixes can contain it.</p>

<h2>What CARL Generates Instead</h2>

<p>CARL generates exactly the pages you create. Nothing else.</p>

<p>There is no REST API. There is no <code>/wp-json/</code> endpoint. There is no <code>admin-ajax.php</code> handler. There are no oEmbed proxies. There are no unfilled permalink templates floating around in Googlebot's crawl queue.</p>

<p>When you generate a page in CARL, a single PHP file is written to your server at the exact path you specified. That's the only URL that exists. Google crawls it, indexes it, and moves on. There are no surprise URLs hiding behind your content, no plugin-generated endpoints polluting your crawl data, no admin handlers publicly accessible by design.</p>

<p>Your crawl budget goes entirely to your actual content. Every URL Google finds on a CARL site is a page you deliberately created and want indexed. That's not a configuration you achieve by blocking things in <code>robots.txt</code>. It's the natural result of a platform that only generates what you ask it to.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-4xx-errors.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Duplicate Canonical | How an AMP Plugin Killed My Indexing | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-duplicate-canonical.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-duplicate-canonical.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-duplicate-canonical-how-an-amp-plugin-killed-my-in-1775247027.webp" alt="WordPress Duplicate Canonical | How an AMP Plugin Killed My Indexing | CARL" style="max-width:100%;height:auto;">
<h1>WordPress Duplicate Canonical  -  How an AMP Plugin Killed My Indexing</h1><p>I installed an AMP plugin and none of my pages got indexed. Not a few. None. Google Search Console showed zero valid URLs for weeks, and I couldn't work out why. The pages existed, the sitemap was clean, and there were no crawl errors. What there were, on every single page, were 2 canonical tags pointing at each other in a loop Google simply refused to resolve.</p>

<p>That's what a duplicate canonical does at its worst. It doesn't just split your link equity or confuse Google about which version of a URL to rank. It can wipe your indexing entirely. And it happens silently, with no warning, no error, and no obvious place to look.</p><p><img src="/images/wordpress-duplicate-canonical-how-an-amp-plugin-killed-my-in-1775247027.webp" alt="WordPress Duplicate Canonical" class="img-fluid" style=""><br></p>

<h2>Here's exactly how it happened.</h2>

<p>WordPress core wrote a canonical tag when I published each page. Fine, 1 canonical, that's correct. Then I installed the AMP plugin. AMP pages are supposed to have a canonical pointing back to the non-AMP HTML version — that's by design, that's correct too. But the plugin also rewrote the canonical on the non-AMP version, pointing it at the AMP URL instead. Now both versions were pointing at each other. Google crawled the loop and indexed nothing.</p>

<p>That's the AMP version of the problem. But you don't need an AMP plugin to end up here.</p>

<p>Install an SEO plugin — Yoast, RankMath, it doesn't matter which. It writes its own canonical tag alongside the one WordPress core already output. Now you've got 2. Switch themes, or install a page builder, or add a schema plugin that injects meta into the head. Several of those also write canonicals. Not always. Not consistently. Just sometimes, on certain post types, on certain template files, when certain conditions are met. Now you've got 3. Maybe 4.</p>

<p>None of these plugins know about each other. None of them check whether a canonical already exists before writing one. They each do their job in isolation and assume they're the only one doing it.</p>

<h2>Nobody owns the <code>HEAD</code>.</h2>

<p>That's the architectural problem. WordPress has no canonical tag manager. There's no single function responsible for outputting 1 canonical. The head <code></code> is a shared space, and every plugin, every theme, every widget that wants to write to it just writes. Core writes. Your SEO plugin writes. Your theme framework writes. The result is a <code></code> that nobody fully controls and nobody fully audits.</p>

<p>Fixing it is whack-a-mole. You find the duplicate, you trace it to a plugin, you find the setting that suppresses it, you regenerate, you recheck Search Console, you find another one on a different template. You spend a Saturday on it. 3 months later, you install a new plugin, and it's back.</p>

<p>The "fix" that isn't a fix is adding <code>remove_action()</code> calls to your <code>functions.php</code> to suppress whatever's firing twice. That works until the plugin updates, renames the hook, or starts firing on a different priority. You're patching a system that wasn't designed to have 1 owner.</p>

<h2>What CARL does.</h2>

<p>CARL has 1 function that builds the canonical tag: <code>buildCanonical()</code>. It runs once, inside <code>buildMetaBlock()</code>, and it writes 1 tag. There's no SEO plugin writing a second one. There's no theme framework writing a third. The generated PHP file contains exactly the canonical URL you configured, and nothing else touches it.</p>

<p>There's no hook system for canonicals. There's no plugin ecosystem. There's no <code></code> that 6 different authors write to. There's a function, a string, and an output. That's it.</p>

<p>Your canonicals are correct because there's no mechanism for them to be anything else.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>

<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-duplicate-canonical-how-an-amp-plugin-killed-my-in-1775247027.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Redirect Pages | Keep Your Link Equity on the Right URL | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-redirect-pages.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-redirect-pages.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-redirect-pages-keep-your-link-equity-on-the-right--1775255060.webp" alt="WordPress Redirect Pages | Keep Your Link Equity on the Right URL | CARL" style="max-width:100%;height:auto;">
<h1><img src="/images/wordpress-redirect-pages-keep-your-link-equity-on-the-right--1775255060.webp" alt="WordPress Redirect Pages - Keep Your Link Equity on the Right URL" class="img-fluid" style=""></h1>

<p>I changed my permalink structure once. Just once. I went from <code>/?p=123</code> to <code>/post-name/</code> because that's what every SEO guide tells you to do. WordPress said it would handle the redirects automatically. It did. It also created a redirect chain I was still untangling 6 months later, with old URLs still appearing in Google's index and crawl budget disappearing into a maze of 301s I didn't fully understand.</p>

<p>That one setting change is the permalink trap. Almost every WordPress user walks straight into it.</p>

<h2>Here's What Actually Happens When You Change Your Permalink Structure</h2>

<p>When you switch permalink formats, the old URLs don't die. Google has them indexed, other sites have linked to them, and they sit in crawl queues for months. WordPress creates 301 redirects from the old format to the new one, which sounds correct, but every 301 hop costs crawl budget and passes slightly less link equity than a direct link.</p>

<p>If you've changed your permalink structure more than once (and most sites have), you've got chains. <code>/?p=123</code> redirects to <code>/old-slug/</code> which redirects to <code>/new-slug/</code>. Googlebot follows the chain, notes the hops, and moves on. Your equity arrived at the destination, having bled through 2 handoffs it never needed to make.</p>

<p>And that's before you installed a redirect plugin.</p>

<h2>The Plugin Blame Shift</h2>

<p>At some point you noticed GSC flagging redirect issues and did what everyone does: you installed Redirection, or turned on Yoast's redirect manager, or RankMath's. Maybe all 3 at different times, because you switched plugins and the old rules didn't migrate cleanly. Now you've got multiple redirect tables running in parallel.</p>

<p>None of them know about each other. None of them check for conflicts before firing. A URL can match a rule in Redirection and a different rule in Yoast simultaneously, and the one that wins depends on plugin load order, which changes every time you update, deactivate, or reorder plugins. You didn't create a redirect loop intentionally. You created 3 separate systems that each think they're in charge, and occasionally they disagree.</p>

<h2>What Your GSC Coverage Report Is Actually Telling You</h2>

<p>Open your Coverage report and look for "Page with redirect." Those aren't just informational. They're URLs Google found, followed, and decided not to index at that location because they ended up somewhere else. Every one of those is a URL that's been linked to, possibly bookmarked, possibly sitting in an external site's anchor text, sending signal to a page that redirects rather than resolves.</p>

<p>Look at the dates. Some of those anomalies will be months old. Google found them, logged them, came back, found them still redirecting, and moved on. Your crawl budget (the finite number of pages Googlebot will process on your site in a given period) is being spent on dead ends. On a young site trying to build authority, that's real pages sitting in the queue while Googlebot follows chains instead.</p>

<p>The standard advice is to audit with Screaming Frog, flatten your chains into single hops, and remove conflicting rules. That advice is correct. It's also a half-day job that needs to be repeated every time you install a new plugin, change a slug, or let someone else touch your WordPress admin.</p>

<h2>What CARL Does.</h2>

<p>CARL doesn't have a redirect layer. When you create a page, you specify the exact path (<code>/wordpress/issues/wordpress-redirect-pages</code>) and CARL writes the PHP file there. That's the URL. There's nothing to redirect from because there's no alternative version that ever existed.</p>

<p>If you change a slug, CARL generates the file at the new path. The old file is gone. You add a single redirect manually if you need one. 1 rule, 1 hop, done. There's no accumulating table of rules quietly building up behind your content over years of site changes.</p>

<p>Your URLs are clean because there's no mechanism for them to become anything else.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-redirect-pages-keep-your-link-equity-on-the-right--1775255060.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress 5xx Errors | Keep Your Site Online Through Every Update | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-5xx-errors.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-5xx-errors.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-5xx-errors-keep-your-site-online-through-every-upd-1775263424.webp" alt="WordPress 5xx Errors | Keep Your Site Online Through Every Update | CARL" style="max-width:100%;height:auto;">
<h1>WordPress 5xx Errors - Keep Your Site Online Through Every Update</h1>

<p>I hit Update All on a Tuesday morning, and my site went white. No error message, no explanation, just a blank screen where a website used to be. WordPress had nothing to say about it. The server had nothing to say about it. Just white.</p>

<p>That's a 500 error. And if you've run WordPress for any length of time, you've seen one.</p>

<h2>What's actually happening when WordPress goes white</h2><p><img src="/images/wordpress-5xx-errors-keep-your-site-online-through-every-upd-1775263424.webp" alt="WordPress 5xx Errors - Keep Your Site Online Through Every Update" class="img-fluid" style=""><br></p>

<p>A 500 Internal Server Error means something broke on the server side and the server couldn't recover cleanly enough to tell you what. In WordPress, the most common cause isn't a server problem at all. It's PHP running out of memory mid-execution and crashing the process.</p>

<p>Here's the sequence. You click Update All. WordPress loads the updated plugin. The plugin needs more memory than it did before (plugins don't declare their memory footprint, so you have no way of knowing this in advance). PHP hits the memory ceiling your host has set, usually somewhere between 64MB and 256MB on shared hosting. The process dies. WordPress can't finish rendering the page, so it returns a 500 and moves on with its life. Your site is down.</p>

<p>The error log might tell you what happened. If you can find it, if your host gives you access to it, if it's not buried 6 directories deep in cPanel. Most people never get that far. They just keep refreshing until something changes.</p>

<h2>Why bumping the memory limit isn't a fix</h2>

<p>The standard advice is to add <code>define('WP_MEMORY_LIMIT', '256M');</code> to your <code>wp-config.php</code>. That works, until it doesn't. The next plugin update ships with heavier code. A second plugin activates a background process that didn't exist before. Your theme framework starts loading a new library. Now you're at 256MB and you hit the ceiling again.</p>

<p>So you bump it to 512MB. Then you're on shared hosting and your host caps you at 256MB regardless of what you put in wp-config. So you upgrade your hosting plan. Now you're paying more every month because a plugin you didn't write decided to use more memory than it used to.</p>

<p>The ceiling keeps coming back because you have no control over what your plugins consume. Every update is a gamble. Every new plugin you install adds another process to the execution chain, another potential ceiling hit, another Tuesday morning where your site goes white and you spend an hour figuring out which of your 23 plugins broke this time.</p>

<h2>What CARL does.</h2>

<p>CARL generates static PHP files. When a visitor hits one of your pages, the server reads a file and returns it. There's no plugin execution chain running on every request, no memory ceiling to exhaust, no update lottery to play.</p>

<p>There are no plugins on the front end of a CARL site. The page was built once, written to disk, and that's what gets served. A visitor can't trigger a 500 because there's nothing executing that could crash.</p>

<p>You still update CARL when new versions ship. But a CARL update doesn't touch your generated pages. Your site stays online regardless.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-5xx-errors-keep-your-site-online-through-every-upd-1775263424.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>WordPress Theme Updates Break Sites | Fix It or Escape | CARL</title>
    <link>https://carlriedel.com/wordpress/issues/wordpress-theme-updates.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/issues/wordpress-theme-updates.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-theme-updates-break-sites-fix-it-or-escape-carl-1775349695.webp" alt="WordPress Theme Updates Break Sites | Fix It or Escape | CARL" style="max-width:100%;height:auto;">
<h1>WordPress Theme Updates Break Sites. Here's Why That's a Design Flaw.</h1>

<p><em>By Carl Riedel, Builder of CARL, Recovering WordPress Survivor</em></p>

<p>You wake up, check your site, and it's gone. Not hacked. Not down. Just broken. A white screen where your homepage used to be, or a layout so mangled it looks like someone dropped your site from a 10th floor window.</p>

<p>You didn't do anything. You were asleep. WordPress did this to you.</p>

<h2>What Actually Happens During a Theme Update</h2>

<p>When WordPress updates your theme, it overwrites the theme files on your server. Every customisation you made directly to those files dies in the process. Every tweak, every CSS hack, every line of PHP you added to <code>functions.php</code> — gone.</p>

<p>If your theme and a plugin have been working together through a shared hook or function, a theme update can pull that hook out from under the plugin mid-air. The plugin crashes. Your site crashes with it.</p>

<p>This isn't a bug. It's how WordPress is built.</p><p><img src="/images/wordpress-theme-updates-break-sites-fix-it-or-escape-carl-1775349695.webp" alt="Wordpress Theme Updates" class="img-fluid" style=""><br></p>

<h2>Child Themes Are the "Official" Fix. They're Also a Trap.</h2>

<p>WordPress developers will tell you to use a child theme. A child theme inherits from the parent theme but keeps your customisations separate, so updates don't wipe them out.</p>

<p>That's true. It's also a lot of work most site owners were never told about when they started. And it still doesn't protect you when the parent theme changes a template file your child theme depends on.</p>

<p>A theme update can change the structure of a template so completely that your child theme's overrides break anyway. You're back to square one, still debugging at midnight.</p>

<h2>The Real Problem Is Dependency</h2>

<p>Your WordPress site depends on a theme you don't control, built by a developer you've never met, updated on a schedule you weren't told about, tested on a setup that isn't yours.</p>

<p>Every update is a gamble. Most of the time you win. Sometimes you don't. And when you don't, your site is down, your traffic is bleeding out, and you're on Google trying to figure out what <code>Fatal error: Cannot redeclare register_block_style()</code> means.</p>

<p>I've been there 18 times. Literally 18 sites in a single day from 1 plugin update alone. I built CARL because I refused to go back.</p>

<h2>What CARL Does Instead</h2>

<p>CARL doesn't have themes in the WordPress sense. It has templates — clean PHP files that you control completely. They live on your server. Nobody updates them without your say-so.</p>

<p>When you generate a page in CARL, it writes a static PHP file to your server. That file doesn't depend on a theme loading correctly, a plugin being active, or a function being registered at the right moment in the WordPress boot sequence. It just loads. Every time. Instantly.</p>

<p>Your layout is your layout. It stays exactly the way you built it until you change it yourself.</p>

<h2>What To Do If You're Still on WordPress</h2>

<p>If you can't move yet, here's the minimum you should do before any theme update:</p>

<p>Take a full backup first. Not just a database backup. A full file backup including your <code>wp-content</code> folder. Then test the update on a staging environment if your host provides one. If you don't have staging, you're one update away from a very bad morning.</p>

<p>Turn off auto-updates for themes completely. Go to Appearance → Themes, click your active theme, and disable automatic updates. Do the same for every plugin. Manual updates are slower. They're also survivable.</p>

<p>And if you're running customisations directly in your theme files rather than a child theme, stop. Move everything to a child theme now, before the next update hits.</p>

<h2>Or Just Stop Playing the Game</h2>

<p>Every workaround for WordPress theme updates is a workaround for a problem that shouldn't exist. You're patching over a design flaw that's been in WordPress since the beginning.</p>

<p>CARL was built so site owners could stop managing infrastructure and start managing content. No theme updates to worry about. No plugin conflicts to debug. No staging environments to maintain just to survive a routine update.</p>

<p>Your templates are yours. Your content is yours. Your site stays up.</p>

<p><strong>Ready to build a site that can't be taken down by a student's plugin?</strong></p>
<p><a href="/members/register.php" class="btn btn-success btn-lg">Get Free Access to CARL</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-theme-updates-break-sites-fix-it-or-escape-carl-1775349695.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Static Site Generator | With a Full CMS Behind It | CARL</title>
    <link>https://carlriedel.com/features/static-site-generator.php</link>
    <guid isPermaLink="true">https://carlriedel.com/features/static-site-generator.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/static-site-generator-with-a-full-cms-behind-it-carl-1776028749.webp" alt="Static Site Generator | With a Full CMS Behind It | CARL" style="max-width:100%;height:auto;">
<h1>Static Site Generator With a Full CMS Behind It</h1>

<p>Most static site generators make you choose between speed and convenience. Fast pages or a proper content management system. CARL gives you both.</p>

<p>When you publish a page in CARL, it <a href="/features/page-generator.php">generates a real PHP file</a> and writes it directly to your server. Visitors get that finished file instantly. No database query on the visit. No CMS runtime is assembling your page on the fly. Just a clean, fast page that was already built.</p>

<p>And unlike every other static site generator on the market, CARL comes with a full CMS, a members area, affiliate link tracking, email list building, AI-powered SEO, and scheduled publishing. All built in. One license.</p>

<img src="https://carlriedel.com/images/wordpress-alternative-own-your-site-drop-the-platform-carl-1774904678.webp" "static="" site="" generator="" with="" a="" full="" cms"="" style="width: 1028.2px;"><h2><br></h2><h2 style="text-align: center; ">[CTA:1]</h2><h2>How CARL Generates Your Pages</h2>

<p>Open the editor, write your content, set your title, meta description, and template, then click Generate. CARL builds a complete PHP file from your content and saves it to your server.</p>

<p>That file is what your visitors get. Pre-built, sitting on your server, serving instantly without touching the database on every visit.</p>

<p>Your pages also continue to work independently of CARL itself. Remove the admin panel and every page you've ever published keeps serving visitors perfectly. No platform dependency. No subscription keeping your site alive. The files are yours, on your server, from the moment you generate them.</p>

<h2>Speed That Doesn't Need a Cache Plugin</h2>

<p>Traditional CMS platforms build your page from scratch on every single visit. The server queries the database, loads plugins, assembles HTML, and sends it to the browser. This takes time and creates overhead that no amount of caching fully solves.</p>

<p>CARL sites don't have this problem because there's nothing to cache. The page was already built when you published it. Visitors get a file. That's it.</p>

<p><a href="/wordpress/wordpress-too-slow.php">Pages load faster than WordPress</a> on identical hardware. Google's Core Web Vitals metrics reflect this. Search engines reward it.</p>

<h2>Security With Almost Nothing to Attack</h2>

<p>Static file generation removes most of the attack surface that makes CMS platforms vulnerable. There's no active database query on the front end of your live pages. No xmlrpc.php getting brute-forced. No REST API endpoints broadcasting your site structure. No <a href="/wordpress/wordpress-plugin-danger.php">plugin ecosystem for a bad actor to exploit</a>.</p>

<p>CARL has no plugins. Every line of code was written by one person and is under direct, ongoing control. There's no third-party update pipeline, no acquisition targets sitting inside your website, and no silent backdoors waiting in a free plugin someone just sold to the wrong buyer.</p>

<h2>A CMS That Actually Manages Your Content</h2>

<p>Most static site generators require you to work in code. Markdown files, command line tools, version control systems. Powerful for developers. Impractical for anyone who just wants to publish content.</p>

<p>CARL has a full browser-based admin panel. Write in the built-in editor or paste in formatted HTML from any tool you already use. Google Docs, Notion, Claude, a text file: CARL has a dedicated one-click paste button that brings formatted content in cleanly, preserving your headings, lists, links, and layout exactly as you wrote them.</p>

<p>Pages, drafts, <a href="/features/scheduled-publishing.php">scheduled posts</a>, templates, images, subscribers, members, affiliate links: all managed from one dashboard without touching a command line.</p>

<h2>AI-Powered SEO Built Into the Publishing Workflow</h2>

<p>Before you publish, CARL's <a href="/features/ai-schema-generator.php">AI Schema Generator</a> reads your full article and generates complete schema markup, Open Graph tags, Twitter card settings, canonical URLs, and meta descriptions with one click.</p>

<p>This isn't a template filler. The AI reads your content and writes schema that reflects what your page is genuinely about. Then CARL notifies Google's sitemap automatically every time you publish.</p>

<p>After publishing, the built-in Site Health tool reads your actual generated files, exactly what the browser sees, and checks that everything landed correctly. Meta tags, headings, canonical URLs, schema, broken links. Most SEO tools check your dashboard settings. CARL checks the real page.</p>

<h2>Membership and Premium Content, Built In</h2>

<p>CARL includes a <a href="/features/members-area.php">full membership system</a> with free and premium content tiers, session management, subscriber linking, and approval mode. Protect any page behind a login with a single dropdown selection in the editor.</p>

<p>Member accounts live on your server in your database. No third-party membership platform. No <a href="/wordpress/wordpress-plugin-costs.php">$179/year plugin</a>. No platform taking a cut of your revenue or changing its terms overnight.</p>

<h2>Affiliate Link Tracking, Built In</h2>

<p>Every <a href="/features/affiliate-link-tracker.php">affiliate link tracked through CARL</a> records the source page, traffic origin, geographic location, and UTM data for each click. Change a destination URL once and it updates across your entire site instantly.</p>

<p>The data lives in your own database from the first click. No Pretty Links subscription at $99/year. No third-party dashboard to log into. Your click data, on your server, always.</p>

<h2>Your Own Templates, From Any HTML Source</h2>

<p>Templates in CARL are standard HTML files. Any free template from BootstrapMade, ThemeForest, or anywhere else on the web works with CARL out of the box. Download it, mark the spots where your content, header, navigation, and sidebar go, and CARL treats it as a native template from that point forward.</p>

<p>Repeated elements like headers, footers, navigation, and sidebars live in include files. Edit one file and the change is live across every page that references it the moment a visitor loads the page. No batch regeneration. No manual updates across hundreds of pages.</p>

<p>If you've been building sites in HTML or PHP by hand and the maintenance overhead was the only thing slowing you down, CARL removes that bottleneck without changing the architecture that was already working.</p>

<h2>Email List Building, Built In</h2>

<p>Every subscriber hits your own database the moment they sign up, before the data goes anywhere else, then <a href="/features/email-subscribers.php">syncs to Kit</a>. If the sync fails, the subscriber is still in your database with a one-click retry.</p>

<p>You own your list from the first signup. Not after you export it from someone else's platform.</p>

<h2>One License. Every Feature.</h2>

<p>CARL runs on standard cPanel shared hosting. PHP 8.3, MariaDB, Apache. No monthly platform fees. No per-site charges. No plugin subscriptions layered on top.</p>

<p>The average WordPress site costs <a href="/wordpress/wordpress-plugin-costs.php">£500 to £1,000 per year in plugin subscriptions</a> to do what CARL does in a single license. The SEO plugin. The membership plugin. The affiliate tracker. The caching plugin. The security plugin. Each one with its own renewal date and its own invoice.</p>

<p>CARL replaces all of it.</p><div style="text-align: center;">[CTA:1]</div>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/static-site-generator-with-a-full-cms-behind-it-carl-1776028749.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Unable To Log Into Wordpress After Plugin Updates</title>
    <link>https://carlriedel.com/wordpress/unable-to-log-into-wordpress-after-plugin-updates.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/unable-to-log-into-wordpress-after-plugin-updates.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/pending-plugin-updates.webp" alt="Unable To Log Into Wordpress After Plugin Updates" style="max-width:100%;height:auto;">
<h1>Unable To Log into WordPress After Plugin Updates</h1><p>This post is a perfect example of why I have such a healthy dislike for WordPress. Yesterday, Sunday morning, April 12th, 2026, I logged into one of my longtime WordPress websites and noticed I had 10 plugins pending an update. Nothing unusual there. So I FIRST made a complete backup of the entire site, then updated all the plugins.</p><p>Still, nothing unusual there.</p><p><img src="/images/pending-plugin-updates.webp" alt="Wordpress Plugins Pending Update" class="img-fluid" style=""></p><p>Later that evening, I decided to post my take on a news article I had read. Lo and behold, I was unable to log into my own website. I suspected it might have something to do with an .htaccess rule I had put in to allow only MY IP to log in, since I had once found that thousands of spam user accounts had been created on my site.</p><p><br>Then I remembered I had removed that rule after blocking user account creation on that site entirely. I checked anyway. Nope. It was not my rule blocking me. What I did find in my cPanel was error log after error log. Something had gone horribly wrong with the update.</p><p><br>I cannot blame ANY plugin, simply because I have exactly ZERO idea which one, or even which COMBINATION, is at the root of this issue. </p><p><img src="/images/unable-to-log-into-wordpress-admin-after-plugin-updates.webp" alt="unable to log into wordpress admin after plugin updates" class="img-fluid" style=""><br>I'm lucky I was careful enough to make a full site backup before updating those plugins. That gives me the option to delete the entire Wordpress installation, reinstall Wordpress and restore my backup. However, like most WP sites that have been around for a while, that thing is an almost 1.5 Gig bloated upload over my 17.6 Mbps island upload connection; it's NOT going to be any fun waiting on that backup to finish.</p><p><img src="/images/upload-speed.webp" alt="17.60 Mbps Upload Speed Internet Connection" class="img-fluid" style=""></p><h2>This Is Why I Built C.A.R.L</h2><p>C.A.R.L has no plugins, no third-party software that may end up "fighting" each other. Everything is built in right from the get-go. And even IF there ever WAS going to be a conflict, I would have seen it FIRST and dealt with it before it ever got to you.  There can be no "signup" spammers creating accounts on your C.A.R.L install. </p><p><br>I built the C.A.R.L CMS to be bulletproof! You get the full functionality of a CMS, without any of the associated headaches! <b>One License gives you unlimited installs.</b></p><p style="text-align: center; ">[CTA:1]</p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/pending-plugin-updates.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Creating Reusable Plugins For CARL</title>
    <link>https://carlriedel.com/videos/creating-reusable-plugins-for-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/videos/creating-reusable-plugins-for-carl.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://i.ytimg.com/vi/s1ijpNjJ0CE/maxresdefault.jpg" alt="Creating Reusable Plugins For CARL" style="max-width:100%;height:auto;">
<h1>🎬 Building Reusable Plugins for C.A.R.L. — Seed Starting Calculator Demo</h1>

<p>Creating plugins for the C.A.R.L. Static Site Generator CMS is faster, simpler, and more secure than building plugins for WordPress. In this video I walk you through exactly how I built a fully interactive Seed Starting Date Calculator as a single reusable include file and embedded it across multiple pages with one line of PHP.</p>

<p>No plugin marketplace. No database queries. No security vulnerabilities from third-party code. Just a clean, self-contained HTML, CSS, and JavaScript file that drops into any page your CMS generates.</p>

<h2>What You Will See in This Video</h2>
<ul>
  <li>How C.A.R.L. include files work as lightweight reusable components</li>
  <li>Building the Seed Starting Date Calculator from scratch with data for 26 crops</li>
  <li>How one include file powers multiple pages across your entire site</li>
  <li>Why static site generation eliminates the attack surface that makes WordPress plugins a security risk</li>
  <li>How to manage, edit, and update your includes from inside the C.A.R.L. admin panel</li>
</ul>

<h2>About the Calculator in This Video</h2>
<p>The Seed Starting Date Calculator covers 26 crops across vegetables, herbs, and fruit. It dynamically calculates precise indoor start dates, transplant dates, direct-sow dates, and estimated harvest windows from a last-frost date input, with full support for Northern, Southern, and Tropical growing regions. The entire tool is a single include file with no external dependencies.</p>

<h2>About C.A.R.L.</h2>
<p>C.A.R.L. is a lightweight static site generator CMS built for speed, security, and simplicity. If you are tired of WordPress bloat, plugin conflicts, and constant security patches, this video shows you a completely different way to build and manage a content-driven website.</p>

<h2>Links Mentioned in This Video</h2>
<ul>
  <li><a href="https://carlriedel.com" target="_blank" rel="noopener">C.A.R.L. CMS — carlriedel.com</a></li>
  <li><a href="https://microsteading.org" target="_blank" rel="noopener">Microsteading.org — live site built on C.A.R.L.</a></li>
  <li><a href="https://microsteading.org/calculators/seed-starting-calculator.php" target="_blank" rel="noopener">Live Seed Starting Date Calculator</a></li>
  <li><a href="https://microsteading.org/calculators/" target="_blank" rel="noopener">Full Calculators Suite on Microsteading.org</a></li>
</ul>

<h2>Follow Along</h2>
<ul>
  <li><a href="https://carlriedel.com/go/youtube" target="_blank" rel="noopener">Subscribe on YouTube</a></li>
  <li><a href="https://carlriedel.com/go/facebook-page" target="_blank" rel="noopener">Follow on Facebook</a></li>
  <li><a href="https://carlriedel.com/go/x" target="_blank" rel="noopener">Follow on X</a></li>
</ul>]]></content:encoded>
    <enclosure url="https://i.ytimg.com/vi/s1ijpNjJ0CE/maxresdefault.jpg" type="image/jpeg" length="0"/>
  </item>
  <item>
    <title>Lynchburg Movers Client Microsite Fully Done in CARL SSG CMS</title>
    <link>https://carlriedel.com/videos/client-microsite-done-in-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/videos/client-microsite-done-in-carl.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://i.ytimg.com/vi/kkoSDjuRGGs/maxresdefault.jpg" alt="Lynchburg Movers Client Microsite Fully Done in CARL SSG CMS" style="max-width:100%;height:auto;">
<h1>Lynchburg Movers Client Microsite Fully Done in CARL SSG CMS</h1>
<p>This is a real client site. A local moving company in Lynchburg, Virginia, that's been online since 2014. In this video, I rebuild it completely on CARL and push it live in under 2 minutes. All pages regenerated, all schema done, all location pages automated. One click.</p>]]></content:encoded>
    <enclosure url="https://i.ytimg.com/vi/kkoSDjuRGGs/maxresdefault.jpg" type="image/jpeg" length="0"/>
  </item>
  <item>
    <title>Creating Custom Templates with CARL CMS</title>
    <link>https://carlriedel.com/videos/creating-custom-templates-with-carl-cms.php</link>
    <guid isPermaLink="true">https://carlriedel.com/videos/creating-custom-templates-with-carl-cms.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://i.ytimg.com/vi/r8Ac0OXrd6g/maxresdefault.jpg" alt="Creating Custom Templates with CARL CMS" style="max-width:100%;height:auto;">
<h1>Creating Custom Templates with CARL CMS</h1><p>Watch me create a custom landing page template right within the C.A.R.L Content Management System. You don't have to bring in your own page templates anymore; you can build them yourself right within CARL.</p><p>You can still bring in your own HTML, of course, but when you generate your page templates in CARL, they will already have the "variables" in the correct spot. So, for example, the {{TITLE}} tag would be correctly placed between the < title >{{TITLE}}< /title > section.</p><p>No guesswork, clean and simple.</p>]]></content:encoded>
    <enclosure url="https://i.ytimg.com/vi/r8Ac0OXrd6g/maxresdefault.jpg" type="image/jpeg" length="0"/>
  </item>
  <item>
    <title>Virginia Moving Company Nearly Doubles Customer Calls in Two Weeks After Switching to CARL | the Bold New Alternative to WordPress</title>
    <link>https://carlriedel.com/news/virginia-moving-company-nearly-doubles-customer-calls-in-two-weeks-after-switching-to-carl-the-bold-new-alternative-to-wordpress.php</link>
    <guid isPermaLink="true">https://carlriedel.com/news/virginia-moving-company-nearly-doubles-customer-calls-in-two-weeks-after-switching-to-carl-the-bold-new-alternative-to-wordpress.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/logo-press.webp" alt="Virginia Moving Company Nearly Doubles Customer Calls in Two Weeks After Switching to CARL | the Bold New Alternative to WordPress" style="max-width:100%;height:auto;">
<p><strong>PRESS RELEASE </strong><strong>🔴 FOR IMMEDIATE RELEASE</strong></p>
<p>May 1, 2026</p><p><img src="/images/logo-press.webp" alt="Virginia Moving Company Nearly Doubles Customer Calls in Two Weeks After Switching to CARL" class="img-fluid" style=""><br></p>
<h1><strong>Virginia Moving Company Nearly Doubles Customer Calls in Two Weeks After Switching to CARL — the Bold New Alternative to WordPress</strong></h1>
<p><em>Lynchburg Movers — which abandoned WordPress after a devastating hack left them on plain HTML for nearly a decade — is the first commercial client to launch on CARL, a new CMS platform purpose-built for speed, security, and real business results.</em></p>
<p><strong>LYNCHBURG, Virginia</strong>, May 1, 2026 — When Armando Corrales, owner of <strong><a href="https://lynchburgmovers.net/" target="_blank">Lynchburg Movers</a></strong>, lost his WordPress website to hackers years ago, he made a decision that countless small business owners quietly make every year: he walked away from the platform and never went back. His company's site has run on plain, hand-coded HTML ever since — functional, frozen in time, and gradually falling further behind his competitors.</p>
<p>That changed in mid-April 2026, when Lynchburg Movers became the first commercial client to launch on <strong>CARL</strong> — the Content Automation Ranking Launchpad — an independently developed website management system created by <strong>Carl Riedel</strong>, Founder of CARL. The results have been swift and measurable. Since launching the new site, inbound call volume has jumped from an average of four to five calls per week to nine to ten calls per week — an increase of nearly 100 percent in under two weeks.</p>
<p><em>The turnaround did not come from an advertising campaign. It did not come from a social media push. It came from a better website.</em></p>
<blockquote>
<h3><em>"I honestly wasn't expecting results this fast. The phone is ringing more often, and when people land on the site, it looks like a real professional business — because it is now</em><em>. But the thing that really changed the game for </em><em>me is the photo quote system. My customers just take pictures of their stuff right from their phone, send them </em><em>through the website, and I get everything I need to give them a price. No back-and-forth, no wasted calls.</em></h3>
<h3><em>We're closing more jobs with less effort."</em></h3>
<h3><em>— Armando Corrales, Owner, Lynchburg Movers</em></h3>
</blockquote>
<h2><strong>From WordPress Hack to HTML Standstill — And Now, a Comeback</strong></h2>
<p>Lynchburg Movers' original website was built on WordPress — at the time, the obvious choice for small business owners who wanted an online presence without a dedicated developer. But WordPress is the world's most targeted CMS platform, and like thousands of businesses before them, Lynchburg Movers paid the price. After the hack, Corrales rebuilt the site in plain HTML and kept it running that way for the better part of a decade. It worked. But it wasn't growing.</p>
<p>The rebuild on CARL changed that immediately. The new site is faster, fully optimized for search engines, and built on a platform that is architecturally immune to the class of attack that ended the WordPress chapter in the first place.</p>
<h2><strong>A Custom Feature That Solves a Real Problem</strong></h2>
<p>The increase in call volume is only part of the story. As part of the Lynchburg Movers rebuild, Riedel developed a <strong>photo quote system</strong> — a custom feature that allows prospective customers to photograph the items they need moved directly from their mobile device and submit those images through the website to request a quote. Corrales receives an email with a direct link to view every photo, assess the job, factor in distance, and reply with a price. No phone tag. No in-person estimates for straightforward jobs. Just faster decisions and more closed bookings.</p>
<p>The feature was not built from a marketplace plugin or a third-party subscription. It was built directly within CARL's extensible architecture — a core design principle of the platform that allows any user to develop and deploy custom functionality to their own site. Riedel built the photo quote tool after observing how Corrales was already handling customer inquiries manually over the phone.</p>
<p><em>"I heard Armando telling customers to take photos and text them to him," said Riedel. "That told me exactly what the website needed to do. CARL is built so that any user — not just developers — can add features like this using Claude AI. What we built for Armando is now available to any CARL site. That's the whole point of the platform."</em></p>
<h2><strong>Why CARL Is Different — and Why It Matters for Small Businesses</strong></h2>
<p>WordPress currently powers approximately 43 percent of all websites on the internet. It is also responsible for a disproportionate share of small-business website hacks, data breaches, and plugin-induced outages that quietly cost business owners time, money, and customers every single day.</p>
<p>CARL takes a fundamentally different approach. Rather than building each page dynamically every time a visitor arrives — querying a database, loading plugins, running real-time server-side code — CARL generates complete, finished web pages at the moment of publication and saves them directly to the server as static files. When a visitor arrives, they receive a fully built page instantly. There is nothing to query, nothing to inject, and no plugin ecosystem to compromise.</p>
<p>The result is a website that loads dramatically faster than a comparable WordPress site, ranks more competitively on Google, and presents no meaningful attack surface for the automated exploit tools that took down Lynchburg Movers' original site.</p>
<p> </p>
<blockquote>
<h3><em>"WordPress had its day. It served millions of small businesses well, and it deserves credit for that. But the </em><em>web has changed, the threats have changed, and what small business owners actually need from a website has </em><em>changed. CARL was built for where we are now — not where we were twenty years ago. Armando's results in </em><em>the first two weeks are exactly what this platform was designed to deliver."</em></h3>
<h3><em>— Carl Riedel, Founder, CARL</em></h3>
</blockquote>
<p> </p>
<h2><strong>Built for the AI Era — and Built to Be Extended</strong></h2>
<p>CARL integrates directly with Claude AI for automated SEO schema generation, Open Graph optimization, and content management tasks that typically require specialist knowledge or expensive plugins on other platforms. More significantly, CARL's architecture is designed to be extended — any user can build and deploy custom features, tools, calculators, and interactive widgets using Claude AI, without touching the platform's core code.</p>
<p>The photo quote tool built for Lynchburg Movers is a working example of this extensibility. It is now part of the CARL feature library, available to any CARL site owner who wants it. This model — where one client's real-world problem becomes a solution available to the entire user base — is intentional. CARL is built to grow in response to its users' needs, not through a marketplace of paid add-ons.</p>
<h2><strong>Availability</strong></h2>
<p>CARL is currently available to individual website owners and small businesses worldwide. Carl Riedel, who is based in Willemstad, Curaçao, and works extensively across Europe and Latin America, developed the platform independently as a direct answer to what he describes as the bloated, vulnerable, and unnecessarily complex state of mainstream web publishing.</p>
<p>For small business owners who have ever paid for a plugin that broke their site, lost a website to a hack, or simply felt that the platforms they rely on were built for the platform's growth — not theirs — CARL represents something different.</p>
<p>More information is available at <strong>https://carlriedel.com</strong>.</p>
<h3><strong>About CARL</strong></h3>
<p>CARL — the Content Automation Ranking Launchpad — is a website management system built for speed, security, and real business results. Unlike database-driven CMS platforms, CARL generates static, server-ready pages at the point of publication, delivering faster load times, stronger SEO performance, and a fundamentally more secure web presence. The platform integrates Claude AI for schema generation and SEO automation, and features a built-in link tracker, affiliate management tools, email subscriber management, a membership system, and an extensible architecture that allows any user to build and deploy custom features. <strong><em>WordPress had its day. Now it's time for something better.</em></strong> Visit https://carlriedel.com.</p>
<p><strong>###</strong></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/logo-press.webp" type="image/webp" length="0"/>
  </item>
  <item>
    <title>Wordpress vs Headless CMS vs CARL</title>
    <link>https://carlriedel.com/wordpress/wordpress-vs-headless-cms-vs-carl.php</link>
    <guid isPermaLink="true">https://carlriedel.com/wordpress/wordpress-vs-headless-cms-vs-carl.php</guid>
    <pubDate>Sat, 30 May 2026 09:01:07 +0000</pubDate>
    <dc:creator>Carl Riedel</dc:creator>
    <description></description>
    <content:encoded><![CDATA[<img src="https://carlriedel.com/images/wordpress-vs-headless-cms-vs-carl-and-find-out-why-carl-wins-1778863990.webp" alt="Wordpress vs Headless CMS vs CARL" style="max-width:100%;height:auto;">
<h1>WordPress vs. Headless CMS vs CARL</h1>
<blockquote>No matter how you toss it or turn it, C.A.R.L users WIN EVERY TIME!</blockquote>

<h2>The Problem With WordPress, In Case You Need a Reminder</h2>

<p>If you're researching headless CMS platforms, WordPress has already let you down. Maybe it <a href="https://carlriedel.com/wordpress/wordpress-hacked.php">got hacked</a>. Maybe a plugin update destroyed your site on a Friday night. Maybe you got tired of paying for security, backup, caching, and SEO plugins, only to watch them fight each other anyway.</p>

<p>WordPress runs on a model with a structural flaw baked into it. Every page load fires a database query, runs PHP, loads plugins, and assembles HTML from scratch. Every single visit. That runtime is the attack surface. The plugin ecosystem is the open door. Getting hacked was never an if. It was always a when.</p>

<p>So you started looking for alternatives. Someone mentioned headless CMS. Here's what that actually means.</p>

<p><img src="/images/wordpress-vs-headless-cms-vs-carl-and-find-out-why-carl-wins-1778863990.webp" alt="WordPress vs. Headless CMS vs CARL" class="img-fluid"><br></p>

<h2>What a Headless CMS Actually Is</h2>

<p>A traditional CMS has two parts: the back end, where you manage content, and the front end (the website your visitors actually see). WordPress controls both. They're married to each other.</p>

<p>A headless CMS removes the front end entirely. That's where the name comes from: a body with no head. It gives you a backend for managing content, delivers that content via an API, and stops there. It doesn't build a website. It doesn't render anything in a browser. It hands you raw content data and leaves the rest to you.</p>

<p>You build your own front end separately, usually with React, Next.js, or Vue.js. Your front-end calls the CMS API every time it needs content, assembles the page, and delivers it to the visitor. Contentful works this way. Sanity works this way. Strapi works this way. They manage content. The website is your engineering problem.</p>

<h2>Who This Architecture Is Actually For</h2>

<p>Headless makes genuine sense in specific situations. Large enterprise teams where content editors, front-end developers, and back-end engineers all work separately and need a clean separation of concerns. Businesses publish the same content across multiple platforms simultaneously (a website, a mobile app, a digital display), where one content source feeds many different outputs. Organizations with dedicated engineering teams that can build, maintain, and host a custom front-end application indefinitely.</p>

<p>If that's your situation, headless might be the right call.</p>

<p>If you're an SEO professional, a web designer, an affiliate marketer, a local business owner, or an indie developer who wants fast, secure websites that rank and generate revenue, headless solves a problem you don't have while creating several you don't want.</p>

<h2>What Headless Actually Costs</h2>

<p>The marketing around headless CMS platforms is polished, and the demos are impressive. What the demos don't show is the total cost of ownership once you factor in everything the architecture actually requires.</p>

<p>Take Contentful. Their Lite plan (the first paid tier, described as being for "smaller businesses managing a single project") costs <strong>$300 a month. $3,600 a year.</strong> That buys you a place to store content. It doesn't include hosting for your front-end application, developer time to build and maintain that front-end, the JavaScript framework you need to learn or hire for, or the additional infrastructure for handling API calls at scale.</p>

<p>SEO is its own headache. Headless front ends require significant extra work to handle meta tags, schema markup, canonical URLs, and structured data correctly. That work either costs developer hours or gets done badly.</p>

<p>A realistic headless setup for a serious content site (content management, front-end hosting, developer time, SEO infrastructure) runs several thousand dollars a year at minimum. And when the API goes down, your website goes down with it. Every page on your site has a live dependency on an external service running correctly, every time a visitor arrives.</p>

<h2>What CARL Does Instead</h2>

<p>CARL is a PHP application platform that generates complete, finished files and writes them directly to your server. More direct and more durable than anything headless or traditional CMS architecture produces.</p>

<p>When you create a page in CARL and click Generate, that file contains everything: content, meta tags, schema markup, navigation, and footer. A real file on your real server.</p>

<p>When a visitor arrives, your server delivers that file. The page is already there, finished, waiting. There's nothing to query, nothing to call, nothing to assemble. A visitor gets a file. The job is done.</p>

<p>CARL also handles what the headless stack leaves for you to figure out. AI-powered schema generation produces complete JSON-LD structured data, Open Graph tags, Twitter cards, and a canonical URL in one click. Affiliate link tracking, email subscriber management, a members area with free and premium tiers, scheduled publishing with automatic sitemap updates, all of it built in. The hosting is standard cPanel shared hosting. The hosting you already have is almost certainly enough.</p>

<p>The include file system goes further. Entire applications (lead marketplaces, booking systems, calculators, custom forms) can be added to any CARL page with a single line of PHP. The CMS and your custom business logic live in the same file structure on your server, which you own.</p>

<h2>WordPress vs Headless CMS vs CARL: The Honest Comparison</h2>

<div class="table-responsive my-4">
  <table class="table table-bordered align-middle" style="font-size:0.92rem;">
    <thead>
      <tr style="background:#1B1B2F;color:#fff;">
        <th style="width:28%;padding:14px 16px;">Feature</th>
        <th style="width:24%;padding:14px 16px;text-align:center;">WordPress</th>
        <th style="width:24%;padding:14px 16px;text-align:center;">Headless CMS<br><small style="font-weight:400;opacity:0.8;">(e.g. Contentful)</small></th>
        <th style="width:24%;padding:14px 16px;text-align:center;color:#D4AF37;">CARL</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Annual cost (per site)</td>
        <td style="text-align:center;">£500–900/yr<br><small class="text-muted">hosting + plugins + theme</small></td>
        <td style="text-align:center;">$3,600+/yr<br><small class="text-muted">CMS only (front end extra)</small></td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">£527 once<br><small style="font-weight:400;color:#555;">unlimited sites, forever</small></td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Generates real files</td>
        <td style="text-align:center;">❌ Builds on every load</td>
        <td style="text-align:center;">❌ API on every load</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Real PHP files on disk</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">External dependency</td>
        <td style="text-align:center;">❌ Hundreds of plugins</td>
        <td style="text-align:center;">❌ External API required</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ None</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Security model</td>
        <td style="text-align:center;">⚠️ Attack surface on every load</td>
        <td style="text-align:center;">⚠️ API endpoint exposure</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Static file: nothing to attack</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Hosting required</td>
        <td style="text-align:center;">Managed WP hosting</td>
        <td style="text-align:center;">CMS + separate front end hosting</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">Standard cPanel shared hosting</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Built-in SEO tools</td>
        <td style="text-align:center;">⚠️ Plugin required</td>
        <td style="text-align:center;">❌ Build it yourself</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ AI schema generation built in</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Affiliate link tracking</td>
        <td style="text-align:center;">⚠️ Plugin required</td>
        <td style="text-align:center;">❌ Not included</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Built in</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Email subscribers</td>
        <td style="text-align:center;">⚠️ Plugin required</td>
        <td style="text-align:center;">❌ Not included</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Built in</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Members area</td>
        <td style="text-align:center;">⚠️ Plugin required</td>
        <td style="text-align:center;">❌ Not included</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Built in</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Scheduled publishing</td>
        <td style="text-align:center;">✅ Built in</td>
        <td style="text-align:center;">⚠️ Varies by platform</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Built in</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Custom application support</td>
        <td style="text-align:center;">⚠️ Plugin or custom dev</td>
        <td style="text-align:center;">⚠️ Custom front end dev</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ PHP include files</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">You own your data</td>
        <td style="text-align:center;">⚠️ Tied to host</td>
        <td style="text-align:center;">❌ Stored on their servers</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Your server. Your database.</td>
      </tr>
      <tr>
        <td class="fw-bold" style="background:#f8f9fa;">Unlimited installs</td>
        <td style="text-align:center;">✅</td>
        <td style="text-align:center;">❌ Per-site pricing</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">✅ Unlimited sites, one payment</td>
      </tr>
      <tr style="background:#fafafa;">
        <td class="fw-bold" style="background:#f8f9fa;">Technical skill required</td>
        <td style="text-align:center;">Low–Medium</td>
        <td style="text-align:center;">High: requires JS framework knowledge</td>
        <td style="text-align:center;font-weight:700;color:#1B6B3A;">Low: plain English interface</td>
      </tr>
    </tbody>
  </table>
</div>

<h2>Who Should Choose What</h2>

<h3>Choose WordPress if...</h3>
<p>You need a specific plugin that has no alternative, you're managing a large existing WordPress site you're not ready to migrate, or you have a client who specifically requires it. Go in clear-eyed about the security implications, keep everything updated obsessively, and treat backups as non-negotiable.</p>

<h3>Choose a headless CMS if...</h3>
<p>If you're a large enterprise publishing content across five or more platforms simultaneously, you have a dedicated front-end engineering team on staff, and $3,600 a year for content management is a rounding error in your budget. If all 3 of those are true, headless architecture is a genuinely powerful approach for your situation.</p>

<h3>Choose CARL if...</h3>
<p>You want fast, secure, SEO-optimized websites on hosting you already own, without a monthly platform fee, a plugin ecosystem to babysit, or your content data sitting on someone else's servers. Whether you're building one site or fifty, running affiliate content, local business sites, membership communities, or lead generation funnels, CARL is a complete platform for a single one-time payment. Unlimited sites. Unlimited domains. Client work included. Lifetime updates, because the developer runs his own sites on CARL and he's not going anywhere.</p>

<h2>The Bottom Line</h2>

<p>Headless CMS is a real solution for a narrow, specific use case. The marketing is excellent. The demos are compelling. For the vast majority of people building websites in 2026 (content sites, affiliate sites, local business sites, membership platforms) it adds cost, complexity, and external dependency in exchange for flexibility they'll never actually use.</p>

<p>CARL generates the file. Writes it to disk. Delivers it instantly. You own everything. You pay once. <a href="/pricing.php">See what's included.</a></p>]]></content:encoded>
    <enclosure url="https://carlriedel.com/images/wordpress-vs-headless-cms-vs-carl-and-find-out-why-carl-wins-1778863990.webp" type="image/webp" length="0"/>
  </item>
</channel>
</rss>