Author SHA1 Message Date
jknapp 8c515a8074 Merge branch 'main' into docs/bichon-opensource-link 2026-06-29 01:30:55 +00:00
shadowdaoandClaude Opus 4.8 5fdf55de7f docs: link Bichon to its open-source project
The archival-email page linked to https://anhonesthost.com/bichon/, which
does not exist. Point readers at the upstream open-source project instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 18:29:33 -07:00
jknapp f7d739fe2d Merge pull request 'docs(how-to): add 'Clear your site's cache' guide' (#7) from docs/clear-your-cache into main
Build and deploy / deploy (push) Successful in 25s
Reviewed-on: #7
2026-06-27 04:04:58 +00:00
shadowdaoandClaude Opus 4.8 858d505e7e docs(how-to): add 'Clear your site's cache' guide
Customer-facing troubleshooting for stale content: hard refresh (browser cache)
first, then purge LiteSpeed Cache via the WP plugin Toolbox for Optimized
Webserver sites, with a note that logged-in views are never cached. Cross-linked
from the Optimized Webserver add-on page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 21:02:17 -07:00
jknapp 240b6d392b Merge pull request 'docs(email): fix webmail-reachability note and SMTP port guidance' (#5) from docs/email-port-webmail-fixes into main
Build and deploy / deploy (push) Successful in 22s
Reviewed-on: #5
2026-06-23 22:15:01 +00:00
jknapp 9762822e97 Merge branch 'main' into docs/email-port-webmail-fixes 2026-06-23 22:14:52 +00:00
jknapp 54bee2cf55 Merge pull request 'docs(archival): retention is for the life of the account, not 14 days' (#6) from docs/archival-retention into main
Build and deploy / deploy (push) Successful in 24s
Reviewed-on: #6
2026-06-23 22:14:46 +00:00
shadowdaoandClaude Opus 4.8 ef4a4605b2 docs(archival): correct retention — kept for the life of the account
The add-on does not impose a 14-day window or offer "configurable retention."
Archived mail is retained for as long as the customer has an active account
with us. Replaced the inaccurate "14-day quick-restore window" and
"Configurable longer retention" highlights, and softened the compliance
use-case wording away from "fixed window."

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 15:13:55 -07:00
4 changed files with 192 additions and 4 deletions
@@ -15,10 +15,10 @@ import Support from '~/content/partials/support-link.mdx';
Archival email keeps a long-term, searchable copy of your mail **outside** the live mailbox. It's useful when:
- You need to retain mail beyond your live mailbox's storage cap.
- Compliance or policy requires you keep email for a fixed window.
- Compliance or policy requires you keep email long-term.
- You want a recovery option for mail you accidentally delete from the live mailbox.
It's powered by our [Bichon](https://anhonesthost.com/bichon/) archival service.
It's powered by the open-source [Bichon](https://github.com/rustmailer/bichon) archival service.
## How it's different from backups
@@ -33,8 +33,8 @@ You can use both — they cover different problems.
## What's included
- **Per-mailbox archive.** Enable on the mailboxes that need it, not the whole account. Your plan has an **archival slots** quota; the Email page shows current usage (e.g. `Archival: 0 of 0 mailboxes archived (no archival slots in your plan)` if you haven't added the add-on yet).
- **14-day quick-restore window.** Accidentally deleted mail is recoverable without staff help.
- **Configurable longer retention.** Set retention to match your policy.
- **Retained for the life of your account.** We keep your archive for as long as you have an active account with us — there's no 14-day limit or fixed expiry window.
- **Self-service restore.** Mail you accidentally delete from the live mailbox stays in the archive, so you can find and recover it yourself without staff help.
- **Independent password reset on the archive.** Grant audit access without disturbing the live mailbox.
## How to enable
@@ -83,6 +83,7 @@ For the site-switching steps, see [Switching a site's backend](/whp/how-to/switc
## Related
- [Clear your site's cache](/whp/how-to/clear-your-cache/) — what to do when a change doesn't show up right away.
- [Add-ons overview](/whp/add-ons/overview/)
- [Resource upgrades](/whp/add-ons/resource-upgrades/) — if you need more CPU or RAM rather than a faster cache layer.
- [Site Monitoring](/whp/add-ons/monitoring/) — pair with Optimized Webserver to catch any cache-related issues early.
@@ -0,0 +1,71 @@
---
title: Clear your site's cache
description: Seeing an old version of a page after making a change? Here's how to clear cached content so your updates show up.
sidebar:
order: 7
---
import { Steps, Aside } from '@astrojs/starlight/components';
import Support from '~/content/partials/support-link.mdx';
If you've changed something on your site but still see the old version — an updated page, a new image, a price, a published post — it's almost always **caching**: a saved copy of the page is being shown to make your site fast. Clearing the cache tells the system to build a fresh copy.
There are two places a saved copy can live: in **your browser**, and on the **server** (if your site uses our [Optimized Webserver](/whp/add-ons/optimized-webserver/) add-on with LiteSpeed Cache). Work through the steps below in order — the first one fixes most cases.
<Aside type="note">
When you're **logged in** to WordPress, we never serve you a cached page — you always see your site exactly as it is right now. So if a change looks missing while you're logged in, it's almost certainly your browser holding an old copy. Start with a hard refresh.
</Aside>
## Start with a hard refresh
A normal refresh often reloads the page from your browser's own saved copy. A *hard* refresh forces your browser to fetch everything fresh from the server.
<Steps>
1. Open the page that looks out of date.
2. Do a hard refresh:
- **Windows / Linux:** press `Ctrl` + `Shift` + `R`
- **Mac:** press `Cmd` + `Shift` + `R`
3. Still seeing the old version? Open the same page in a **private / incognito window** (which ignores your browser cache entirely). If it looks correct there, the issue was just your browser — clear your browser cache and you're done.
</Steps>
## Purge the server cache
If your site is on the **Optimized Webserver** add-on, pages are also cached at the server by LiteSpeed Cache. Most of the time this clears itself automatically — publishing a post, updating a page, or completing an order purges the right pages for you. Occasionally (after a large change, a theme edit, or a bulk update) you may want to clear it by hand.
For WordPress sites, you do this from the free **LiteSpeed Cache** plugin:
<Steps>
1. Sign in to your site's **WordPress admin** (`yourdomain.com/wp-admin`).
2. In the left menu, go to **LiteSpeed Cache → Toolbox**.
3. On the **Purge** tab, click **Purge All**. This drops every cached page; the next visitor to each page gets a freshly built copy.
</Steps>
<Aside type="tip">
There's also a shortcut in the black toolbar at the top of every WordPress admin page: the **LiteSpeed Cache** menu has a **Purge All** option you can use without leaving the page you're on.
</Aside>
<Aside type="caution">
The very first visit to each page after a purge runs at normal (uncached) speed while the fresh copy is built — then it's fast again. Don't judge your site's speed on that first load right after purging.
</Aside>
## If that didn't fix it
- **Your site isn't WordPress**, or you don't have the LiteSpeed Cache plugin — there's nothing for you to purge directly. Contact us and we'll clear the server-side cache for you.
- **You purged everything and still see the old version** — give it a moment and try a hard refresh again. If it persists, it may not be a cache issue at all (for example, a change that didn't actually save, or a content/plugin problem). Reach out and we'll take a look.
## Related
- [Optimized Webserver (OpenLiteSpeed + LSCache)](/whp/add-ons/optimized-webserver/) — what the server-level cache is and how to enable it.
- [Switching a site's backend](/whp/how-to/switching-site-backend/)
## Still stuck?
<Support />
+116
View File
@@ -0,0 +1,116 @@
/**
* Traffic analytics capture — for the June 2026 platform-updates blog post.
*
* Captures, as the demo customer:
* - traffic-analytics-overview.png View Traffic landing: Yesterday's Snapshot,
* Top URLs / Bandwidth Consumers, Daily Totals
* - traffic-analytics-day-detail.png the per-day drill-down: hourly request graph,
* top pages by views, top pages by bandwidth
*
* Viewport 1440x900, deviceScaleFactor 2, fullPage:false. Redacts the fleet
* server strip ("WHP-01" / "Welcome, ...") and the version-number footer; keeps
* the brand demo domain (whp-demo.anhh.co) visible on purpose. Read-only.
*
* Output goes to /workspace/blog-assets (this is a blog image, not a KB page).
*/
import { chromium, type Page } from 'playwright';
import { mkdir } from 'node:fs/promises';
import { resolve } from 'node:path';
const OUT_DIR = '/workspace/blog-assets';
function need(name: string): string {
const v = process.env[name];
if (!v) throw new Error(`missing env: ${name}`);
return v;
}
const BASE = need('WHP_BASE');
const USER = need('WHP_USER');
const PASS = need('WHP_PASS');
// Hide the server-identifying navbar strip and the version footer.
const HIDE_CSS = `.navbar-text, .brand-full, .navbar-brand { visibility: hidden !important; }`;
async function login(page: Page) {
await page.goto(`${BASE}/login.php`, { waitUntil: 'domcontentloaded' });
await page.fill('input[name="user"]', USER);
await page.fill('input[name="password"]', PASS);
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
}
async function redact(page: Page) {
await page.addStyleTag({ content: HIDE_CSS });
await page.evaluate(() => {
const swaps: [RegExp, string][] = [
[/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '<your-server>.cloud-hosting.io'],
[/WHP\d+(-[A-Z0-9]+)?\b/g, '<YOUR-SERVER>'],
[/whp\d+(-[a-z0-9]+)?\b/gi, '<your-server>'],
[/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '<server-IP>'],
[/demo-user/g, 'your-username'],
// Strip the release/version identifier from the footer.
[/Web Hosting Panel\s*-\s*\d{4}\.\d{2}\.\d+/gi, 'Web Hosting Panel'],
[/\b\d{4}\.\d{2}\.\d+\b/g, ''],
];
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
const nodes: Text[] = [];
let n: Node | null = walker.nextNode();
while (n) { nodes.push(n as Text); n = walker.nextNode(); }
for (const node of nodes) {
let v = node.nodeValue ?? '';
for (const [re, rep] of swaps) v = v.replace(re, rep);
if (v !== node.nodeValue) node.nodeValue = v;
}
});
}
async function main() {
await mkdir(OUT_DIR, { recursive: true });
const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({
ignoreHTTPSErrors: true,
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
});
const page = await ctx.newPage();
try {
await login(page);
// Resolve the demo site's traffic view via the "View Traffic" button.
await page.goto(`${BASE}/index.php?page=site-traffic`, { waitUntil: 'networkidle' });
await page.locator('a:has-text("View Traffic"), button:has-text("View Traffic")').first().click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const trafficUrl = page.url();
// 1. Overview — clip to the main content card so the snapshot + daily totals
// frame nicely without the empty side gutters.
await redact(page);
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(300);
await page.screenshot({ path: resolve(OUT_DIR, 'traffic-analytics-overview.png'), fullPage: false });
console.log('captured traffic-analytics-overview');
// 2. Day drill-down — click the 23rd (richest demo data), then clip to the
// "Breakdown for ..." card (hourly graph + top pages + bandwidth).
await page.locator('a:has-text("2026-06-23")').first().click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1500);
await redact(page);
const card = page.locator('#day-detail, .card:has-text("Breakdown for")').first();
await card.scrollIntoViewIfNeeded().catch(() => {});
await page.waitForTimeout(400);
if (await card.count()) {
await card.screenshot({ path: resolve(OUT_DIR, 'traffic-analytics-day-detail.png') });
} else {
await page.screenshot({ path: resolve(OUT_DIR, 'traffic-analytics-day-detail.png'), fullPage: false });
}
console.log('captured traffic-analytics-day-detail');
console.log('traffic url was', trafficUrl);
} finally {
await browser.close();
}
}
main().catch((err) => { console.error(err); process.exit(1); });