Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
113c795c83 | ||
|
|
0e3ae941eb | ||
|
|
1e3e0bd546 | ||
|
|
a28e20f881 | ||
|
|
063f992516 | ||
|
|
e4ea0484fd | ||
|
|
b00a5fd8cf | ||
|
|
425f364f48 | ||
|
|
ee6b6af107 | ||
|
|
f0ec3395ab | ||
|
|
8c515a8074 | ||
|
|
5fdf55de7f | ||
|
|
f7d739fe2d | ||
|
|
858d505e7e | ||
|
|
240b6d392b | ||
|
|
9762822e97 | ||
|
|
54bee2cf55 | ||
|
|
ef4a4605b2 | ||
|
|
3b31acbbac | ||
|
|
3a526783cb | ||
|
|
1769d5dc0b | ||
|
|
f1159867df | ||
|
|
a6269d18fd | ||
|
|
8f42adc799 | ||
|
|
7f63f064c9 | ||
|
|
c9a08313ca | ||
|
|
0b53569821 | ||
|
|
23cc5a887b | ||
|
|
03aa273100 | ||
|
|
da24dc8c67 | ||
|
|
08adca6955 | ||
|
|
6ee2cf13b0 | ||
|
|
33d1da92ca | ||
|
|
69439afe4a | ||
|
|
aeb033bae5 | ||
|
|
119d376029 | ||
|
|
8c965f76d2 | ||
|
|
6a0a461c26 | ||
|
|
ebbb75d7f5 | ||
|
|
7f81240d22 | ||
|
|
fef98357c7 | ||
|
|
5d829c44ba | ||
|
|
2cd10bc56d | ||
|
|
ccfe8bb649 | ||
|
|
c602b8f8f3 | ||
|
|
53bc37fd0d | ||
|
|
af94b72777 | ||
|
|
16e14019b7 | ||
|
|
e3b113cc2f | ||
|
|
748fcfeb6f |
@@ -0,0 +1,63 @@
|
|||||||
|
name: Build and deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Type and link check
|
||||||
|
run: npm run check
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Install lftp
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y --no-install-recommends lftp
|
||||||
|
|
||||||
|
- name: SFTP mirror via lftp
|
||||||
|
env:
|
||||||
|
SFTP_HOST: ${{ secrets.SFTP_HOST }}
|
||||||
|
SFTP_PORT: ${{ secrets.SFTP_PORT }}
|
||||||
|
SFTP_USER: ${{ secrets.SFTP_USER }}
|
||||||
|
SFTP_PASS: ${{ secrets.SFTP_PASS }}
|
||||||
|
SFTP_PATH: ${{ secrets.SFTP_PATH }}
|
||||||
|
run: |
|
||||||
|
lftp -e "
|
||||||
|
set sftp:auto-confirm yes;
|
||||||
|
set ssl:verify-certificate no;
|
||||||
|
open -u '$SFTP_USER,$SFTP_PASS' sftp://$SFTP_HOST:$SFTP_PORT;
|
||||||
|
mirror --reverse --delete --parallel=4 --verbose \
|
||||||
|
--exclude-glob '.well-known/' \
|
||||||
|
./dist/ $SFTP_PATH;
|
||||||
|
bye
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
{
|
||||||
|
echo "### Deployed"
|
||||||
|
echo ""
|
||||||
|
echo "- Commit: ${{ github.sha }}"
|
||||||
|
echo "- Page count: $(find dist -name '*.html' 2>/dev/null | wc -l)"
|
||||||
|
echo "- Build size: $(du -sh dist 2>/dev/null | cut -f1)"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
@@ -30,3 +30,6 @@ GEMINI.md
|
|||||||
|
|
||||||
# local-only screenshot creds
|
# local-only screenshot creds
|
||||||
tools/screenshots/.env
|
tools/screenshots/.env
|
||||||
|
|
||||||
|
# Admin capture-script discovery artifacts (scratch only)
|
||||||
|
tools/screenshots/_admin-*-links.json
|
||||||
|
|||||||
@@ -7,6 +7,23 @@ import { fileURLToPath } from 'node:url';
|
|||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://kb.anhonesthost.com',
|
site: 'https://kb.anhonesthost.com',
|
||||||
|
// Send section-only URLs to the first article in that section. Without these,
|
||||||
|
// hitting e.g. /whp/admin/ would render the Apache directory listing because
|
||||||
|
// there is no index.html in that folder.
|
||||||
|
redirects: {
|
||||||
|
'/whp/': '/whp/getting-started/welcome/',
|
||||||
|
'/whp/getting-started/': '/whp/getting-started/welcome/',
|
||||||
|
'/whp/how-to/': '/whp/how-to/add-a-domain/',
|
||||||
|
'/whp/site-builder/': '/whp/site-builder/overview/',
|
||||||
|
'/whp/local-dev/': '/whp/local-dev/overview/',
|
||||||
|
'/whp/reference/': '/whp/reference/service-hostnames/',
|
||||||
|
'/whp/add-ons/': '/whp/add-ons/overview/',
|
||||||
|
'/whp/admin/': '/whp/admin/overview/',
|
||||||
|
'/domains/': '/domains/transferring-a-domain-to-us/',
|
||||||
|
'/support/': '/support/remote-support/',
|
||||||
|
'/cpanel/': '/cpanel/nameservers/',
|
||||||
|
'/email/': '/email/set-up-your-email-client/',
|
||||||
|
},
|
||||||
vite: {
|
vite: {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@@ -18,11 +35,15 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
starlight({
|
starlight({
|
||||||
title: 'An Honest Host KB',
|
title: 'AnHonestHost KB',
|
||||||
description: 'Customer documentation for WHP and other An Honest Host services.',
|
description: 'Customer documentation for WHP and other AnHonestHost services.',
|
||||||
logo: {
|
components: {
|
||||||
src: './src/assets/logo.svg',
|
// Inline-SVG brand mark + "Knowledge Base" label.
|
||||||
replacesTitle: false,
|
// Inlining lets the SVG's currentColor follow the active theme.
|
||||||
|
SiteTitle: './src/components/SiteTitle.astro',
|
||||||
|
// Wraps Starlight's default <Head> to add a click-to-zoom lightbox
|
||||||
|
// (medium-zoom) that targets article content images.
|
||||||
|
Head: './src/components/Head.astro',
|
||||||
},
|
},
|
||||||
customCss: [
|
customCss: [
|
||||||
'@fontsource-variable/inter',
|
'@fontsource-variable/inter',
|
||||||
@@ -44,6 +65,15 @@ export default defineConfig({
|
|||||||
label: 'How-to guides',
|
label: 'How-to guides',
|
||||||
items: [{ autogenerate: { directory: 'whp/how-to' } }],
|
items: [{ autogenerate: { directory: 'whp/how-to' } }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Site Builder',
|
||||||
|
badge: { text: 'Beta', variant: 'tip' },
|
||||||
|
items: [{ autogenerate: { directory: 'whp/site-builder' } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Local development',
|
||||||
|
items: [{ autogenerate: { directory: 'whp/local-dev' } }],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Reference',
|
label: 'Reference',
|
||||||
items: [{ autogenerate: { directory: 'whp/reference' } }],
|
items: [{ autogenerate: { directory: 'whp/reference' } }],
|
||||||
@@ -52,8 +82,29 @@ export default defineConfig({
|
|||||||
label: 'Add-ons',
|
label: 'Add-ons',
|
||||||
items: [{ autogenerate: { directory: 'whp/add-ons' } }],
|
items: [{ autogenerate: { directory: 'whp/add-ons' } }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Admin',
|
||||||
|
// badge removed once content was verified against the real UI
|
||||||
|
items: [{ autogenerate: { directory: 'whp/admin' } }],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'cPanel hosting',
|
||||||
|
items: [{ autogenerate: { directory: 'cpanel' } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Domains',
|
||||||
|
items: [{ autogenerate: { directory: 'domains' } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
items: [{ autogenerate: { directory: 'email' } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Support',
|
||||||
|
items: [{ autogenerate: { directory: 'support' } }],
|
||||||
|
},
|
||||||
// Future products only appear once they have content.
|
// Future products only appear once they have content.
|
||||||
],
|
],
|
||||||
pagefind: true,
|
pagefind: true,
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Deploy artefacts
|
||||||
|
|
||||||
|
Config that lives on other servers but belongs under version control here,
|
||||||
|
because it exists to serve this KB.
|
||||||
|
|
||||||
|
## `whmcs-kb-redirects.conf`
|
||||||
|
|
||||||
|
301 redirects that send the retired WHMCS knowledgebase
|
||||||
|
(`secure.anhonesthost.com/knowledgebase/*`) to its replacement pages on
|
||||||
|
kb.anhonesthost.com. Installed 2026-08-01.
|
||||||
|
|
||||||
|
**Where it lives in production:** `/home/whmcs/public_html/.htaccess` on
|
||||||
|
`secure.anhonesthost.com`, prepended **before** the
|
||||||
|
`### BEGIN - WHMCS managed rules ###` marker. It has to come first — WHMCS's
|
||||||
|
own block ends with a catch-all that routes everything to `index.php`, so
|
||||||
|
rules placed after it never run.
|
||||||
|
|
||||||
|
The canonical copy on that server is `/root/kb-redirects.conf`, and the
|
||||||
|
pre-migration `.htaccess` is backed up at
|
||||||
|
`/root/htaccess-backup-20260801.bak`.
|
||||||
|
|
||||||
|
### Reinstalling
|
||||||
|
|
||||||
|
WHMCS rewrites `.htaccess` during some updates, which will silently drop
|
||||||
|
these rules. To restore:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/whmcs/public_html
|
||||||
|
cp -a .htaccess /root/htaccess-backup-$(date +%Y%m%d).bak # keep whatever WHMCS wrote
|
||||||
|
cat /root/kb-redirects.conf /root/htaccess-backup-$(date +%Y%m%d).bak > /tmp/htaccess.new
|
||||||
|
install -o whmcs -g whmcs -m 644 /tmp/htaccess.new .htaccess
|
||||||
|
apachectl -t
|
||||||
|
```
|
||||||
|
|
||||||
|
Then spot-check a redirect:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sI https://secure.anhonesthost.com/knowledgebase/13/x.html | grep -i location
|
||||||
|
# expect: https://kb.anhonesthost.com/support/remote-support/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Two things that will bite you when editing it
|
||||||
|
|
||||||
|
**Article and category IDs collide.** Both use the shape
|
||||||
|
`/knowledgebase/<id>/<slug>`, and the same number means different things —
|
||||||
|
article 5 is "What are my Name Servers?", category 5 is "WordPress Specific".
|
||||||
|
The *only* discriminator is the trailing `.html` on articles. That's why every
|
||||||
|
article rule appears before every category rule and terminates with `[L]`: by
|
||||||
|
the time the broad category patterns run, anything ending `.html` is already
|
||||||
|
gone. Reordering the file breaks this silently, and the wrong page still
|
||||||
|
returns 200.
|
||||||
|
|
||||||
|
**The slug is ignored.** WHMCS reads only the id, so any slug with the right
|
||||||
|
id resolves. The rules match `[^/]*` for the slug for the same reason.
|
||||||
|
|
||||||
|
Legacy pre-SEO URLs (`knowledgebase.php?action=displayarticle&id=N` and
|
||||||
|
`?action=displaycat&catid=N`) are mapped explicitly. `(^|&)id=` cannot match
|
||||||
|
`catid=` — the preceding character is `t`, not `&` or start-of-string — so the
|
||||||
|
two sets can't cross-fire.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
### BEGIN - KB migration redirects to kb.anhonesthost.com ###
|
||||||
|
# Added 2026-08-01. The WHMCS knowledgebase is retired; these 301s send its
|
||||||
|
# URLs to the equivalent page on the dedicated KB.
|
||||||
|
#
|
||||||
|
# ORDER MATTERS. Article and category URLs share the shape
|
||||||
|
# /knowledgebase/<id>/<slug>
|
||||||
|
# and their IDs COLLIDE (article 5 is "What are my Name Servers?", category 5
|
||||||
|
# is "WordPress Specific"). The only discriminator is the trailing ".html" on
|
||||||
|
# articles. Article rules therefore come first and terminate with [L]; the
|
||||||
|
# category rules below can then match broadly because anything ending .html
|
||||||
|
# has already been redirected and will never reach them.
|
||||||
|
#
|
||||||
|
# The slug is ignored by WHMCS (only the id is read), so it is ignored here
|
||||||
|
# too — any slug with the right id resolves to the right destination.
|
||||||
|
#
|
||||||
|
# The trailing "?" on each target discards the inbound query string.
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteBase /
|
||||||
|
|
||||||
|
# --- Articles (.html) -------------------------------------------------
|
||||||
|
RewriteRule ^knowledgebase/2/[^/]*\.html$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/3/[^/]*\.html$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/4/[^/]*\.html$ https://kb.anhonesthost.com/email/spam-filtering-changes/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/5/[^/]*\.html$ https://kb.anhonesthost.com/cpanel/nameservers/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/6/[^/]*\.html$ https://kb.anhonesthost.com/cpanel/fix-a-403-error/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/7/[^/]*\.html$ https://kb.anhonesthost.com/cpanel/wordpress-email-from-your-domain/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/8/[^/]*\.html$ https://kb.anhonesthost.com/cpanel/free-ssl-certificate/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/9/[^/]*\.html$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/10/[^/]*\.html$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/11/[^/]*\.html$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/12/[^/]*\.html$ https://kb.anhonesthost.com/domains/flush-your-dns-cache/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/13/[^/]*\.html$ https://kb.anhonesthost.com/support/remote-support/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/14/[^/]*\.html$ https://kb.anhonesthost.com/whp/getting-started/welcome/? [R=301,L]
|
||||||
|
|
||||||
|
# --- Categories (no .html) --------------------------------------------
|
||||||
|
RewriteRule ^knowledgebase/2/[^/]*/?$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/3/[^/]*/?$ https://kb.anhonesthost.com/email/spam-filtering-changes/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/4/[^/]*/?$ https://kb.anhonesthost.com/cpanel/nameservers/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/5/[^/]*/?$ https://kb.anhonesthost.com/cpanel/wordpress-email-from-your-domain/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/6/[^/]*/?$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/7/[^/]*/?$ https://kb.anhonesthost.com/cpanel/fix-a-403-error/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/8/[^/]*/?$ https://kb.anhonesthost.com/support/remote-support/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase/9/[^/]*/?$ https://kb.anhonesthost.com/whp/getting-started/welcome/? [R=301,L]
|
||||||
|
|
||||||
|
# --- Legacy query-string URLs -----------------------------------------
|
||||||
|
# Pre-SEO-URL links still in old tickets and emails:
|
||||||
|
# knowledgebase.php?action=displayarticle&id=<id>
|
||||||
|
# knowledgebase.php?action=displaycat&catid=<id>
|
||||||
|
# WHMCS used to 301 these to the friendly URL itself, but our rules above
|
||||||
|
# now intercept first — so map them explicitly or they all land on the KB
|
||||||
|
# home page and lose their destination.
|
||||||
|
#
|
||||||
|
# "(^|&)id=" cannot match "catid=" (the preceding char is "t", not & or
|
||||||
|
# start-of-string), so the two sets can't cross-fire.
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=2(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=3(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=4(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/spam-filtering-changes/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=5(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/nameservers/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=6(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/fix-a-403-error/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=7(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/wordpress-email-from-your-domain/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=8(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/free-ssl-certificate/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=9(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=10(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=11(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=12(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/domains/flush-your-dns-cache/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=13(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/support/remote-support/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)id=14(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/whp/getting-started/welcome/? [R=301,L]
|
||||||
|
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=2(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/set-up-your-email-client/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=3(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/email/spam-filtering-changes/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=4(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/nameservers/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=5(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/wordpress-email-from-your-domain/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=6(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=7(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/cpanel/fix-a-403-error/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=8(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/support/remote-support/? [R=301,L]
|
||||||
|
RewriteCond %{QUERY_STRING} (^|&)catid=9(&|$)
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/whp/getting-started/welcome/? [R=301,L]
|
||||||
|
|
||||||
|
# --- Tags, search, index, and anything else under /knowledgebase ------
|
||||||
|
# Catch-all last: any KB URL not matched above lands on the KB home page
|
||||||
|
# rather than a WHMCS 404.
|
||||||
|
RewriteRule ^knowledgebase\.php$ https://kb.anhonesthost.com/? [R=301,L]
|
||||||
|
RewriteRule ^knowledgebase(/.*)?$ https://kb.anhonesthost.com/? [R=301,L]
|
||||||
|
</IfModule>
|
||||||
|
### END - KB migration redirects to kb.anhonesthost.com ###
|
||||||
|
|
||||||
@@ -13,10 +13,14 @@
|
|||||||
"@fontsource-variable/inter": "^5.2.8",
|
"@fontsource-variable/inter": "^5.2.8",
|
||||||
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
||||||
"astro": "^6.3.1",
|
"astro": "^6.3.1",
|
||||||
|
"medium-zoom": "^1.1.0",
|
||||||
"sharp": "^0.34.5"
|
"sharp": "^0.34.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.9",
|
"@astrojs/check": "^0.9.9",
|
||||||
|
"@types/node": "^25.8.0",
|
||||||
|
"playwright": "^1.60.0",
|
||||||
|
"tsx": "^4.22.1",
|
||||||
"typescript": "^6.0.3"
|
"typescript": "^6.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2057,12 +2061,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.12.4",
|
"version": "25.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz",
|
||||||
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
|
"integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/sax": {
|
"node_modules/@types/sax": {
|
||||||
@@ -4356,6 +4360,12 @@
|
|||||||
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/medium-zoom": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/medium-zoom/-/medium-zoom-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/micromark": {
|
"node_modules/micromark": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
|
||||||
@@ -5384,6 +5394,53 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.60.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
|
||||||
|
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.60.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.60.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
|
||||||
|
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright/node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.14",
|
"version": "8.5.14",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
||||||
@@ -6061,6 +6118,21 @@
|
|||||||
"npm": ">=10.8.2"
|
"npm": ">=10.8.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sitemap/node_modules/@types/node": {
|
||||||
|
"version": "24.12.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz",
|
||||||
|
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sitemap/node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/smol-toml": {
|
"node_modules/smol-toml": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
||||||
@@ -6259,6 +6331,509 @@
|
|||||||
"license": "0BSD",
|
"license": "0BSD",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tsx": {
|
||||||
|
"version": "4.22.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz",
|
||||||
|
"integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "~0.28.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tsx": "dist/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsx/node_modules/esbuild": {
|
||||||
|
"version": "0.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
||||||
|
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.28.0",
|
||||||
|
"@esbuild/android-arm": "0.28.0",
|
||||||
|
"@esbuild/android-arm64": "0.28.0",
|
||||||
|
"@esbuild/android-x64": "0.28.0",
|
||||||
|
"@esbuild/darwin-arm64": "0.28.0",
|
||||||
|
"@esbuild/darwin-x64": "0.28.0",
|
||||||
|
"@esbuild/freebsd-arm64": "0.28.0",
|
||||||
|
"@esbuild/freebsd-x64": "0.28.0",
|
||||||
|
"@esbuild/linux-arm": "0.28.0",
|
||||||
|
"@esbuild/linux-arm64": "0.28.0",
|
||||||
|
"@esbuild/linux-ia32": "0.28.0",
|
||||||
|
"@esbuild/linux-loong64": "0.28.0",
|
||||||
|
"@esbuild/linux-mips64el": "0.28.0",
|
||||||
|
"@esbuild/linux-ppc64": "0.28.0",
|
||||||
|
"@esbuild/linux-riscv64": "0.28.0",
|
||||||
|
"@esbuild/linux-s390x": "0.28.0",
|
||||||
|
"@esbuild/linux-x64": "0.28.0",
|
||||||
|
"@esbuild/netbsd-arm64": "0.28.0",
|
||||||
|
"@esbuild/netbsd-x64": "0.28.0",
|
||||||
|
"@esbuild/openbsd-arm64": "0.28.0",
|
||||||
|
"@esbuild/openbsd-x64": "0.28.0",
|
||||||
|
"@esbuild/openharmony-arm64": "0.28.0",
|
||||||
|
"@esbuild/sunos-x64": "0.28.0",
|
||||||
|
"@esbuild/win32-arm64": "0.28.0",
|
||||||
|
"@esbuild/win32-ia32": "0.28.0",
|
||||||
|
"@esbuild/win32-x64": "0.28.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typesafe-path": {
|
"node_modules/typesafe-path": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz",
|
||||||
@@ -6309,9 +6884,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.16.0",
|
"version": "7.24.6",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"check": "astro check",
|
"check": "astro check",
|
||||||
|
"screenshots": "tsx tools/screenshots/run.ts",
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -16,10 +17,14 @@
|
|||||||
"@fontsource-variable/inter": "^5.2.8",
|
"@fontsource-variable/inter": "^5.2.8",
|
||||||
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
||||||
"astro": "^6.3.1",
|
"astro": "^6.3.1",
|
||||||
|
"medium-zoom": "^1.1.0",
|
||||||
"sharp": "^0.34.5"
|
"sharp": "^0.34.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.9",
|
"@astrojs/check": "^0.9.9",
|
||||||
|
"@types/node": "^25.8.0",
|
||||||
|
"playwright": "^1.60.0",
|
||||||
|
"tsx": "^4.22.1",
|
||||||
"typescript": "^6.0.3"
|
"typescript": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Static-HTML site hardening for kb.anhonesthost.com.
|
||||||
|
# Lands in dist/ at build time and is uploaded with the rest of the site.
|
||||||
|
|
||||||
|
# Never expose a directory listing to visitors.
|
||||||
|
Options -Indexes
|
||||||
|
|
||||||
|
# Astro emits both /path/ (with index.html) and /path.html for every route.
|
||||||
|
# Default to the trailing-slash form; Apache MultiViews can sometimes serve
|
||||||
|
# /path.html for /path/, which Pagefind doesn't index — disable it.
|
||||||
|
Options -MultiViews
|
||||||
|
|
||||||
|
# Send anything that doesn't resolve to a file to /404.html.
|
||||||
|
# Starlight builds a real 404 page; this just makes Apache serve it.
|
||||||
|
ErrorDocument 404 /404.html
|
||||||
|
ErrorDocument 403 /404.html
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# AnHonestHost Knowledge Base
|
||||||
|
|
||||||
|
> Customer documentation for WHP (AnHonestHost's containerized Web Hosting Platform), plus cPanel hosting, domains, email, and remote support.
|
||||||
|
|
||||||
|
WHP is AnHonestHost's containerized hosting platform, and most of this knowledge base documents it: getting started, how-to guides, the Site Builder, local development with the same container images used in production, reference material, and optional add-ons. Separate sections cover legacy cPanel hosting, domain management, email setup, and remote support. Pages in the WHP Admin section document server-wide super-admin controls, which most customers won't have access to.
|
||||||
|
|
||||||
|
## WHP — Getting started
|
||||||
|
|
||||||
|
- [Welcome to WHP](https://kb.anhonesthost.com/whp/getting-started/welcome/): Quick orientation for new customers — what WHP is and where to start.
|
||||||
|
- [What is containerized hosting?](https://kb.anhonesthost.com/whp/getting-started/what-is-containerized-hosting/): Plain-language explainer of containerized hosting and how it compares to shared hosting and a VPS.
|
||||||
|
|
||||||
|
## WHP — How-to guides
|
||||||
|
|
||||||
|
- [Add a domain](https://kb.anhonesthost.com/whp/how-to/add-a-domain/): Point a domain at your WHP hosting — whether you registered it with us or elsewhere.
|
||||||
|
- [Manage DNS records](https://kb.anhonesthost.com/whp/how-to/manage-dns-records/): View, add, edit, and delete DNS records for your domains using the Domains & DNS records editor in WHP.
|
||||||
|
- [Create a site](https://kb.anhonesthost.com/whp/how-to/create-a-site/): Spin up a containerized site on a domain you've added to WHP.
|
||||||
|
- [Create an email account](https://kb.anhonesthost.com/whp/how-to/create-an-email-account/): Add a mailbox on one of your domains and connect your email client.
|
||||||
|
- [Backups](https://kb.anhonesthost.com/whp/how-to/backups/): Run on-demand and scheduled backups of your sites and databases, and confirm they're succeeding.
|
||||||
|
- [Switching your site's backend](https://kb.anhonesthost.com/whp/how-to/switching-site-backend/): Change the web engine (container type) running a site — standard PHP/FPM or the premium LiteSpeed/OpenLiteSpeed tier.
|
||||||
|
- [Clear your site's cache](https://kb.anhonesthost.com/whp/how-to/clear-your-cache/): Seeing an old version of a page after making a change? Here's how to clear cached content so your updates show up.
|
||||||
|
|
||||||
|
## WHP — Site Builder
|
||||||
|
|
||||||
|
- [Site Builder overview](https://kb.anhonesthost.com/whp/site-builder/overview/): Build a website visually inside WHP — drag-and-drop blocks, ready-made templates, draft / publish workflow.
|
||||||
|
- [Getting started](https://kb.anhonesthost.com/whp/site-builder/getting-started/): Open Site Builder, pick a template or start from scratch, make your first edits, and publish.
|
||||||
|
- [Blocks & pages](https://kb.anhonesthost.com/whp/site-builder/blocks-and-pages/): The Site Builder block library, plus how to add pages and edit the shared header and footer.
|
||||||
|
- [Styling your site](https://kb.anhonesthost.com/whp/site-builder/styling/): Set colours, fonts, and link styles once with Site Design Tokens; reach for Advanced when you need finer control.
|
||||||
|
- [Publishing & code injection](https://kb.anhonesthost.com/whp/site-builder/publishing/): Drafts vs. publish, device previews, and adding analytics, custom fonts, or global CSS via the custom head code panel.
|
||||||
|
|
||||||
|
## WHP — Local development
|
||||||
|
|
||||||
|
- [Develop locally with our containers](https://kb.anhonesthost.com/whp/local-dev/overview/): Run the same Apache/PHP and Node containers locally that we use to host your site in production.
|
||||||
|
- [PHP + Apache locally](https://kb.anhonesthost.com/whp/local-dev/php-apache/): Run the cloud-apache-container image on your laptop for WordPress and other PHP apps.
|
||||||
|
- [Node + Nginx locally](https://kb.anhonesthost.com/whp/local-dev/node/): Run the cloud-node-container image on your laptop for Express, custom Node apps, and PM2-managed processes.
|
||||||
|
|
||||||
|
## WHP — Reference
|
||||||
|
|
||||||
|
- [Service hostnames](https://kb.anhonesthost.com/whp/reference/service-hostnames/): Quick reference for connecting to MySQL, PostgreSQL, and Valkey from inside your container.
|
||||||
|
|
||||||
|
## WHP — Add-ons
|
||||||
|
|
||||||
|
- [Add-ons overview](https://kb.anhonesthost.com/whp/add-ons/overview/): Optional features you can layer on your hosting plan — monitoring, archival email, resource upgrades, and more.
|
||||||
|
- [Site Monitoring](https://kb.anhonesthost.com/whp/add-ons/monitoring/): Proactive alerts for site errors, brute-force attempts, and exploit signatures.
|
||||||
|
- [Archival email](https://kb.anhonesthost.com/whp/add-ons/archival-email/): Long-term, searchable archive of your mailbox content, separate from the live mailbox.
|
||||||
|
- [Resource upgrades](https://kb.anhonesthost.com/whp/add-ons/resource-upgrades/): Add CPU, RAM, or disk to your container without migrating to a different plan.
|
||||||
|
- [Email upgrades](https://kb.anhonesthost.com/whp/add-ons/email-upgrades/): Add mailboxes or bump per-mailbox storage on your plan.
|
||||||
|
- [Optimized Webserver (OpenLiteSpeed + LSCache)](https://kb.anhonesthost.com/whp/add-ons/optimized-webserver/): Run your sites on OpenLiteSpeed with server-level full-page caching for dramatically faster page delivery — ideal for WordPress and other dynamic CMS sites.
|
||||||
|
|
||||||
|
## WHP — Admin
|
||||||
|
|
||||||
|
- [Admin overview](https://kb.anhonesthost.com/whp/admin/overview/): What WHP super admin unlocks — server-wide controls for services, mail, DNS, security, monitoring, and users.
|
||||||
|
- [Server settings & services](https://kb.anhonesthost.com/whp/admin/server-settings/): Restart services, configure mail server, manage DNS / nameservers, HAProxy + system SSL certificates, and integration API keys.
|
||||||
|
- [Coraza WAF rules](https://kb.anhonesthost.com/whp/admin/coraza-waf/): Set the global WAF mode, tune individual rules, audit blocked requests, and add per-host overrides.
|
||||||
|
- [AI Monitor, Issues & Ignore Rules](https://kb.anhonesthost.com/whp/admin/site-monitoring/): The three admin pages that drive the Site Monitoring add-on — AI Monitor dashboard, Issues, and Ignore Rules.
|
||||||
|
- [Users & delegated access](https://kb.anhonesthost.com/whp/admin/user-management/): Create WHP users, set account types, change passwords, plus delegated user access and account suspensions.
|
||||||
|
- [Backups](https://kb.anhonesthost.com/whp/admin/backups/): How WHP's automatic backups work, the default-target requirement, full-server backups vs customer data backups, and managing backup targets.
|
||||||
|
- [Data-drive encryption (LUKS)](https://kb.anhonesthost.com/whp/admin/data-drive-encryption/): Optional LUKS2 encryption of the /docker data volume on new server installs. Encrypts customer data, databases, and container state at rest; adds a manual unlock step after every reboot.
|
||||||
|
|
||||||
|
## cPanel hosting
|
||||||
|
|
||||||
|
- [What are my nameservers?](https://kb.anhonesthost.com/cpanel/nameservers/): The nameservers to set at your registrar so your domain points at your cPanel hosting with us.
|
||||||
|
- [Get your free SSL certificate](https://kb.anhonesthost.com/cpanel/free-ssl-certificate/): Every cPanel account includes free Let's Encrypt certificates through AutoSSL. Here's what has to be in place and what to do if one doesn't issue.
|
||||||
|
- [Why is my site getting a 403 error?](https://kb.anhonesthost.com/cpanel/fix-a-403-error/): A 403 Forbidden on cPanel hosting is almost always file permissions or file ownership. Here's how to check and fix both.
|
||||||
|
- [Send WordPress email from your own domain](https://kb.anhonesthost.com/cpanel/wordpress-email-from-your-domain/): Stop WordPress sending as user@server and send from an address on your domain instead, using an SMTP plugin and a real mailbox.
|
||||||
|
|
||||||
|
## Domains
|
||||||
|
|
||||||
|
- [Transferring a domain to us](https://kb.anhonesthost.com/domains/transferring-a-domain-to-us/): What happens after you start a domain transfer — the confirmation emails, how long it takes, and the nameserver check to do afterwards.
|
||||||
|
- [Flush your DNS cache](https://kb.anhonesthost.com/domains/flush-your-dns-cache/): Your site moved or its DNS changed, but your computer still loads the old version? Clear the DNS cache your device has saved locally.
|
||||||
|
|
||||||
|
## Email
|
||||||
|
|
||||||
|
- [Set up your email](https://kb.anhonesthost.com/email/set-up-your-email-client/): Connect Outlook, Apple Mail, Thunderbird, or your phone to your mailbox — with the server settings for both WHP and cPanel hosting.
|
||||||
|
- [Changes to spam filtering](https://kb.anhonesthost.com/email/spam-filtering-changes/): We're moving spam filtering from Baruwa to Proxmox Mail Gateway. What's changing, when, and what you need to do.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- [Remote support with RustDesk](https://kb.anhonesthost.com/support/remote-support/): Install and configure the RustDesk client so our support team can connect to your device and help directly.
|
||||||
|
|
||||||
|
## Optional
|
||||||
|
|
||||||
|
- [AnHonestHost KB homepage](https://kb.anhonesthost.com/): Start page for the knowledge base.
|
||||||
|
- [Sitemap](https://kb.anhonesthost.com/sitemap-index.xml): XML sitemap index for all pages on this site.
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 32" width="120" height="32">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 32" fill="none" height="32" role="img" aria-label="AnHonestHost Knowledge Base">
|
||||||
<text x="0" y="22" font-family="Inter,system-ui,sans-serif" font-size="20" font-weight="700" fill="#00d4aa">AHH</text>
|
<path d="M12 4L3 16L12 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<text x="50" y="22" font-family="Inter,system-ui,sans-serif" font-size="20" font-weight="400" fill="currentColor">KB</text>
|
<path d="M24 4L33 16L24 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="12" y1="16" x2="24" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
|
||||||
|
<text x="44" y="21" font-family="Inter, system-ui, -apple-system, sans-serif" font-size="18" font-weight="800" fill="currentColor" letter-spacing="-0.5">AnHonest<tspan fill="#00d4aa">Host</tspan></text>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 700 B |
|
After Width: | Height: | Size: 406 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 534 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 303 KiB |
|
After Width: | Height: | Size: 307 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 345 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 358 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 236 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
import Default from '@astrojs/starlight/components/Head.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Default><slot /></Default>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Lightbox: click-to-zoom on article images. Loaded once per page; auto-reattaches
|
||||||
|
// after Starlight client-side navigation.
|
||||||
|
import mediumZoom from 'medium-zoom';
|
||||||
|
import 'medium-zoom/dist/style.css';
|
||||||
|
|
||||||
|
const SELECTOR = '.sl-markdown-content img:not(.no-zoom)';
|
||||||
|
let zoom: ReturnType<typeof mediumZoom> | null = null;
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (!zoom) {
|
||||||
|
zoom = mediumZoom(SELECTOR, {
|
||||||
|
background: 'rgba(10, 22, 40, 0.92)',
|
||||||
|
margin: 32,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
zoom.detach();
|
||||||
|
zoom.attach(SELECTOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', refresh, { once: true });
|
||||||
|
} else {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
document.addEventListener('astro:after-swap', refresh);
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
const { siteTitleHref } = Astro.locals.starlightRoute;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="site-title-row sl-flex">
|
||||||
|
<a href={siteTitleHref} class="site-title sl-flex" translate="no" aria-label="AnHonestHost Knowledge Base">
|
||||||
|
<svg
|
||||||
|
class="brand-logo"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 240 32"
|
||||||
|
fill="none"
|
||||||
|
height="28"
|
||||||
|
role="img"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M12 4L3 16L12 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 4L33 16L24 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="12" y1="16" x2="24" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
|
||||||
|
<text x="44" y="21" font-family="Inter, system-ui, -apple-system, sans-serif" font-size="18" font-weight="800" fill="currentColor" letter-spacing="-0.5">AnHonest<tspan fill="#00d4aa">Host</tspan></text>
|
||||||
|
</svg>
|
||||||
|
<span class="kb-label">Knowledge Base</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<nav class="ext-nav" aria-label="AnHonestHost links">
|
||||||
|
<a href="https://anhonesthost.com/" class="ext-link">Get a Plan</a>
|
||||||
|
<a href="https://secure.anhonesthost.com/clientarea.php" class="ext-link">Client Portal</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.site-title-row {
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.site-title {
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
color: var(--sl-color-text);
|
||||||
|
}
|
||||||
|
.brand-logo {
|
||||||
|
height: calc(var(--sl-nav-height) - 2 * var(--sl-nav-pad-y));
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.kb-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--anhh-text-secondary, #94a3b8);
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
border-left: 1px solid var(--anhh-border-color, #334155);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.ext-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
.ext-link {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--anhh-text-secondary, #94a3b8);
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color 120ms ease;
|
||||||
|
}
|
||||||
|
.ext-link:hover,
|
||||||
|
.ext-link:focus {
|
||||||
|
color: var(--anhh-accent, #00d4aa);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
/* Hide kb-label and external nav on narrow viewports — search + hamburger
|
||||||
|
get tight on mobile. */
|
||||||
|
@media (max-width: 60em) {
|
||||||
|
.kb-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ext-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
title: Page not found
|
||||||
|
description: That URL doesn't exist on our knowledge base.
|
||||||
|
template: splash
|
||||||
|
editUrl: false
|
||||||
|
hero:
|
||||||
|
title: '404'
|
||||||
|
tagline: That URL doesn't exist on our knowledge base.
|
||||||
|
actions:
|
||||||
|
- text: Go to the knowledge base home
|
||||||
|
link: /
|
||||||
|
icon: right-arrow
|
||||||
|
variant: primary
|
||||||
|
- text: WHP getting started
|
||||||
|
link: /whp/getting-started/welcome/
|
||||||
|
variant: secondary
|
||||||
|
---
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
title: Why is my site getting a 403 error?
|
||||||
|
description: A 403 Forbidden on cPanel hosting is almost always file permissions or file ownership. Here's how to check and fix both.
|
||||||
|
sidebar:
|
||||||
|
order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
A **403 Forbidden** means the web server found your file but refused to serve it. On our cPanel servers that's nearly always one of two things: permissions that are wrong, or files owned by the wrong user.
|
||||||
|
|
||||||
|
We run **suEXEC**, which makes your site's code run as *your* account rather than a shared web-server user. That's a security benefit — one account can't read another's files — but it does mean the server is strict about ownership and permissions.
|
||||||
|
|
||||||
|
## The correct values
|
||||||
|
|
||||||
|
| Item | Permissions |
|
||||||
|
| --- | --- |
|
||||||
|
| Folders | `755` |
|
||||||
|
| Files | `644` |
|
||||||
|
|
||||||
|
Anything **more permissive** (a folder at `777`, a file at `666`) is refused by suEXEC. This surprises people, because loosening permissions is the instinctive fix for a permissions error — here it makes things worse, and it's a genuine security risk.
|
||||||
|
|
||||||
|
## Fixing it in cPanel
|
||||||
|
|
||||||
|
The File Manager handles this without needing a terminal:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Sign in to cPanel and open **File Manager**.
|
||||||
|
|
||||||
|
2. Navigate to the site's folder. For your primary domain that's normally `public_html`.
|
||||||
|
|
||||||
|
3. Select the folder or file, then click **Permissions** in the toolbar.
|
||||||
|
|
||||||
|
4. Set folders to `755` and files to `644`. To apply to everything beneath a folder at once, tick the **recurse into subdirectories** option and choose whether it applies to files or folders — you'll need one pass for each, since they take different values.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## When ownership is the problem
|
||||||
|
|
||||||
|
Permissions are only half of it. Files also have to be owned by your account.
|
||||||
|
|
||||||
|
- **Uploaded over FTP or SFTP?** Ownership is almost certainly fine — the files were created as you.
|
||||||
|
- **Pulled the site down over SSH** with `git clone`, `wget`, `curl`, or unpacked an archive as another user? Ownership may be wrong, and no amount of `chmod` will fix that. The files need `chown` back to your account.
|
||||||
|
|
||||||
|
If you suspect ownership, [open a ticket](https://secure.anhonesthost.com/submitticket.php) — it's a one-command fix from our side and safer than guessing.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Be careful with recursive commands over SSH. A `chmod -R 777` or a `chown` aimed at the wrong path can break your whole account, and in the case of `777` will leave the site returning 403 anyway. If you aren't confident, ask us.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## If permissions and ownership are both correct
|
||||||
|
|
||||||
|
Other causes of a 403:
|
||||||
|
|
||||||
|
- **No index file.** A folder with no `index.html` or `index.php`, on a server with directory listing disabled, returns 403.
|
||||||
|
- **An `.htaccess` rule** denying access — either one you added, or one a security plugin wrote.
|
||||||
|
- **A security plugin or firewall** blocking your IP after failed logins. Try from a different network or your phone on mobile data; if that works, it's an IP block.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Get your free SSL certificate](/cpanel/free-ssl-certificate/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
title: Get your free SSL certificate
|
||||||
|
description: Every cPanel account includes free Let's Encrypt certificates through AutoSSL. Here's what has to be in place and what to do if one doesn't issue.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Your cPanel hosting includes free SSL certificates from **Let's Encrypt**, issued automatically by cPanel's AutoSSL service. There's nothing to buy and nothing to install by hand.
|
||||||
|
|
||||||
|
## What has to be true first
|
||||||
|
|
||||||
|
AutoSSL can only issue a certificate once both of these are in place:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. **The domain is added to your cPanel account** — as the primary domain, an addon domain, or a subdomain.
|
||||||
|
|
||||||
|
2. **DNS points at our server.** Let's Encrypt validates a certificate by fetching a file from wherever the domain currently resolves. If it still points at your old host, validation fails and no certificate is issued.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
Check where a domain currently resolves at [whatsmydns.net](https://www.whatsmydns.net/).
|
||||||
|
|
||||||
|
## When it issues
|
||||||
|
|
||||||
|
AutoSSL runs on a schedule — **once every 24 hours**. So after adding a domain and pointing DNS at us, a certificate normally appears within a day without you doing anything.
|
||||||
|
|
||||||
|
Renewals are automatic too. Let's Encrypt certificates are valid for 90 days and AutoSSL renews them well before expiry, so a working site stays working.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Need it sooner than the next daily run? Confirm DNS is pointing at us, then [open a ticket](https://secure.anhonesthost.com/submitticket.php) — we can trigger AutoSSL by hand and usually have the certificate in place shortly after.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## If a certificate doesn't appear
|
||||||
|
|
||||||
|
Work through these in order — the first two cover most cases:
|
||||||
|
|
||||||
|
- **DNS isn't pointing here yet.** The most common cause by far. Verify at whatsmydns.net that the domain resolves to our server's IP.
|
||||||
|
- **It hasn't been 24 hours.** Give the scheduled run a chance before assuming something is broken.
|
||||||
|
- **The domain isn't actually in your account.** Check cPanel → **Domains**. A domain you own but haven't added is invisible to AutoSSL.
|
||||||
|
- **A redirect is intercepting validation.** Forced redirects — especially domain-wide ones in `.htaccess` — can stop Let's Encrypt from reaching the validation file.
|
||||||
|
- **CAA records are blocking issuance.** If your DNS has a CAA record naming a different certificate authority, Let's Encrypt is refused. It must permit `letsencrypt.org`.
|
||||||
|
|
||||||
|
## Making your site actually use it
|
||||||
|
|
||||||
|
An issued certificate doesn't automatically mean visitors get HTTPS. Once it's in place, make sure your site loads over `https://` and redirects visitors from `http://`. In WordPress, that's usually setting both the **WordPress Address** and **Site Address** to the `https://` version under **Settings → General**.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [What are my nameservers?](/cpanel/nameservers/)
|
||||||
|
- [Why is my site getting a 403 error?](/cpanel/fix-a-403-error/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
title: What are my nameservers?
|
||||||
|
description: The nameservers to set at your registrar so your domain points at your cPanel hosting with us.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Nameservers tell the internet which company is in charge of your domain's DNS. To use your hosting with us, set these at whichever registrar your domain is registered with.
|
||||||
|
|
||||||
|
## Shared and reseller hosting (cPanel01)
|
||||||
|
|
||||||
|
```text
|
||||||
|
ns1.cpanel01.cloud-hosting.io
|
||||||
|
ns2.cpanel01.cloud-hosting.io
|
||||||
|
```
|
||||||
|
|
||||||
|
Both shared and reseller accounts on cPanel01 use the same pair.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
**On WHP instead?** WHP servers each have their own nameservers, so there's no single pair to publish here. Yours are shown in WHP on the **Dashboard** page under **Server Information** — see [Add a domain](/whp/how-to/add-a-domain/).
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Setting them
|
||||||
|
|
||||||
|
Nameservers are changed at your **registrar** (whoever you bought the domain from), not in cPanel. Look for a section called *Nameservers*, *DNS*, or *Domain settings*, choose the "custom nameservers" option, and enter both values above.
|
||||||
|
|
||||||
|
If your domain is registered with us, tell us the change you need and we'll make it for you.
|
||||||
|
|
||||||
|
## How long does it take?
|
||||||
|
|
||||||
|
Nameserver changes propagate across the internet gradually — usually within a few hours, occasionally up to 48. You can watch progress at [whatsmydns.net](https://www.whatsmydns.net/).
|
||||||
|
|
||||||
|
During that window some visitors reach the new server while others still reach the old one. That's expected, and it's why we recommend leaving your old hosting active until propagation finishes.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Changing nameservers moves **all** of your DNS to us — website, email, and anything else. If your email is hosted somewhere other than your website, tell us before you switch so we can recreate those mail records here first. Otherwise mail delivery stops when the change takes effect.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Flush your DNS cache](/domains/flush-your-dns-cache/) — if you still see the old site after propagation.
|
||||||
|
- [Transferring a domain to us](/domains/transferring-a-domain-to-us/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
title: Send WordPress email from your own domain
|
||||||
|
description: Stop WordPress sending as user@server and send from an address on your domain instead, using an SMTP plugin and a real mailbox.
|
||||||
|
sidebar:
|
||||||
|
order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
If your WordPress site sends mail — contact forms, order confirmations, password resets — you may notice it arrives from something like `user@host.server-host.tld` rather than your own domain.
|
||||||
|
|
||||||
|
That's deliberate. PHP's built-in mail function is configured to send as the account that owns the site, which makes it much harder for a compromised script to forge mail as someone else. The side effect is unbranded — and often poorly delivered — email.
|
||||||
|
|
||||||
|
The fix is to send through a real mailbox on your domain using SMTP.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
This also improves **deliverability**. Mail sent through an authenticated mailbox on your domain passes SPF and DKIM checks; mail sent by PHP as `user@server` frequently doesn't, and lands in spam.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Step 1 — Create a mailbox to send from
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. In cPanel, open **Email Accounts** under the **Email** section.
|
||||||
|
|
||||||
|
2. Create an account for the site to send as — `noreply@yourdomain.com` is the usual choice.
|
||||||
|
|
||||||
|
3. Use the **password generator** and copy the password somewhere temporarily — you'll need it in a moment, and you won't be shown it again.
|
||||||
|
|
||||||
|
4. Set a **mailbox quota**. People reply to `noreply` addresses regardless of the name, and without a quota those replies accumulate against your hosting space indefinitely.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Step 2 — Install an SMTP plugin
|
||||||
|
|
||||||
|
WordPress needs a plugin to route mail through SMTP instead of PHP. Any of the well-maintained ones work:
|
||||||
|
|
||||||
|
- [WP Mail SMTP](https://wordpress.org/plugins/wp-mail-smtp/)
|
||||||
|
- [Easy WP SMTP](https://wordpress.org/plugins/easy-wp-smtp/)
|
||||||
|
- [Post SMTP](https://wordpress.org/plugins/post-smtp/)
|
||||||
|
|
||||||
|
## Step 3 — Configure it
|
||||||
|
|
||||||
|
In the plugin's settings, choose the **Other SMTP** / custom option and enter:
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| **SMTP host** | Your server's hostname (for example `cpanel01.cloud-hosting.io`) |
|
||||||
|
| **Encryption** | TLS |
|
||||||
|
| **Port** | `587` |
|
||||||
|
| **Authentication** | On |
|
||||||
|
| **Username** | The **full** email address — `noreply@yourdomain.com`, not `noreply` |
|
||||||
|
| **Password** | The password you generated |
|
||||||
|
| **From address** | The same mailbox address |
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Use port **587 with TLS**, or **465 with SSL** if your plugin prefers implicit TLS. Don't use port **25** without encryption — it sends your mailbox password across the network in the clear, and many networks block it outright.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
Two details that account for most failures: the username has to be the **whole** address, and the **From** address must match the mailbox you authenticated as. A mismatch gets rejected or treated as spoofing.
|
||||||
|
|
||||||
|
## Step 4 — Send a test
|
||||||
|
|
||||||
|
Every one of these plugins has a test-email feature. Use it before assuming it works, and send to an address at a different provider (Gmail, Outlook) rather than another mailbox on your own domain — that exercises the path real recipients take.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [What are my nameservers?](/cpanel/nameservers/)
|
||||||
|
- **On WHP?** See [Create an email account](/whp/how-to/create-an-email-account/) — the mailbox part differs, the plugin setup is the same.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
title: Flush your DNS cache
|
||||||
|
description: Your site moved or its DNS changed, but your computer still loads the old version? Clear the DNS cache your device has saved locally.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
When you visit a site, your computer saves ("caches") the answer to *which server is this domain on?* so it doesn't have to ask again every time. That's normally invisible and helpful — but right after a domain moves to a new server or its DNS records change, your device can keep using the old answer and show you the old site.
|
||||||
|
|
||||||
|
Devices are supposed to re-check after a short while, but some record lifetimes stretch to hours or even days. Clearing the cache by hand skips the wait.
|
||||||
|
|
||||||
|
## First, confirm it's actually your computer
|
||||||
|
|
||||||
|
Before changing anything locally, check whether the new DNS has actually gone out to the world. Look your domain up at [whatsmydns.net](https://www.whatsmydns.net/) — it queries servers in many countries at once.
|
||||||
|
|
||||||
|
- **Most locations show the new value** — the change has propagated, and a stale local cache is the likely culprit. Continue below.
|
||||||
|
- **Most locations still show the old value** — the change hasn't propagated yet. Flushing your own cache won't help; give it time.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
A quick sanity check: open the site in a **private / incognito window**, or on your phone using mobile data instead of Wi-Fi. If it looks correct there, the problem is local to your computer.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Press the **Windows key**, type `Command Prompt`, and open it.
|
||||||
|
|
||||||
|
2. Type this and press **Enter**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ipconfig /flushdns
|
||||||
|
```
|
||||||
|
|
||||||
|
3. You should see *"Successfully flushed the DNS Resolver Cache."*
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Open **Terminal** (in **Applications → Utilities**, or press `Cmd` + `Space` and search for "Terminal").
|
||||||
|
|
||||||
|
2. Type this and press **Enter**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Enter your administrator password when prompted, then press **Enter**. Nothing is printed when it works — no news is good news.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
Both commands matter on macOS: the first clears the lookup cache, the second restarts the service that answers DNS queries. Running only the first often appears to do nothing.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Still seeing the old site?
|
||||||
|
|
||||||
|
Your computer isn't the only thing that caches DNS. Work outward:
|
||||||
|
|
||||||
|
- **Your browser** keeps its own cache. Do a hard refresh (`Ctrl` + `Shift` + `R`, or `Cmd` + `Shift` + `R` on a Mac) or try a private window.
|
||||||
|
- **Your router** caches DNS too. Restarting it clears that.
|
||||||
|
- **Your internet provider's resolvers** cache as well, and you can't clear those — they expire on their own schedule. If everything else is clean, this is usually what's left. It typically resolves within a few hours.
|
||||||
|
|
||||||
|
If it's still wrong well after the record's lifetime should have expired, it may not be a caching problem at all — get in touch and we'll look at the actual DNS records.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Manage DNS records](/whp/how-to/manage-dns-records/)
|
||||||
|
- [Add a domain](/whp/how-to/add-a-domain/)
|
||||||
|
- [Clear your site's cache](/whp/how-to/clear-your-cache/) — for when *content* is stale rather than DNS.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
title: Transferring a domain to us
|
||||||
|
description: What happens after you start a domain transfer — the confirmation emails, how long it takes, and the nameserver check to do afterwards.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
You've started a domain transfer to us — here's what happens next, and the one thing worth checking once it lands.
|
||||||
|
|
||||||
|
## Before the transfer will go through
|
||||||
|
|
||||||
|
Your current registrar has to let the domain go first. At the registrar you're leaving, make sure you've:
|
||||||
|
|
||||||
|
- **Unlocked the domain.** Registrars set a transfer lock by default to prevent unauthorised moves.
|
||||||
|
- **Turned off WHOIS / domain privacy.** Privacy services can hide the administrative address the confirmation email needs to reach.
|
||||||
|
- **Got the EPP code** (sometimes called the auth code or transfer key). Your registrar provides this, usually by email or in the domain's settings.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Domains can't be transferred within **60 days** of being registered, or within 60 days of a previous transfer. That's an ICANN rule, not a registrar policy — nobody can waive it.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Confirming the transfer
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. **Watch for confirmation emails.** You'll get one or both of:
|
||||||
|
|
||||||
|
- a message from **your previous registrar**, confirming the domain is moving away from them;
|
||||||
|
- a message from **NameCheap**, who we use to resell domains, confirming it's moving to our account.
|
||||||
|
|
||||||
|
They go to the domain's registered contact address — which may not be the address you use with us. If nothing arrives, check that address and its spam folder.
|
||||||
|
|
||||||
|
2. **Click the confirmation link** in the email. Nothing moves until you do; an unconfirmed transfer just sits until it expires.
|
||||||
|
|
||||||
|
3. **Wait for it to complete.** Once confirmed, the transfer usually finishes in **1 to 12 hours**, depending on how quickly the losing registrar releases it. Your account page updates to reflect the domain once it's done.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## After it completes: check your nameservers
|
||||||
|
|
||||||
|
This is the step people miss. Some registrars — **GoDaddy** most notably — reset a domain's nameservers when they release it. If that happens, the domain transfers to us successfully but still points at the old host, so your site or email can appear to break for no obvious reason.
|
||||||
|
|
||||||
|
Once the transfer shows as complete, confirm the domain's nameservers match the ones for your hosting. If you're on WHP, your server's nameservers are listed on the **Dashboard** page under **Server Information** — see [Add a domain](/whp/how-to/add-a-domain/) for where to set them.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
A transfer moves *registration*, not *hosting*. Your site's files and email stay wherever they already live. If you're also moving hosting to us, that's a separate step — ask us and we'll help sequence the two so nothing goes dark.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Add a domain](/whp/how-to/add-a-domain/)
|
||||||
|
- [Manage DNS records](/whp/how-to/manage-dns-records/)
|
||||||
|
- [Flush your DNS cache](/domains/flush-your-dns-cache/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
title: Set up your email
|
||||||
|
description: Connect Outlook, Apple Mail, Thunderbird, or your phone to your mailbox — with the server settings for both WHP and cPanel hosting.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
You can read your mail in any standard email app. Most will configure themselves once you enter your address and password — and if yours doesn't, the settings are below.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
**Just want to check your mail?** You don't need to set anything up. Webmail works in any browser with no configuration — ask us for your webmail address if you don't have it.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Let your app configure itself
|
||||||
|
|
||||||
|
Try this first. It works in most modern clients, including Outlook, Apple Mail, and the mail apps on iOS and Android.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Add a new account in your email app.
|
||||||
|
|
||||||
|
2. Enter your **full email address** (`you@yourdomain.com`) and its password.
|
||||||
|
|
||||||
|
3. Choose **Next** or **Sign in** and let the app look up the settings.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
If the app finds everything, you're done. If it asks for server details, use the table below.
|
||||||
|
|
||||||
|
## Server settings
|
||||||
|
|
||||||
|
Use your **full email address** as the username — not just the part before the `@`. That single detail accounts for most setup failures.
|
||||||
|
|
||||||
|
### WHP hosting
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| **Incoming (IMAP)** | `mail01.cloud-hosting.io`, port **993**, SSL/TLS |
|
||||||
|
| **Incoming (POP3)** | `mail01.cloud-hosting.io`, port **995**, SSL/TLS |
|
||||||
|
| **Outgoing (SMTP)** | `mail01.cloud-hosting.io`, port **587**, STARTTLS |
|
||||||
|
| **Username** | Your full email address |
|
||||||
|
| **Authentication** | Required, for outgoing as well as incoming |
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
`mail01` is an example. Your server's mail hostname is shown in WHP on the **Dashboard** page under **Server Information**, and the **Email** page has a **Setup Instructions** button with the exact values for your account.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### cPanel hosting
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| **Incoming (IMAP)** | `cpanel01.cloud-hosting.io`, port **993**, SSL/TLS |
|
||||||
|
| **Incoming (POP3)** | `cpanel01.cloud-hosting.io`, port **995**, SSL/TLS |
|
||||||
|
| **Outgoing (SMTP)** | `cpanel01.cloud-hosting.io`, port **465**, SSL/TLS |
|
||||||
|
| **Username** | Your full email address |
|
||||||
|
| **Authentication** | Required, for outgoing as well as incoming |
|
||||||
|
|
||||||
|
Port **587 with STARTTLS** also works for outgoing if your app prefers it.
|
||||||
|
|
||||||
|
## IMAP or POP3?
|
||||||
|
|
||||||
|
Choose **IMAP** unless you have a specific reason not to.
|
||||||
|
|
||||||
|
- **IMAP** keeps mail on the server and mirrors it to every device. Read a message on your phone and it shows as read on your laptop. This is what you want when you use more than one device.
|
||||||
|
- **POP3** downloads mail to one device and, by default, removes it from the server. Mail then exists only on that machine — if it dies, so does your mail.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Take care switching an existing account from POP3 to IMAP. If POP3 has been deleting messages from the server as it downloaded them, those messages exist only in your local app — and they won't reappear when you switch. Back up first, or ask us to check before you change anything.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Common problems
|
||||||
|
|
||||||
|
**Incoming works, outgoing fails.** Almost always because outgoing authentication is off. Many apps leave it unticked by default. Find the option — usually *My outgoing server requires authentication* — and enable it with the same username and password.
|
||||||
|
|
||||||
|
**Password rejected.** Use the full email address as the username. If it still fails, reset the mailbox password and try again — and watch for autocorrect capitalising the first letter on phones.
|
||||||
|
|
||||||
|
**Certificate warnings.** Connect using the server hostname in the table above, not your own domain. A certificate is issued for the server's name, so connecting as `mail.yourdomain.com` can trigger a mismatch warning even though everything works.
|
||||||
|
|
||||||
|
**Old mail missing after setup.** If the account was previously POP3, see the caution above before assuming anything is lost.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/) — WHP
|
||||||
|
- [Send WordPress email from your own domain](/cpanel/wordpress-email-from-your-domain/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
title: Changes to spam filtering
|
||||||
|
description: We're moving spam filtering from Baruwa to Proxmox Mail Gateway. What's changing, when, and what you need to do.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
badge: Notice
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
We're changing the platform that filters spam for our mail customers.
|
||||||
|
|
||||||
|
**Baruwa**, which we've used for years, reaches end of life in **November 2026** — its developers are stopping support and security updates. Rather than run mail filtering on software that no longer receives fixes, we're moving to **Proxmox Mail Gateway**.
|
||||||
|
|
||||||
|
## What this means for you
|
||||||
|
|
||||||
|
**For most customers, nothing changes and there's nothing to do.** Your address stays the same, your mailbox and its contents are untouched, and your email app keeps working without reconfiguration. Filtering continues throughout — we're changing what does it, not whether it happens.
|
||||||
|
|
||||||
|
If you're one of the customers who uses the Baruwa web interface directly — to review quarantined mail, or to manage your own allow and block lists — that's the part that changes, since Proxmox Mail Gateway has its own interface. **We'll contact you individually** to move you across and show you the equivalent screens. You don't need to do anything in the meantime.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Worth doing now, whoever you are: if you've built up allow or block lists in Baruwa that matter to you, take a copy. We'll migrate what we can, but having your own record makes it easy to confirm nothing was missed.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
|
||||||
|
| When | What happens |
|
||||||
|
| --- | --- |
|
||||||
|
| Now | Baruwa continues to run. We begin moving customers across. |
|
||||||
|
| Before November 2026 | Affected customers are contacted individually and migrated. |
|
||||||
|
| November 2026 | Baruwa reaches end of life and is retired. |
|
||||||
|
|
||||||
|
We'll update this page as the migration progresses.
|
||||||
|
|
||||||
|
## Questions we expect
|
||||||
|
|
||||||
|
**Will I lose quarantined mail?** Anything currently held in quarantine stays available until your migration. If something in there matters, release it to your inbox rather than leaving it quarantined.
|
||||||
|
|
||||||
|
**Do I need to change my email settings?** No. Server names, ports, and passwords are unaffected — see [Set up your email](/email/set-up-your-email-client/) if you're configuring a new device.
|
||||||
|
|
||||||
|
**Is my mail less protected during the change?** No. Filtering runs continuously through the migration; there's no window where mail is unfiltered.
|
||||||
|
|
||||||
|
**I'd rather opt out of filtering entirely.** Talk to us and we'll go through the options and the trade-offs.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Set up your email](/email/set-up-your-email-client/)
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/)
|
||||||
|
|
||||||
|
## Questions about your account?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
title: Remote support with RustDesk
|
||||||
|
description: Install and configure the RustDesk client so our support team can connect to your device and help directly.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Sometimes the fastest way to sort something out is for us to see your screen — configuring an email client, reproducing an error we can't recreate from our side, or walking through a setting together.
|
||||||
|
|
||||||
|
We use **RustDesk**, an open-source remote-desktop tool, running on our own relay server rather than a third-party service. Nothing is installed permanently and nothing runs in the background: you start it when you want help and close it when you're done.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
We will never ask you to install remote-support software out of the blue. If someone contacts you claiming to be from AnHonestHost and asks for remote access to your computer — especially about a payment, a refund, or a "problem with your account" — stop and [open a ticket](https://secure.anhonesthost.com/submitticket.php) to check it's really us. Only ever run this at the end of a conversation *you* started with us.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Install the client
|
||||||
|
|
||||||
|
Download RustDesk for your device from the official releases:
|
||||||
|
|
||||||
|
- **Windows, macOS, and Android** — [github.com/rustdesk/rustdesk/releases/latest](https://github.com/rustdesk/rustdesk/releases/latest). Pick the `.msi` for Windows, the `.dmg` for macOS, or the signed `.apk` for Android.
|
||||||
|
- **iOS / iPadOS** — [RustDesk on the App Store](https://apps.apple.com/us/app/rustdesk-remote-desktop/id1581225015).
|
||||||
|
|
||||||
|
## Point it at our server
|
||||||
|
|
||||||
|
We run our own ID and relay server, which is faster and keeps your session off shared public infrastructure. You only need to do this once.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Open **Settings**. On desktop that's the **three dots** next to your ID; on mobile it's the settings icon.
|
||||||
|
|
||||||
|
2. On desktop, choose **Network**. (On mobile, skip straight to the next step.)
|
||||||
|
|
||||||
|
3. Choose **ID/Relay server** and enter:
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| **ID server** | `rustdesk.cloud-hosting.io` |
|
||||||
|
| **Relay server** | `rustdesk.cloud-hosting.io` |
|
||||||
|
| **API server** | `https://rustdesk.cloud-hosting.io` |
|
||||||
|
| **Key** | `UmpkeFe76AKt8vw8Pj0YCSbxIcYLsqqfPGYzLRl+PgA=` |
|
||||||
|
|
||||||
|
4. **Save**, then go back to the **Home** screen.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Starting a session
|
||||||
|
|
||||||
|
The support operator needs two things from the Home screen:
|
||||||
|
|
||||||
|
- **Your ID** — a nine-digit number that stays the same for your device.
|
||||||
|
- **Your one-time password** — shown beneath the ID, and regenerated each time.
|
||||||
|
|
||||||
|
Give both to your support operator over the phone, or in your ticket.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
If you send them in a ticket rather than over the phone, **leave your computer on with RustDesk running** — the one-time password only works while the client is open, and we can't connect to a sleeping machine.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Ending a session
|
||||||
|
|
||||||
|
Close the RustDesk window when you're finished. With it closed, no connection is possible — the one-time password from your session is already spent, and a new one is generated next time you open it. You can uninstall the client entirely if you'd rather.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
title: Archival email
|
||||||
|
description: Long-term, searchable archive of your mailbox content, separate from the live mailbox.
|
||||||
|
sidebar:
|
||||||
|
order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import PaidAddon from '~/content/partials/paid-addon-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<PaidAddon />
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
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 long-term.
|
||||||
|
- You want a recovery option for mail you accidentally delete from the live mailbox.
|
||||||
|
|
||||||
|
It's powered by the open-source [Bichon](https://github.com/rustmailer/bichon) archival service.
|
||||||
|
|
||||||
|
## How it's different from backups
|
||||||
|
|
||||||
|
| | Backups | Archival email |
|
||||||
|
|---|---|---|
|
||||||
|
| **Type** | Point-in-time snapshots | Continuous mailbox copy |
|
||||||
|
| **Granularity** | Restore the whole mailbox to a moment in time | Search and retrieve individual messages |
|
||||||
|
| **Best for** | Recovering from a known event ("the mailbox was hacked yesterday") | Long-term retention, occasional message lookup |
|
||||||
|
|
||||||
|
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).
|
||||||
|
- **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
|
||||||
|
|
||||||
|
From the [client portal](https://secure.anhonesthost.com/clientarea.php), open **Add-ons** under your hosting service and pick Archival email. Billing is per archived mailbox.
|
||||||
|
|
||||||
|
## How to use it
|
||||||
|
|
||||||
|
The archive is managed alongside your regular mailboxes on the **Email** page in WHP — the **Email Accounts** section shows archive status per mailbox, and the per-mailbox menu lets you search and restore archived mail.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/)
|
||||||
|
- [Backups](/whp/how-to/backups/)
|
||||||
|
- [Email upgrades](/whp/add-ons/email-upgrades/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
title: Email upgrades
|
||||||
|
description: Add mailboxes or bump per-mailbox storage on your plan.
|
||||||
|
sidebar:
|
||||||
|
order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
import PaidAddon from '~/content/partials/paid-addon-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<PaidAddon />
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
Add **more mailboxes** than your plan includes, or **bump the per-mailbox storage cap** so individual mailboxes can hold more mail.
|
||||||
|
|
||||||
|
## What you can upgrade
|
||||||
|
|
||||||
|
- **Mailbox count** — extra accounts beyond your plan's included number.
|
||||||
|
- **Per-mailbox size** — raise the storage cap on individual mailboxes (extra GB).
|
||||||
|
- **Shared email pool** — if your plan uses a shared storage pool across all mailboxes, you can increase the pool size.
|
||||||
|
|
||||||
|
## When you might need this
|
||||||
|
|
||||||
|
- **Hiring more staff** and you've used up your included mailboxes.
|
||||||
|
- **A mailbox is approaching its storage cap.** First consider whether [archival email](/whp/add-ons/archival-email/) is the better fit — if the issue is *old* mail you rarely need, archiving is cheaper and keeps the live mailbox responsive.
|
||||||
|
- **A domain that sends newsletters** and needs many sending aliases.
|
||||||
|
|
||||||
|
## How to enable
|
||||||
|
|
||||||
|
From the [client portal](https://secure.anhonesthost.com/clientarea.php), open your hosting service → **Upgrade/Downgrade** → pick the email option. WHP applies it immediately.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/)
|
||||||
|
- [Archival email](/whp/add-ons/archival-email/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
title: Site Monitoring
|
||||||
|
description: Proactive alerts for site errors, brute-force attempts, and exploit signatures.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import PaidAddon from '~/content/partials/paid-addon-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<PaidAddon />
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
Site Monitoring watches your site's logs, error rates, and access patterns in the background. If something looks unusual — a sustained spike in 500 errors, a brute-force pattern against a login page, a request matching a known exploit signature — you get an alert.
|
||||||
|
|
||||||
|
By default, alerts go to the contact email on your account. SMS is available for critical-severity events if you opt in.
|
||||||
|
|
||||||
|
## What's included
|
||||||
|
|
||||||
|
- **Real-time log scanning** across PHP, Apache, and access logs.
|
||||||
|
- **Brute-force detection** on auth endpoints (WordPress wp-login.php, generic /login, mail auth, SFTP).
|
||||||
|
- **Exploit-signature alerts** with severity grading (informational, warning, critical).
|
||||||
|
- **Per-rule ignore lists** so you can mute things you've already triaged or know are harmless for your site.
|
||||||
|
- **SMS notifications** for critical-severity alerts (configurable; off by default).
|
||||||
|
|
||||||
|
## How to enable
|
||||||
|
|
||||||
|
Site Monitoring is enabled per site. From the [client portal](https://secure.anhonesthost.com/clientarea.php), go to your hosting service → **Upgrade/Downgrade** and pick Site Monitoring.
|
||||||
|
|
||||||
|
## Where it lives in WHP
|
||||||
|
|
||||||
|
Once enabled, find it in the sidebar: **Site Monitoring**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
When the add-on isn't enabled, the page reads: *"AI monitoring is not enabled for any of your sites. Contact your hosting provider to enable this feature."*
|
||||||
|
|
||||||
|
Once enabled, the page shows a feed of events with severity badges and counts. Click an event for the full log context.
|
||||||
|
|
||||||
|
## Tuning false positives
|
||||||
|
|
||||||
|
Some signatures are noisier than others — for example, a security scanner you run yourself will trip every exploit-signature rule. Open the rule from the event detail and click **Ignore** to mute it for your account; the rule stays active for everyone else.
|
||||||
|
|
||||||
|
If you find yourself muting a rule frequently, [open a ticket](https://secure.anhonesthost.com/submitticket.php) — we may be able to tune the underlying rule for everyone instead of you suppressing each instance.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Add-ons overview](/whp/add-ons/overview/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
title: Optimized Webserver (OpenLiteSpeed + LSCache)
|
||||||
|
description: Run your sites on OpenLiteSpeed with server-level full-page caching for dramatically faster page delivery — ideal for WordPress and other dynamic CMS sites.
|
||||||
|
sidebar:
|
||||||
|
order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import PaidAddon from '~/content/partials/paid-addon-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<PaidAddon />
|
||||||
|
|
||||||
|
The Optimized Webserver add-on moves your sites from standard Apache/PHP onto our shared **OpenLiteSpeed** pool with the **LiteSpeed Cache (LSCache)** engine. LSCache is a server-level full-page cache built directly into the web server — cached pages are served in a few milliseconds, before PHP ever runs.
|
||||||
|
|
||||||
|
This is an **account-level** add-on: purchase it once and you can put as many of your hosted sites on the premium backend as you like — no per-site charge.
|
||||||
|
|
||||||
|
## Why use it
|
||||||
|
|
||||||
|
### Dramatically faster cacheable pages
|
||||||
|
|
||||||
|
LSCache operates at the web-server layer, not inside WordPress or PHP. When a page is cached, the response is served in a few milliseconds with no PHP execution and no database queries. For a WordPress site that normally takes 300–800 ms to generate a page, cached hits feel instant to visitors.
|
||||||
|
|
||||||
|
### Built for dynamic CMS sites — especially WordPress
|
||||||
|
|
||||||
|
OpenLiteSpeed works directly with the official [LiteSpeed Cache plugin for WordPress](https://wordpress.org/plugins/litespeed-cache/). The plugin manages cache purging automatically: publish a post, update a page, or complete a WooCommerce order and the right cache entries are dropped so visitors see fresh content immediately.
|
||||||
|
|
||||||
|
### Handles traffic spikes efficiently
|
||||||
|
|
||||||
|
Because cached hits skip PHP and the database entirely, the server can absorb a burst of traffic without proportionally increasing CPU or memory load. Sites on the standard backend can stall under sudden spikes; on LSCache, most of that load is absorbed before it reaches PHP.
|
||||||
|
|
||||||
|
### HTTP/3 and QUIC
|
||||||
|
|
||||||
|
OpenLiteSpeed has native HTTP/3/QUIC support. Visitors on modern browsers get the lower-latency transport automatically — no configuration needed.
|
||||||
|
|
||||||
|
### Efficient memory profile on shared hosting
|
||||||
|
|
||||||
|
Our shared OpenLiteSpeed pool is tuned for multi-tenant use. You get the performance benefits without needing a dedicated server or a much larger container allocation.
|
||||||
|
|
||||||
|
## When you may NOT want it
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Read this section before switching. The performance gains are real, but not every site benefits equally — and there are a few cases where the standard backend is the right choice.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
- **Purely static sites or very low-traffic sites.** If your site serves a handful of visitors a day, the standard backend is already fast enough. You won't see a meaningful difference, and the add-on cost won't pay off.
|
||||||
|
- **Highly personalised or uncacheable output.** Sites that generate unique content per visitor on every request (custom dashboards, paywalled pages with per-user state, complex session-driven flows) can't benefit much from a full-page cache. LSCache has smarts for partial caching, but if the majority of your pages can't be cached, the speed gains are limited.
|
||||||
|
- **You haven't tested your site on it.** We don't auto-migrate sites. You opt each site in yourself, verify everything looks right — plugins, checkout flows, member areas — and only then commit. Cache behaviour can surface edge cases (stale content after a post update, logged-in users seeing cached guest pages) that need a plugin configuration tweak. Test before you rely on it.
|
||||||
|
- **Cold cache after switching.** The first time each page is requested after you switch, it runs through PHP as normal (the cache starts empty). Subsequent hits are served from cache. Don't judge performance on the very first load.
|
||||||
|
- **Don't stack full-page cache plugins.** If you already run a second full-page caching plugin (W3 Total Cache, WP Super Cache, WP Fastest Cache, etc.) alongside LSCache, they'll conflict. Disable any other full-page caching plugin when you switch; LSCache replaces it, not supplements it. Object-cache and CDN integrations are fine.
|
||||||
|
|
||||||
|
## How to enable it
|
||||||
|
|
||||||
|
### Step 1 — Purchase the add-on
|
||||||
|
|
||||||
|
The Optimized Webserver add-on is purchased from the [client portal](https://secure.anhonesthost.com/clientarea.php). Go to **Services → My Services**, open your hosting plan, then **Upgrade/Downgrade → Optimized Webserver**. Once confirmed, the option becomes available in WHP within a few minutes.
|
||||||
|
|
||||||
|
### Step 2 — Switch individual sites onto it
|
||||||
|
|
||||||
|
Enabling the add-on at the account level doesn't automatically move any of your sites. You opt each site in one at a time:
|
||||||
|
|
||||||
|
1. In WHP, go to **Sites** and click **Edit** on the site you want to switch.
|
||||||
|
2. Under **PHP / Backend**, choose one of the **LiteSpeed PHP** options.
|
||||||
|
3. Save. The site container is recreated on the OpenLiteSpeed pool — there is a brief moment of downtime (usually under 30 seconds) during the switchover.
|
||||||
|
|
||||||
|
For a full walkthrough, see [Switching a site's backend](/whp/how-to/switching-site-backend/).
|
||||||
|
|
||||||
|
### Step 3 — Install (or verify) the LiteSpeed Cache plugin
|
||||||
|
|
||||||
|
For WordPress sites, install the free [LiteSpeed Cache plugin](https://wordpress.org/plugins/litespeed-cache/) from the WordPress plugin directory if it isn't already active. It handles cache purging on content changes automatically. Default settings work well for most sites; advanced tuning options are in the plugin's settings panel.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
If you were previously running another full-page caching plugin, deactivate (don't just disable caching — actually deactivate) it after switching. Leaving two full-page caches active causes unpredictable behaviour.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Cancelling
|
||||||
|
|
||||||
|
To cancel the Optimized Webserver add-on you must first **move all your sites back to a standard backend**. The panel will block cancellation while any site is still using a LiteSpeed PHP option.
|
||||||
|
|
||||||
|
Switch each affected site back via **Sites → Edit → PHP / Backend**, then cancel the add-on from the [client portal](https://secure.anhonesthost.com/clientarea.php).
|
||||||
|
|
||||||
|
For the site-switching steps, see [Switching a site's backend](/whp/how-to/switching-site-backend/).
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
title: Add-ons overview
|
||||||
|
description: Optional features you can layer on your hosting plan — monitoring, archival email, resource upgrades, and more.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Add-ons are **optional extras** you can layer onto your base hosting plan. Each is billed separately and can be enabled or removed from your client portal at any time.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
An add-on is billed in addition to your base plan. Removing an add-on stops the charge from the next billing cycle.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## What's available
|
||||||
|
|
||||||
|
- **[Site Monitoring](/whp/add-ons/monitoring/)** — proactive alerts when something looks wrong (error spikes, brute-force attempts, exploit signatures).
|
||||||
|
- **[Archival email](/whp/add-ons/archival-email/)** — long-term, searchable retention of your mail outside the live mailbox.
|
||||||
|
- **[Resource upgrades](/whp/add-ons/resource-upgrades/)** — extra CPU, RAM, or storage without changing plans.
|
||||||
|
- **[Email upgrades](/whp/add-ons/email-upgrades/)** — extra mailboxes or larger per-mailbox storage caps.
|
||||||
|
- **[Optimized Webserver](/whp/add-ons/optimized-webserver/)** — run your sites on OpenLiteSpeed with server-level LSCache for dramatically faster page delivery.
|
||||||
|
|
||||||
|
## How billing works
|
||||||
|
|
||||||
|
Add-ons are **pro-rated** to align with your base plan's billing cycle and appear as separate line items on your next invoice. Cancelling an add-on stops it from the next cycle — you keep access through the end of the period you've already paid for.
|
||||||
|
|
||||||
|
## How to enable an add-on
|
||||||
|
|
||||||
|
1. Sign in to the [client portal](https://secure.anhonesthost.com/clientarea.php).
|
||||||
|
2. Go to **Services → My Services** and pick your hosting plan.
|
||||||
|
3. Click **Upgrade/Downgrade** (sometimes called **Add-ons** depending on the add-on).
|
||||||
|
4. Pick the add-on, review the price, and confirm.
|
||||||
|
|
||||||
|
WHP applies the change usually within a few minutes — no downtime for any of the add-ons listed here.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
title: Resource upgrades
|
||||||
|
description: Add CPU, RAM, or disk to your container without migrating to a different plan.
|
||||||
|
sidebar:
|
||||||
|
order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
import PaidAddon from '~/content/partials/paid-addon-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<PaidAddon />
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
Boost your container's **CPU**, **RAM**, or **persistent disk** without migrating to a new plan. Useful when you've outgrown your base allocation but the next plan tier up is more than you actually need.
|
||||||
|
|
||||||
|
## What you can upgrade
|
||||||
|
|
||||||
|
Each resource is independent — upgrade only what you need.
|
||||||
|
|
||||||
|
- **CPU cores per container** — visible on the Sites page as **CPU per Container (cores)** (default `0.25`). Add cores for sustained-compute workloads.
|
||||||
|
- **RAM per container** — visible on the Sites page as **Memory per Container (MB)** (default `256`). For memory-hungry apps (caches, large WordPress sites with many plugins, Node apps holding big in-memory state).
|
||||||
|
- **Number of containers** — scale a busy site horizontally from 1 up to 10 replicas; WHP load-balances traffic across them.
|
||||||
|
- **Persistent disk** — for sites that store a lot of files (media libraries, user uploads, etc.).
|
||||||
|
|
||||||
|
## When you might need this
|
||||||
|
|
||||||
|
- **High CPU on busy days.** Sustained-load sites consistently hit the per-container CPU cap during peak hours.
|
||||||
|
- **"Out of memory" errors** in your app log.
|
||||||
|
- **Disk usage approaching 80%** of your allocation. Get ahead of it — full disks cause backup failures and uploads to fail.
|
||||||
|
|
||||||
|
## How to enable
|
||||||
|
|
||||||
|
For per-container CPU/RAM upgrades, edit the values directly on the **Sites** page for the site you want to change, and click **Save**. For account-wide upgrades (more total RAM, more total disk), open the [client portal](https://secure.anhonesthost.com/clientarea.php), go to your hosting service → **Upgrade/Downgrade**, and pick the resource increment.
|
||||||
|
|
||||||
|
## See your current usage
|
||||||
|
|
||||||
|
Open the **Dashboard** page in WHP. The **Server Information** and account stats show what you're consuming. For per-site usage, open a site from the **Sites** page.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Site Monitoring](/whp/add-ons/monitoring/) — catches resource saturation before customers complain.
|
||||||
|
- [Add-ons overview](/whp/add-ons/overview/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
title: Backups
|
||||||
|
description: How WHP's automatic backups work, the default-target requirement, full-server backups vs customer data backups, and managing backup targets.
|
||||||
|
sidebar:
|
||||||
|
order: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
WHP backs up **customer data** (sites and databases) when an admin has configured a default backup target. WHP does **not** back up the full server — that's a separate concern.
|
||||||
|
|
||||||
|
## Two different things called "backup"
|
||||||
|
|
||||||
|
It's worth being precise:
|
||||||
|
|
||||||
|
- **Customer data backups** — what WHP does. Site files (per user, per domain) and databases. Configured per server via backup targets. These start automatically once a default target exists.
|
||||||
|
- **Full server backups** — backing up the host OS, `/etc`, container images, the WHP install itself, etc. **WHP does not do this.**
|
||||||
|
|
||||||
|
### Full server backups
|
||||||
|
|
||||||
|
| Where WHP runs | Who owns full-server backups |
|
||||||
|
|---|---|
|
||||||
|
| **Our Virtual Dedicated Server (VDS)** plans | Included. AnHonestHost snapshots the VDS at the platform level. |
|
||||||
|
| **Anywhere else** | The server operator. WHP doesn't ship a full-server backup mechanism — you'll need to set up something at the OS / hypervisor level. |
|
||||||
|
|
||||||
|
If you're running WHP on your own infrastructure, plan accordingly. WHP's configuration isn't all in `/etc` — there are config and state files in the WHP install directory, in Docker volumes for the platform containers (HAProxy, MySQL, Postgres, Valkey, the WAF), and in service-specific paths elsewhere on the host. The safest approach is a **full-server backup** (image snapshot or filesystem-level backup) rather than trying to enumerate paths.
|
||||||
|
|
||||||
|
## Sign in as super admin
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## How customer auto-backups start
|
||||||
|
|
||||||
|
**Customer auto-backups don't run until at least one default backup target exists.** A fresh WHP install has no targets and no schedule — every user's backup status is "no targets available" until an admin sets one up.
|
||||||
|
|
||||||
|
Once a default target is configured, the platform begins automatic daily backups for every customer with sites or databases. Customers see their own backup history under the [Backups](/whp/how-to/backups/) page in their account.
|
||||||
|
|
||||||
|
## The Backup Management page
|
||||||
|
|
||||||
|
Sidebar → **Backups**. The admin view of this page mirrors the customer view, with extra controls:
|
||||||
|
|
||||||
|
- **Stat tiles** — Total Backups, Total Size, Sites, Databases. Server-wide totals across every account.
|
||||||
|
- **Create New Backup** — fires an on-demand backup. Admin form adds a **User** dropdown so you can backup any customer's account, not just your own.
|
||||||
|
- **Backup Targets** — the table of destinations available on the server. The **Global** column distinguishes shared targets from per-account targets.
|
||||||
|
|
||||||
|
### Backup targets
|
||||||
|
|
||||||
|
Each target row has:
|
||||||
|
|
||||||
|
- **Name** — your label for the destination.
|
||||||
|
- **Type** — S3 (and other supported types) — built-in support for S3-compatible storage (AWS S3, Cloudflare R2, MinIO, etc.).
|
||||||
|
- **Connection** — the endpoint URL and bucket / path.
|
||||||
|
- **Retention** — how long backups are kept (default 5 days).
|
||||||
|
- **Max Backups** — cap on the number of snapshots retained (default 10).
|
||||||
|
- **Global** — `Yes` if every account can use this target, `No` if it's bound to a single account.
|
||||||
|
- **Actions** — **Test** (verify credentials and write a probe object), **Edit**, **Delete**.
|
||||||
|
|
||||||
|
### Adding a global backup target
|
||||||
|
|
||||||
|
Click **+ Add Backup Target** and fill in:
|
||||||
|
|
||||||
|
1. **Name** — descriptive label.
|
||||||
|
2. **Type** — pick S3 (or whichever storage backend you want).
|
||||||
|
3. **Endpoint URL** — for non-AWS S3 (Cloudflare R2, MinIO, Wasabi, etc.), point at the provider's endpoint.
|
||||||
|
4. **Bucket / path**.
|
||||||
|
5. **Access key / Secret** — the credentials WHP will use.
|
||||||
|
6. **Retention / Max Backups** — server-wide defaults for any account that uses this target.
|
||||||
|
7. **Global** — leave **on** so every account can use it as their default destination.
|
||||||
|
|
||||||
|
Click **Test** before saving to confirm WHP can reach the bucket. A good target round-trips a probe object in under a second.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Keep one global target as the default. Customers who want their own offsite (e.g. their own S3 bucket) can add a per-account, non-global target — visible in the same table for the admin, but only usable by that one customer.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Per-customer (non-global) targets
|
||||||
|
|
||||||
|
Customers can add their own backup targets from the customer-side Backups page. Those show up here with **Global: No** plus a note linking them to the owning account. Admins can edit or delete those on the customer's behalf when they need help.
|
||||||
|
|
||||||
|
### Triggering an on-demand backup for a user
|
||||||
|
|
||||||
|
In the **Create New Backup** form:
|
||||||
|
|
||||||
|
1. Pick the customer in the **User** dropdown.
|
||||||
|
2. Pick a **Backup Type** (Sites / Databases / both).
|
||||||
|
3. Pick a **Backup Target**.
|
||||||
|
4. **Start Backup**. Progress is visible in the run history below.
|
||||||
|
|
||||||
|
This is the right path when a customer asks for a fresh backup right before a risky migration.
|
||||||
|
|
||||||
|
## Verifying customer backups are succeeding
|
||||||
|
|
||||||
|
The Total Backups and Total Size tiles climb over time on a healthy server. If they sit flat:
|
||||||
|
|
||||||
|
- Confirm at least one target exists with **Global: Yes**.
|
||||||
|
- Open the run history (lower on the page) and look for failed entries — the error message usually points at credentials or quota.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**No backups are running for any customer.** Confirm at least one **Global: Yes** target exists and that its **Test** button returns success. Without a global default, the scheduler doesn't fire.
|
||||||
|
|
||||||
|
**One target is failing.** Click **Test** on the target row. The most common causes are rotated credentials, an incorrect endpoint URL (R2 / MinIO often need an explicit endpoint different from AWS's default), or the bucket lifecycle policy deleting backups before the retention window.
|
||||||
|
|
||||||
|
**Customer says their backup is too old.** Check **Max Backups** on the target — if it's lower than their backup cadence × retention window, older backups get pruned.
|
||||||
|
|
||||||
|
**Backup ran but `tar` step failed mid-stream.** Disk pressure on the host is a common cause. Check **Disk Usage** (admin sidebar) and consider raising the target's retention so fewer backups stack up on the host before upload.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Backups](/whp/how-to/backups/) — the customer-facing side.
|
||||||
|
- [Server settings & services](/whp/admin/server-settings/) — including Backup Upload host service.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
title: Coraza WAF rules
|
||||||
|
description: Set the global WAF mode, tune individual rules, audit blocked requests, and add per-host overrides.
|
||||||
|
sidebar:
|
||||||
|
order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
[Coraza](https://coraza.io/) is an open-source web-application firewall (WAF) that runs as a sidecar in front of your sites. The **Coraza Rules** page in the admin sidebar gives you a UI to set the global mode, tune individual rules, and audit blocked requests — all without rebuilding the sidecar image.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Global WAF mode
|
||||||
|
|
||||||
|
A coloured pill at the top of the page shows the **Global WAF mode** with a **change** link:
|
||||||
|
|
||||||
|
- **Off** — no inspection. Requests pass untouched.
|
||||||
|
- **Detect** — inspect and log matches; do not block. Use during a roll-out or while validating a rule change.
|
||||||
|
- **Enforce** — inspect and block anything that matches an enforcing rule.
|
||||||
|
|
||||||
|
The WAF is **fail-open**: if the Coraza sidecar is itself unhealthy, traffic still flows.
|
||||||
|
|
||||||
|
## Sign in as super admin
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## The three tabs
|
||||||
|
|
||||||
|
### Firing rules
|
||||||
|
|
||||||
|
The default view. Each row is a rule that has matched at least one request in the selected time window (toggle **Last 24h** or **Last 7d** at top right).
|
||||||
|
|
||||||
|
Columns:
|
||||||
|
|
||||||
|
- **Rule ID** — the CRS or custom rule identifier. **view** opens the rule definition. **Ask AI** opens an explanation of what this rule catches.
|
||||||
|
- **Hits** — how many times the rule fired in the time window.
|
||||||
|
- **Top hosts** — the top customer domains that triggered this rule (with per-host hit counts).
|
||||||
|
- **Top URIs** — the most common request paths that triggered it (helpful for distinguishing scans from real traffic).
|
||||||
|
- **State** — per-rule override of the global mode. Options: `(default → enforce)`, `(default → detect)`, `(default → score)`, `off`. Picking anything other than `(default → …)` overrides the global mode for that rule.
|
||||||
|
- **Per-host** — opens a **Hosts (N)** drawer to set per-host overrides for that rule.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Rule changes apply on the next request. The page reloads the sidecar via SIGHUP after every save (~10ms — no traffic blip).
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### CRS catalog
|
||||||
|
|
||||||
|
The full OWASP Core Rule Set catalogue (v4 families: 901, 905, 911, 913, 920–922, 930–934, 941–944, 949, 950–956, 959, 980). Use this when you want to look up a rule that *hasn't* fired yet — for example, to pre-mute a rule you know will produce false positives on a specific app.
|
||||||
|
|
||||||
|
### Activity
|
||||||
|
|
||||||
|
A timeline / log of every WAF block in the audit window. Use it to:
|
||||||
|
|
||||||
|
- Confirm a customer report (cross-reference the **X-Request-Reference** UUID on the visitor-facing 403 page).
|
||||||
|
- Spot bursts of activity from the same source IP.
|
||||||
|
- Tune source-of-truth back-end queries — the audit is read from `security.db`.
|
||||||
|
|
||||||
|
## Common tasks
|
||||||
|
|
||||||
|
### Roll a new site onto the WAF
|
||||||
|
|
||||||
|
1. Set the rule's **State** to `(default → detect)` for that site via the Per-host drawer.
|
||||||
|
2. Drive normal traffic for at least 24 hours.
|
||||||
|
3. Open the **Activity** tab and filter to that host. Confirm no legitimate request is matching an enforcing rule.
|
||||||
|
4. Flip the host to **enforce**.
|
||||||
|
|
||||||
|
### Mute a noisy rule
|
||||||
|
|
||||||
|
1. Find the rule in **Firing rules**.
|
||||||
|
2. Click **Per-host → Hosts (N)** to add a per-site mute, OR change the rule's **State** column to mute it globally.
|
||||||
|
|
||||||
|
Prefer per-host. Global mute weakens the WAF for every site.
|
||||||
|
|
||||||
|
### Audit a block
|
||||||
|
|
||||||
|
Customer reports a request was wrongly blocked? The branded 403 page includes an **X-Request-Reference** UUID. Cross-reference it:
|
||||||
|
|
||||||
|
1. In the **Activity** tab, search for the UUID.
|
||||||
|
2. The row shows the matched rule ID, source IP, URL, and offending parameter.
|
||||||
|
3. Decide whether to mute the rule (see above) or leave it — many "false positives" turn out to be real attempts.
|
||||||
|
|
||||||
|
## Implementation notes
|
||||||
|
|
||||||
|
- **Source of truth: `security.db`.** All rule edits go through the panel and write to that SQLite file; manual file edits won't survive a regeneration.
|
||||||
|
- **Reload mechanism.** Rule edits apply via SIGHUP to `coraza-spoa`; adding or removing whole rule files requires a full container restart, which the panel performs when the change needs it.
|
||||||
|
- **Real source IPs are in the audit log** — even with HAProxy in front of the WAF, the real client IP is propagated through the SPOE messages.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**A rule shows enabled but doesn't fire.** Check that the host is in **detect** or **enforce** mode. A host in **off** bypasses every rule.
|
||||||
|
|
||||||
|
**The Activity log is empty.** Confirm `coraza-spoa` is healthy on the **Services** tab of Server Settings. If it's restarting in a loop, check the container logs — usually a malformed rule file or a missing include.
|
||||||
|
|
||||||
|
**Edits revert on restart.** Make sure you're editing through the panel; manual edits outside the panel-managed path are overwritten.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Server settings & services](/whp/admin/server-settings/)
|
||||||
|
- [AI Monitor, Issues & Ignore Rules](/whp/admin/site-monitoring/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
---
|
||||||
|
title: Data-drive encryption (LUKS)
|
||||||
|
description: Optional LUKS2 encryption of the /docker data volume on new server installs. Encrypts customer data, databases, and container state at rest; adds a manual unlock step after every reboot.
|
||||||
|
sidebar:
|
||||||
|
order: 7
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
WHP can encrypt the **`/docker` data volume** — where customer site files,
|
||||||
|
databases, container state, and SSL material live — using LUKS2 (the same
|
||||||
|
disk-encryption layer used by every mainstream Linux distribution).
|
||||||
|
|
||||||
|
When enabled, anyone who walks off with the physical disk (or images it
|
||||||
|
offline) sees only ciphertext. The encryption key never lives on disk in
|
||||||
|
plaintext.
|
||||||
|
|
||||||
|
<Aside type="caution" title="Available only on new server installs">
|
||||||
|
Data-drive encryption must be enabled when the server is **first
|
||||||
|
provisioned**. There is no in-place "encrypt this existing server"
|
||||||
|
workflow today — switching a live server to encrypted storage requires a
|
||||||
|
manual data migration and is currently handled as a custom engagement.
|
||||||
|
Open a [support ticket](https://secure.anhonesthost.com/submitticket.php)
|
||||||
|
if you're considering this for an existing server.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## What changes if you enable it
|
||||||
|
|
||||||
|
The trade-off is one extra manual step every time the server reboots.
|
||||||
|
|
||||||
|
**At install time:**
|
||||||
|
- A strong passphrase (32 hex characters) is generated and **shown to you
|
||||||
|
once** in the install banner. You're responsible for saving it before
|
||||||
|
install completes — there is no recovery if it's lost.
|
||||||
|
|
||||||
|
**On every reboot (planned or unplanned):**
|
||||||
|
- The `/docker` volume comes up **locked**. The regular WHP control panel
|
||||||
|
at `:8443` is offline until you unlock it.
|
||||||
|
- The host itself boots normally — SSH, networking, monitoring, etc.
|
||||||
|
remain reachable.
|
||||||
|
- You unlock via either:
|
||||||
|
- **Web:** browse to `https://<server>:8444/`, sign in with the server's
|
||||||
|
`root` credentials, and paste the LUKS passphrase. The form redirects
|
||||||
|
you back to the regular panel after the volume mounts and services
|
||||||
|
come up (around 30 seconds end-to-end).
|
||||||
|
- **SSH:** run `/root/whp/scripts/whp-unlock-data-drive` and enter the
|
||||||
|
passphrase at the prompt. Same outcome.
|
||||||
|
|
||||||
|
**While unlocked:**
|
||||||
|
- The server runs identically to a non-encrypted server. No performance
|
||||||
|
penalty noticeable for typical hosting workloads.
|
||||||
|
|
||||||
|
<Aside type="caution" title="Plan for the reboot overhead">
|
||||||
|
Customer sites are unreachable from the moment the server reboots until
|
||||||
|
someone unlocks `/docker`. **Plan reboots accordingly** — and make sure
|
||||||
|
the right people have both the root credentials and the LUKS passphrase
|
||||||
|
before the first reboot.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## The security model in plain English
|
||||||
|
|
||||||
|
LUKS protects you against **data at rest** being read by someone without
|
||||||
|
the passphrase. Specifically:
|
||||||
|
|
||||||
|
- **Stolen or lost disk:** the device is unreadable without the passphrase.
|
||||||
|
- **Disk RMA / drive replacement:** you can return drives without secure
|
||||||
|
erase; the data on them is ciphertext.
|
||||||
|
- **Offline forensic imaging:** an attacker who can power off the server
|
||||||
|
and copy the disk gets ciphertext.
|
||||||
|
|
||||||
|
It does **not** protect against:
|
||||||
|
|
||||||
|
- **A live, running server being compromised** — once `/docker` is
|
||||||
|
unlocked, the data is plaintext in memory and accessible to anyone with
|
||||||
|
root on the host. (This is the same as any other Linux server.)
|
||||||
|
- **Lost passphrase** — there is no recovery key, no master override, no
|
||||||
|
vendor reset. Losing the passphrase means losing the data.
|
||||||
|
|
||||||
|
If your threat model is "compliance / customer expectations around
|
||||||
|
data-at-rest encryption," LUKS covers it. If it's "attacker has live
|
||||||
|
shell on the server," LUKS does nothing additional — you need separate
|
||||||
|
controls (access policy, monitoring, segmentation).
|
||||||
|
|
||||||
|
## The unlock surface in detail
|
||||||
|
|
||||||
|
The unlock daemon (`whp-locked-unlock.service`) is a small standalone
|
||||||
|
HTTPS server that runs **independently** of the main control panel
|
||||||
|
(Apache + PHP-FPM). It exists for exactly one purpose: prompt for and
|
||||||
|
validate the LUKS passphrase, then hand off to the helper that opens the
|
||||||
|
volume and starts the customer-facing services.
|
||||||
|
|
||||||
|
- **Port:** `8444` (separate from the regular panel on `:8443` / `:8080`)
|
||||||
|
- **TLS:** if your server has a real Let's Encrypt certificate for its
|
||||||
|
hostname (the usual case on AnHonestHost-managed installs), the daemon
|
||||||
|
uses that. If not, it uses the same self-signed cert that the regular
|
||||||
|
panel falls back to.
|
||||||
|
- **Authentication:** the server's root credentials via PAM (same login
|
||||||
|
as the regular control panel), **plus** the LUKS passphrase. Both must
|
||||||
|
be correct.
|
||||||
|
- **Rate limit:** five failed attempts per IP per five minutes triggers a
|
||||||
|
short lockout. You can unlock from another IP, from SSH, or wait for
|
||||||
|
the window to expire.
|
||||||
|
- **Auto-stop:** once the unlock succeeds, the daemon exits cleanly. It
|
||||||
|
comes back automatically on the next reboot.
|
||||||
|
|
||||||
|
After the web unlock, the regular control panel at `:8443` becomes
|
||||||
|
reachable within ~30 seconds (the time it takes Docker, Apache, and the
|
||||||
|
boot orchestrator to start the customer containers).
|
||||||
|
|
||||||
|
## Header backup — critical for recovery
|
||||||
|
|
||||||
|
The LUKS2 header is a small region (~16 MB) at the start of the
|
||||||
|
encrypted device that holds the keyslot data. If that region gets
|
||||||
|
corrupted (bad sectors, partition table accident, careless `dd`), the
|
||||||
|
data is unrecoverable **even with the correct passphrase**.
|
||||||
|
|
||||||
|
WHP keeps a header backup automatically:
|
||||||
|
|
||||||
|
- **Initial backup** at `/etc/whp/luks-header.backup`, created during the
|
||||||
|
install.
|
||||||
|
- **Daily refresh** via cron — only writes a new file when the header
|
||||||
|
has actually changed (i.e. on passphrase rotation).
|
||||||
|
- **Rolling 30-day history** at `/var/lib/whp/luks-headers/`.
|
||||||
|
- **Off-host upload** if you've configured an rclone remote named
|
||||||
|
`whp-system-backup`. We strongly recommend setting this up so the
|
||||||
|
header isn't only on the same disk it's protecting.
|
||||||
|
|
||||||
|
In a header-corruption scenario, restoring is a one-command operation
|
||||||
|
using the saved backup, then unlocking with your passphrase as normal.
|
||||||
|
|
||||||
|
## Rotating the passphrase
|
||||||
|
|
||||||
|
If you need to change the LUKS passphrase (staff rotation, suspected
|
||||||
|
compromise of the saved copy, etc.), do it in this order:
|
||||||
|
|
||||||
|
1. **Add the new passphrase** as a second keyslot — the volume now
|
||||||
|
accepts either one.
|
||||||
|
2. **Verify the new passphrase works** with a non-destructive test.
|
||||||
|
3. **Remove the old passphrase** from its keyslot.
|
||||||
|
|
||||||
|
The full command sequence is documented in the operator runbook on the
|
||||||
|
server itself (`/root/whp/docs/LUKS_RUNBOOK.md`). After any keyslot
|
||||||
|
change, the daily header backup picks up the new header SHA and uploads
|
||||||
|
a fresh copy on its next run.
|
||||||
|
|
||||||
|
## Things to know before you commit to it
|
||||||
|
|
||||||
|
- **Lost passphrase = lost data.** This is the single most important
|
||||||
|
thing to plan around. Treat the passphrase the way you'd treat a
|
||||||
|
payment-processor master key.
|
||||||
|
- **Every reboot needs someone awake.** Auto-restart watchdogs, kernel
|
||||||
|
updates that trigger a reboot, and power events all become events
|
||||||
|
that require manual intervention. If your team can't respond within
|
||||||
|
your acceptable downtime window, this isn't the right fit.
|
||||||
|
- **Server-side backups still matter.** Encryption protects against
|
||||||
|
data theft. It does not protect against data loss. Your customer
|
||||||
|
backup configuration and the LUKS header backup are independent
|
||||||
|
concerns — both need to be in place.
|
||||||
|
- **Boot drive is unencrypted.** The OS, the control panel itself, and
|
||||||
|
the LUKS configuration file all live on the unencrypted boot drive.
|
||||||
|
This is intentional — it lets the unlock UI come up and lets you SSH
|
||||||
|
in for recovery before `/docker` is available.
|
||||||
|
|
||||||
|
## When LUKS is **not** the right choice
|
||||||
|
|
||||||
|
- Servers where unattended reboots are operationally required (e.g.
|
||||||
|
automated kernel updates with reboot on a recurring schedule with no
|
||||||
|
human in the loop).
|
||||||
|
- Servers where the only "threat" is the existing AnHonestHost
|
||||||
|
operational controls — if your compliance posture doesn't ask for
|
||||||
|
data-at-rest encryption specifically, the operational overhead may
|
||||||
|
not be worth it.
|
||||||
|
- Existing servers with live customer data on them — wait for the
|
||||||
|
retrofit workflow rather than attempting a manual migration without
|
||||||
|
guidance.
|
||||||
|
|
||||||
|
## Setting this up on a new server
|
||||||
|
|
||||||
|
If you're spinning up a fresh server and want data-drive encryption,
|
||||||
|
mention it in the provisioning ticket and we'll configure it as part of
|
||||||
|
the install. We'll send you the generated passphrase through a secure
|
||||||
|
channel and confirm you have it stored before the server is handed over.
|
||||||
|
|
||||||
|
For technically-inclined customers running WHP themselves, the install
|
||||||
|
flag is documented in the operator runbook that ships on every server at
|
||||||
|
`/root/whp/docs/LUKS_RUNBOOK.md`.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Server settings & services](/whp/admin/server-settings/) — restart
|
||||||
|
individual services after an unlock, view system info.
|
||||||
|
- [Backups](/whp/admin/backups/) — independent of LUKS; both should be
|
||||||
|
configured.
|
||||||
|
- [Admin overview](/whp/admin/overview/) — other super-admin features.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
title: Admin overview
|
||||||
|
description: What WHP super admin unlocks — server-wide controls for services, mail, DNS, security, monitoring, and users.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
## What super admin unlocks
|
||||||
|
|
||||||
|
WHP's super admin role exposes server-wide pages alongside the customer-facing nav. The customer pages (Sites, Domains, Email, etc.) work identically for everyone; the admin pages sit alongside them and are gated to the super admin only.
|
||||||
|
|
||||||
|
**Today, the only super admin is the `root` user on the server.** There's no UI to add another super admin — if you need additional people to have super admin, share the root credentials via your usual secret-sharing flow (or [open a ticket](https://secure.anhonesthost.com/submitticket.php) if you need a different model).
|
||||||
|
|
||||||
|
This typically applies to customers running a [Virtual Dedicated Server](https://anhonesthost.com/vds) — they get full server control as part of the plan and sign in to WHP as `root`.
|
||||||
|
|
||||||
|
### Signing in as super admin
|
||||||
|
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## Admin-only sidebar sections
|
||||||
|
|
||||||
|
When you sign in as a super admin, these sections appear in addition to the customer-facing nav:
|
||||||
|
|
||||||
|
- **AI Monitor** — the admin dashboard, plus **Issues**, **Site Reports**, and **Ignore Rules**.
|
||||||
|
- **Security Management** — security policy across the server.
|
||||||
|
- **Coraza Rules** — Web-application firewall (WAF) rule tuning, audit, and global mode.
|
||||||
|
- **User Management** — create WHP users, set account types, manage existing users.
|
||||||
|
- **User Resources** — per-user CPU/RAM/disk allowances and current usage.
|
||||||
|
- **Delegated Users** — list of contractor / sub-account access grants.
|
||||||
|
- **Active Sessions** — every signed-in browser across the server.
|
||||||
|
- **Server Settings** — System, Services, Mail, DNS, Network & SSL, Security tabs.
|
||||||
|
- **Disk Usage** — server-wide disk consumption breakdown.
|
||||||
|
- **Announcements Management** — edit the announcements that appear on every customer's dashboard.
|
||||||
|
- **Update Management** — apply WHP platform updates.
|
||||||
|
- **Docker Management** — see and manage every container on the host.
|
||||||
|
- **Valkey Admin** — server-wide Valkey configuration.
|
||||||
|
- **Container Boot & Health** — boot-order and per-container health.
|
||||||
|
- **Site Disable Audit** — record of sites disabled / re-enabled.
|
||||||
|
- **Account Suspensions** — suspended customer accounts.
|
||||||
|
|
||||||
|
## What's in this section
|
||||||
|
|
||||||
|
- **[Server settings & services](/whp/admin/server-settings/)** — the six tabs under Server Settings: system info, restart services, mail-server config, DNS / nameservers, HAProxy + SSL, and integration API keys.
|
||||||
|
- **[Coraza WAF rules](/whp/admin/coraza-waf/)** — set the global WAF mode, tune individual rules, and audit blocked requests.
|
||||||
|
- **[AI Monitor, Issues & Ignore Rules](/whp/admin/site-monitoring/)** — the three pages that drive the Site Monitoring add-on.
|
||||||
|
- **[Users & delegated access](/whp/admin/user-management/)** — create accounts, set account types, delegate access, and handle suspensions.
|
||||||
|
- **[Backups](/whp/admin/backups/)** — configure the default backup target so customer auto-backups start running. Full-server backups are a separate, plan-dependent concern.
|
||||||
|
- **[Data-drive encryption (LUKS)](/whp/admin/data-drive-encryption/)** — optional at-rest encryption of the `/docker` data volume. Available only on new server installs; adds a manual unlock step after every reboot.
|
||||||
|
|
||||||
|
## Things to know before you change server-wide settings
|
||||||
|
|
||||||
|
- **One change can affect every site on the server.** Where customer-side pages scope changes to one site, admin pages typically scope to the whole server.
|
||||||
|
- **Service restarts are visible to live traffic.** Restart Apache or PHP-FPM during a quiet window when possible.
|
||||||
|
- **Customer backups don't cover server config.** The [customer backups](/whp/how-to/backups/) you've configured snapshot site files and databases, not server-level changes you make by hand. Server config lives in several places — not just `/etc` — so the right safety net is a **full-server backup**. On AnHonestHost-managed plans and VDS we handle that for you; running WHP elsewhere, the operator is responsible. See [Backups](/whp/admin/backups/) for the full picture.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [What is containerized hosting?](/whp/getting-started/what-is-containerized-hosting/) — differences between container plans and full server access.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
title: Server settings & services
|
||||||
|
description: Restart services, configure mail server, manage DNS / nameservers, HAProxy + system SSL certificates, and integration API keys.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
The **Server Settings** page lives in the admin sidebar and has six tabs along the left rail. Each tab is a different surface area of the server you can inspect or change.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Sign in as super admin
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## The six tabs
|
||||||
|
|
||||||
|
### System
|
||||||
|
|
||||||
|
Read-only system summary plus two simple change controls:
|
||||||
|
|
||||||
|
- **System Information** — Hostname, Operating System, Kernel, Timezone, Uptime, Load Average, Disk Usage, Memory Usage.
|
||||||
|
- **Hostname Settings** — change the server's FQDN. Restart the relevant services after a hostname change.
|
||||||
|
- **Timezone Settings** — change the system timezone (affects cron timing, backup schedules, log timestamps).
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Changing the hostname affects the certificates and DNS records that reference it. Plan for the related re-issuance and DNS propagation before changing it on a live server.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Services
|
||||||
|
|
||||||
|
Service status and restart controls.
|
||||||
|
|
||||||
|
- **Service Status** — health pills for the host-side services:
|
||||||
|
- **Apache** and **PHP-FPM** on the host serve the **WHP control panel itself**, not customer sites. Customer sites run inside their own per-site containers, separate from these host services.
|
||||||
|
- **Docker** — the host's Docker daemon. If this is down, no customer container will start.
|
||||||
|
- **ProFTPD** — host FTP service (used by FTP-enabled customer accounts).
|
||||||
|
- **Backup Upload** — the host-side uploader that streams backups to your configured backup targets.
|
||||||
|
- **Restart Services** — checkboxes per service plus **Restart Selected Services**.
|
||||||
|
- **Docker Container Management** — status of the core platform containers (Mysql, Haproxy manager, Memcache, Postgresql) and a per-container **Execute Operation** picker (e.g. restart a single container).
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Restarting host Apache or PHP-FPM kicks the **control panel** offline briefly — customers can't sign in to WHP for a few seconds. It does **not** affect customer sites (those are in their own containers). For MySQL, in-flight transactions in the MySQL container roll back. Pick a quiet window when you can.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Mail
|
||||||
|
|
||||||
|
Two panels:
|
||||||
|
|
||||||
|
- **Mail Server** — set the **Mail Server Hostname** (used for MX records on new domains and as the IMAP host for archival). Configure the Mail Server API (URL, Username, Password) that WHP uses to provision mailboxes. Toggle **Enable Mailserver API Debug Logging** when troubleshooting; it writes mailserver API requests/responses to the PHP error log.
|
||||||
|
- **Outbound Email (SMTP)** — configure SMTP for outbound system alerts and customer AI Monitor notifications. Toggle **Enable Outbound Email** and provide the relay's credentials.
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
Two panels:
|
||||||
|
|
||||||
|
- **WHP Nameserver Configuration** — set the primary and secondary nameserver hostnames and IPs. These are baked into every customer's DNS zone, so changing them affects every domain you host.
|
||||||
|
- **Network DNS Settings** — set the upstream resolvers the server uses (defaults to Cloudflare `1.1.1.1` and Google `8.8.8.8`).
|
||||||
|
- **DNS Configuration Settings** — default TTL for new DNS records (60–86400 seconds).
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Changing nameserver hostnames or IPs invalidates the zone delegation for every customer domain pointed at the old values. Migrate slowly; expect lag in the order of TTL × propagation window.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Network & SSL
|
||||||
|
|
||||||
|
Operational controls for HAProxy and system-service certs:
|
||||||
|
|
||||||
|
- **HAProxy Configuration Management** — **Regenerate** (rebuild config for every active site), **Reload** (apply config without restart), **Health Check** (probe HAProxy).
|
||||||
|
- **HAProxy API Key** — Bearer token used to authenticate against the HAProxy Manager API. After rotating, restart the HAProxy container.
|
||||||
|
- **System Service SSL Certificates** — request a Let's Encrypt cert for system-level services like the WHP panel itself and FTP, by picking the service and the domain name.
|
||||||
|
|
||||||
|
<Aside type="caution" title="Panel SSL + HSTS — VM rebuild and cert change gotcha">
|
||||||
|
|
||||||
|
The WHP panel always sends `Strict-Transport-Security: max-age=31536000; includeSubDomains` on every HTTPS response. That header is part of the panel's defence-in-depth — once a browser has visited the panel over HTTPS, it will refuse to fall back to HTTP and will refuse to accept an untrusted certificate (no clickable "proceed anyway" escape).
|
||||||
|
|
||||||
|
**When this bites you:** any time the panel's certificate fingerprint changes for the **same hostname**. Most common cause: rebuilding the VM. A fresh `setup.sh` run regenerates the self-signed `/etc/pki/tls/certs/localhost.crt`. Your browser still has the year-long HSTS rule cached from the previous VM, so it refuses to connect to the new cert. Both Chrome and Firefox enforce HSTS at the browser level.
|
||||||
|
|
||||||
|
**Recovery options for the admin who's locked out of their own panel:**
|
||||||
|
|
||||||
|
1. **Use the IP directly instead of the hostname.** HSTS is scoped to host names, not IP addresses. Hit `http://<server-ip>:8080/` or `https://<server-ip>:8443/`, log in, then go straight to **Network & SSL → System Service SSL Certificates** and issue a real Let's Encrypt cert for the panel's hostname. Reload the page and the hostname URL works again.
|
||||||
|
2. **Clear the HSTS rule in your browser.**
|
||||||
|
- **Chrome / Edge:** `chrome://net-internals/#hsts` → *Delete domain security policies* → enter the panel hostname → *Delete*.
|
||||||
|
- **Firefox:** start a private window for the panel hostname, OR close Firefox, open `<profile>/SiteSecurityServiceState.txt`, and remove the line beginning with the panel hostname.
|
||||||
|
3. **Issue a real Let's Encrypt cert before the next rebuild.** Once `whp02.example.com` has a real cert that survives rebuilds (e.g., the LE private key is preserved across rebuilds, or the cert is re-issued as part of post-install automation), the HSTS rule has nothing to argue with.
|
||||||
|
|
||||||
|
Issuing a Let's Encrypt cert for the panel hostname is a first-day operation for every new server, and it's how you avoid having to recover from this scenario on rebuild #2.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
API keys for **external integrations** like WHMCS. Not customer-facing.
|
||||||
|
|
||||||
|
- **Create New API Key** — Key Name, Rate Limit (requests per hour), Permissions (User Management, Resource Management, SSO Access, System Statistics, AI Monitor Management), an optional IP whitelist, and Notes.
|
||||||
|
- **Existing API Keys** — list of issued keys with their permissions and rate limit. Revoke by removing the row.
|
||||||
|
|
||||||
|
## Server-wide configuration files
|
||||||
|
|
||||||
|
The host runs the WHP control panel and orchestrates customer containers. Most customer-affecting configuration lives **inside** containers, not on the host. A short map:
|
||||||
|
|
||||||
|
- **Control-panel Apache:** `/etc/httpd/conf.d/` on the host configures the WHP panel's own Apache. Editing here changes how the panel serves; it doesn't change how customer sites serve.
|
||||||
|
- **Control-panel PHP-FPM:** `/etc/php-fpm.d/` on the host configures the panel's PHP. Same scope.
|
||||||
|
- **MySQL:** the MySQL instance runs as a container. Files under `/etc/my.cnf.d/` on the host are surfaced to customer database connections — they're effectively client-facing settings, not host settings.
|
||||||
|
- **HAProxy:** runs as a container with its own volume. Reload via the **Network & SSL** tab; don't hand-edit files in the container.
|
||||||
|
|
||||||
|
### Customer-side container customisation
|
||||||
|
|
||||||
|
If a customer needs a non-standard runtime, library, or service inside their site's container — that's done by **building a custom Docker image** and adding it as a container type option in WHP, not by editing host-level config.
|
||||||
|
|
||||||
|
The pattern is documented in our cloud-container repos. See <a href="https://repo.anhonesthost.net/cloud-hosting-platform/">repo.anhonesthost.net/cloud-hosting-platform/</a> for the cloud-apache-container and cloud-node-container examples — they show the layout, build, and how to publish an image so it appears in the **Container Type** dropdown on the Sites page.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Cloud container images are a one-time setup per stack you want to support. Once published, every site using that container type benefits automatically — no per-site shell access required.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**A service won't restart.** Check `journalctl -u <service>` (for systemd-managed services) or `docker logs <container>` (for containerized ones). The most common cause is a syntax error in a config file you just edited.
|
||||||
|
|
||||||
|
**Edits to a generated vhost keep disappearing.** That file is generated. Put your customisation in a per-app drop-in under `/etc/httpd/conf.d/`, or open a ticket about adding a stable include hook.
|
||||||
|
|
||||||
|
**Mailserver API debug log too noisy.** Toggle **Enable Mailserver API Debug Logging** off on the **Mail** tab once you've finished diagnosing.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Coraza WAF rules](/whp/admin/coraza-waf/)
|
||||||
|
- [Users & delegated access](/whp/admin/user-management/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
title: AI Monitor, Issues & Ignore Rules
|
||||||
|
description: The three admin pages that drive the Site Monitoring add-on — AI Monitor dashboard, Issues, and Ignore Rules.
|
||||||
|
sidebar:
|
||||||
|
order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
[Site Monitoring](/whp/add-ons/monitoring/) is the customer-facing alerting add-on. The admin side exposes three pages — together they let you tune what gets monitored, what surfaces as a customer-visible issue, and what gets suppressed.
|
||||||
|
|
||||||
|
## Sign in as super admin
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## AI Monitor (admin dashboard)
|
||||||
|
|
||||||
|
Sidebar → **AI Monitor → Dashboard**. The operational heartbeat of the whole monitoring pipeline.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Panels:
|
||||||
|
|
||||||
|
- **AI Log Monitor Status** — overall on/off plus three sub-statuses:
|
||||||
|
- **Minute-cadence poll** — the cron that scans logs every minute.
|
||||||
|
- **Health API** — the internal API that exposes per-container health.
|
||||||
|
- **HAProxy stats** — the proxy stats feed used for error-rate tracking.
|
||||||
|
- **Stat tiles** — Last Run, Errors Tracked, Remediations, API Calls Today (with a rate-limit denominator).
|
||||||
|
- **Health Check Timeline (last 7d)** — every state transition (`cpu`, `swap`, `haproxy`, etc.) with its severity and an **AI Diagnosis** explanation.
|
||||||
|
|
||||||
|
Use this page to confirm the pipeline is healthy and to drill into recent state changes. The AI Diagnosis text gives a plain-language summary of *why* a transition happened — useful for context before you act.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
Sidebar → **AI Monitor → Issues**. The customer-visible findings, server-wide.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Four stat tiles at the top:
|
||||||
|
|
||||||
|
- **Critical** — open critical-severity issues.
|
||||||
|
- **Warning** — open warning-severity issues.
|
||||||
|
- **Auto-resolved (review)** — issues the monitor closed on its own that may still need a human glance.
|
||||||
|
- **Active suppressions** — issues currently muted by an Ignore Rule.
|
||||||
|
|
||||||
|
Filter row: Scope (All / specific user), Status (Open / Closed / All), Severity, Source, Signature prefix.
|
||||||
|
|
||||||
|
Bulk actions: **Mark Fixed**, **Ignore**, **Delete**.
|
||||||
|
|
||||||
|
Each row has its own quick-actions: **Fix** (close it) and **Ignore** (create an ignore rule from this row's match criteria).
|
||||||
|
|
||||||
|
## Ignore Rules
|
||||||
|
|
||||||
|
Sidebar → **AI Monitor → Ignore Rules**. Match criteria that prevent matching findings from becoming customer-visible issues.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Each rule has these fields:
|
||||||
|
|
||||||
|
- **Scope** — `user` (just one customer) or `global` (every customer).
|
||||||
|
- **Target** — the user or domain the rule applies to.
|
||||||
|
- **Match** — a comma-separated set of `field=value` predicates (e.g. `cat=degraded & title~"Beaver Builder cache files missing"`). Match fields combine with **AND** semantics — every predicate must match.
|
||||||
|
- **Reason** — a short note for future-you explaining why the rule exists.
|
||||||
|
- **Hits / Last hit** — how often the rule has matched, and when.
|
||||||
|
- **Enabled** — toggle without deleting.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Prefer **per-user** ignores over global. Global rules silence the finding for everyone on the server.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Common tasks
|
||||||
|
|
||||||
|
### Mute a known-noisy finding for one customer
|
||||||
|
|
||||||
|
1. Open **Issues**, find the row.
|
||||||
|
2. Click the row's **Ignore** action — that pre-fills an Ignore Rule with the matching criteria scoped to that user.
|
||||||
|
3. Add a **Reason** so future-you (or someone else on the team) understands why it exists.
|
||||||
|
4. Save. Future matching findings will be suppressed; the **Active suppressions** tile will tick up.
|
||||||
|
|
||||||
|
### Investigate an "Auto-resolved (review)" issue
|
||||||
|
|
||||||
|
These are issues where the underlying signal recovered before a human looked at them. Open the row to see the AI Diagnosis and the original detection. If the resolution looks legitimate, click **Mark Fixed**; if you're suspicious, leave it open and add a comment for context.
|
||||||
|
|
||||||
|
### Tune brute-force detection
|
||||||
|
|
||||||
|
Brute-force rules live in **Coraza Rules** (rule families CRS-913 / 921 / 942 / 949) rather than AI Monitor. AI Monitor surfaces the *effects* (error spikes) once a brute-force pattern fires, but the matching itself is in the WAF — see [Coraza WAF rules](/whp/admin/coraza-waf/).
|
||||||
|
|
||||||
|
## Routing alerts
|
||||||
|
|
||||||
|
Customer email alerts are sent via the SMTP relay configured on **Server Settings → Mail → Outbound Email (SMTP)**. Toggle **Enable Outbound Email** off there if you need to silence outbound notifications for a maintenance window.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Pipeline shows "stale" — Last Run more than a few minutes ago.** Check the cron's container on **Server Settings → Services → Docker Container Management**. The monitor poll is `whp-monitor-poll`.
|
||||||
|
|
||||||
|
**Issues appearing for a known noisy site.** Add an Ignore Rule scoped to that user.
|
||||||
|
|
||||||
|
**No customer alerts arriving.** Confirm Outbound Email is enabled and the SMTP relay is reachable from the server.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Site Monitoring add-on](/whp/add-ons/monitoring/) — the customer-facing side.
|
||||||
|
- [Coraza WAF rules](/whp/admin/coraza-waf/) — request-level firewall.
|
||||||
|
- [Server settings & services](/whp/admin/server-settings/) — SMTP relay config for outbound alerts.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
---
|
||||||
|
title: Users & delegated access
|
||||||
|
description: Create WHP users, set account types, change passwords, plus delegated user access and account suspensions.
|
||||||
|
sidebar:
|
||||||
|
order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import SuperAdmin from '~/content/partials/super-admin-callout.mdx';
|
||||||
|
import AdminSignIn from '~/content/partials/admin-signin.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<SuperAdmin />
|
||||||
|
|
||||||
|
Four admin pages collectively control who can sign in and what they can do on the server:
|
||||||
|
|
||||||
|
- **User Management** — create / change-password / delete WHP users.
|
||||||
|
- **User Resources** — set CPU / RAM / disk allowances per user.
|
||||||
|
- **Delegated Users** — list of contractor / sub-account grants on customer sites.
|
||||||
|
- **Account Suspensions** — suspended accounts.
|
||||||
|
|
||||||
|
## Sign in as super admin
|
||||||
|
|
||||||
|
<AdminSignIn />
|
||||||
|
|
||||||
|
## User Management
|
||||||
|
|
||||||
|
Sidebar → **User Management**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Create New User
|
||||||
|
|
||||||
|
Every user created here is a **customer account**, not a super admin. (Super admin is the `root` user on the server, and there's no UI to add another.)
|
||||||
|
|
||||||
|
Fields:
|
||||||
|
|
||||||
|
- **Username** — UNIX-safe username; also becomes the SFTP user and home-directory name (`/docker/users/<username>`).
|
||||||
|
- **Password** — strong password. The user can change it later from the panel.
|
||||||
|
- **Account Type** — pick the scope of features this customer should see:
|
||||||
|
- **Full Hosting** — sites, databases, domains, DNS, email. The default for normal customers.
|
||||||
|
- **Domain/DNS Only** — domains and DNS records only (no sites, databases, or email).
|
||||||
|
- **Mail/DNS Only** — email plus domains/DNS (no sites or databases).
|
||||||
|
|
||||||
|
Click **Create User** to provision the account. The user's home directory and SFTP credentials are set up immediately.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Pick the smallest account type that does the job. You can change it later from the **Actions** column in the user list.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
### Change User Password
|
||||||
|
|
||||||
|
Pick the user from the dropdown, enter a new password, click **Change Password**. The user is forced to sign in again on next visit; any in-flight panel sessions are still live until you also revoke them via **Active Sessions**.
|
||||||
|
|
||||||
|
### User Accounts table
|
||||||
|
|
||||||
|
Columns: **Username**, **UID** (UNIX uid), **Account Type**, **Home Directory**, **Actions** (Account-Type dropdown + Delete).
|
||||||
|
|
||||||
|
To change a user's account type, change the dropdown in the row and the change applies immediately. The **System** badge on a row marks an internal/system user (such as `root`, `whp`, `daemon`, `www-data`, `nobody`, the various `systemd-*` users, etc.).
|
||||||
|
|
||||||
|
**System users are protected.** The panel refuses to delete any user on the protected list — `delete_user` in `web-files/libs/usermgmt.php` checks `is_protected_user($username)` first and returns *"Cannot delete protected system user"* without touching the OS user. Password changes are blocked the same way, with one exception: `root`'s password can be changed (the rest of the protected list cannot).
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Deleting a non-system user removes their home directory and every site, database, and mailbox associated with them. There is no undo. Use **Account Suspensions** instead for temporary disablement.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## User Resources
|
||||||
|
|
||||||
|
Sidebar → **User Resources**. Configure CPU and memory allowances per user.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Each row shows current allocation vs. usage:
|
||||||
|
|
||||||
|
- **Max CPU** / **CPU Used** — in 0.25-core increments.
|
||||||
|
- **Max Mem** / **Mem Used** — in 256 MB increments.
|
||||||
|
- **Disk** / **Used** — total disk allocation and current consumption (with %).
|
||||||
|
- **Email** — mailbox slot count.
|
||||||
|
- **Mail MB** — total mail storage cap.
|
||||||
|
- **Arch.** — archival email slots used / total.
|
||||||
|
- **Cont.** — currently-running container count.
|
||||||
|
|
||||||
|
Use the **Actions** column to edit a user's caps. Changes apply on the next container restart for that user's sites.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
Memory-usage tracking isn't available in some Docker environments. The **Active sites** count reflects running containers.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Delegated Users
|
||||||
|
|
||||||
|
Sidebar → **Delegated Users**. Customers can use the Delegated Users page on their own account to grant a contractor scoped access to one of their sites. The admin view shows every active delegation across the server.
|
||||||
|
|
||||||
|
From the admin view you can:
|
||||||
|
|
||||||
|
- **Audit** — see who has cross-account access at a glance.
|
||||||
|
- **Edit** — modify scope, permissions, or expiry on any delegation for an independent customer. Useful when a customer asks support to fix a grant they set up incorrectly.
|
||||||
|
- **Revoke** — remove a delegation outright.
|
||||||
|
|
||||||
|
If a customer reports a delegation issue, this page is where you confirm the grant exists, inspect its scope, and adjust it on their behalf.
|
||||||
|
|
||||||
|
## Account Suspensions
|
||||||
|
|
||||||
|
Sidebar → **Account Suspensions**. The list of suspended customer accounts.
|
||||||
|
|
||||||
|
A suspension takes a customer's sites offline without deleting any data — the customer can be reinstated by removing the suspension. Useful for non-payment, terms-of-service issues, or maintenance hold.
|
||||||
|
|
||||||
|
The page lists who's suspended, when, by whom, and why. Reinstate from the action button on each row.
|
||||||
|
|
||||||
|
### How the suspension page is served
|
||||||
|
|
||||||
|
The "site suspended" page is served by **HAProxy** itself, not by a separate backend. When you suspend an account, WHP rewrites the HAProxy config to point that account's frontends at a 503 errorfile (`/usr/local/etc/haproxy/errors/503.http`) and reloads HAProxy.
|
||||||
|
|
||||||
|
**If a suspended site is still serving the real content** (or is throwing a network error instead of the suspended page), it almost always means HAProxy didn't pick up the config reload. Check, in order:
|
||||||
|
|
||||||
|
1. **HAProxy container is running.** **Server Settings → Services → Docker Container Management** → confirm `Haproxy manager` shows **Running**.
|
||||||
|
2. **HAProxy reload succeeded.** Either re-trigger from **Server Settings → Network & SSL → Reload Configuration**, or check the HAProxy container logs (`docker logs haproxy-manager`) for a reload error — usually a syntax error in the generated config from the suspension action.
|
||||||
|
3. **Errorfile is in place.** The 503 page lives at `/usr/local/etc/haproxy/errors/503.http` inside the container.
|
||||||
|
|
||||||
|
## Active Sessions
|
||||||
|
|
||||||
|
Sidebar → **Active Sessions**. Every active panel session across the server, with last activity time, IP, and a **Terminate** button. Use this when offboarding someone — kick them out of any active sessions *first*, then change their password or delete the user.
|
||||||
|
|
||||||
|
## When someone leaves your team
|
||||||
|
|
||||||
|
1. **Revoke active sessions** for that user via **Active Sessions**.
|
||||||
|
2. **Change their password** in **User Management** (locks them out even if they save their cookies).
|
||||||
|
3. **Downgrade or delete** the user in **User Management**.
|
||||||
|
4. Audit **Delegated Users** for any cross-account delegations that should also be revoked.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**"Cannot delete protected system user".** Expected — system users (root, daemon, www-data, mail, the `systemd-*` users, etc.) are blocked at the panel level to prevent breaking the host. If you really need to remove a user, confirm it's a customer account first.
|
||||||
|
|
||||||
|
**Created user can't sign in.** Confirm the password meets the strength rules. If the user is signing in for the first time, they may be hitting the password-change-on-first-login flow.
|
||||||
|
|
||||||
|
**Suspended customer's sites are still serving real traffic.** The suspension page is served by HAProxy — see "How the suspension page is served" above. Most often it's an HAProxy reload that didn't happen; check the container logs.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Server settings & services](/whp/admin/server-settings/) — including the suspension backend service.
|
||||||
|
- [AI Monitor, Issues & Ignore Rules](/whp/admin/site-monitoring/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -9,7 +9,11 @@ Containerized hosting means your site lives in its own lightweight, isolated env
|
|||||||
|
|
||||||
## An analogy
|
## An analogy
|
||||||
|
|
||||||
Think of a shared house with one kitchen versus an apartment building. **Shared hosting** is the house: lots of people share the same kitchen, and when one person leaves dishes in the sink, everyone notices. **Containerized hosting** gives you your own apartment, with your own kitchen, in the same building — you still benefit from the shared infrastructure (heat, security, plumbing), but day-to-day you're not stepping on anyone else's mess.
|
Think of an **apartment** versus a **townhome in a gated community**.
|
||||||
|
|
||||||
|
**Shared hosting** is the apartment. You have your own unit, but the walls are thin. When the neighbour upstairs hosts a party, you hear it. When someone two doors down cooks something pungent, you smell it. You're sharing the same air, the same plumbing risers, the same elevator — so what one tenant does ripples through everyone else's day.
|
||||||
|
|
||||||
|
**Containerized hosting** is the townhome in a gated community. You still benefit from shared community resources — the gates, the security, the community centre, the maintained grounds — but you have your own walls, your own kitchen, your own front door. Whatever the neighbouring townhome does inside their walls stays inside their walls. You only feel the upside of being in the community, not the noise.
|
||||||
|
|
||||||
## How it compares to shared hosting
|
## How it compares to shared hosting
|
||||||
|
|
||||||
@@ -18,23 +22,29 @@ Think of a shared house with one kitchen versus an apartment building. **Shared
|
|||||||
- **Isolation = security.** A vulnerability in another customer's site can't reach yours. There's a hard boundary between containers.
|
- **Isolation = security.** A vulnerability in another customer's site can't reach yours. There's a hard boundary between containers.
|
||||||
- **Add resources without moving.** Need more RAM next month? Upgrade in place — no migration, no downtime.
|
- **Add resources without moving.** Need more RAM next month? Upgrade in place — no migration, no downtime.
|
||||||
|
|
||||||
## How it compares to a VPS
|
## How it compares to a Virtual Dedicated Server (VDS)
|
||||||
|
|
||||||
- **No server admin required.** We patch the underlying OS, monitor the host, and back up the underlying disk. You focus on your site, not on `apt update`.
|
A [Virtual Dedicated Server](https://anhonesthost.com/vds) is our VPS-class product — you get a whole virtual machine with admin access, and the same WHP control panel layered on top with full server-wide controls. The differences from a containerized plan:
|
||||||
- **Right-sized resources.** A VPS gives you a whole virtual machine whether you need it or not. A container gives your site exactly the CPU, RAM, and disk you actually use — and you can resize whenever.
|
|
||||||
- **Cheaper to run small.** Containers share the host efficiently, so you don't pay for an idle VM sitting at 5% CPU all day.
|
- **No server admin required on a container plan.** We patch the underlying OS, monitor the host, and manage the services. On a VDS, you have admin access and can patch, configure, and manage server-wide settings yourself if you want to.
|
||||||
|
- **Right-sized resources on a container plan.** A VDS gives you a whole virtual machine with fixed CPU/RAM/storage. A container gives your site exactly the CPU, RAM, and disk you actually use — and you can resize whenever.
|
||||||
|
- **Cheaper to run small on a container plan.** Containers share the host efficiently, so you don't pay for an idle VM sitting at 5% CPU all day.
|
||||||
|
|
||||||
## What it doesn't change
|
## What it doesn't change
|
||||||
|
|
||||||
- **SFTP still works** within the limits of your plan, so your usual file-upload workflow is unaffected.
|
- **SFTP still works** within the limits of your plan, so your usual file-upload workflow is unaffected.
|
||||||
- **Your databases still live next to your site**, on the same host, with low-latency connections.
|
- **Your databases still live next to your site**, on the same host, with low-latency connections.
|
||||||
- **Your domain works the same way** — DNS, SSL, redirects all behave like any other web host.
|
- **Your domain works the same way** — DNS, SSL, redirects all behave like any other web host.
|
||||||
|
- **The WHP panel works the same way.** Whether you're on a containerized plan or a VDS, you sign in to the same WHP — VDS just unlocks the server-wide admin sections.
|
||||||
|
|
||||||
## When you might want a VPS instead
|
## When you might want a VDS instead
|
||||||
|
|
||||||
We'll be honest with you: if you need kernel-level tweaks, custom networking, non-standard runtimes we don't support, or root over SSH, a VPS may suit you better. Most sites don't need any of that — and the trade-off is that you're then responsible for your own OS upkeep.
|
A VDS suits you when you want full admin control of the server — kernel-level tweaks, custom services, your own systemd units, or you simply prefer to manage the OS yourself. You get a real virtual machine with root access, and WHP still gives you the friendly panel on top.
|
||||||
|
|
||||||
|
A VDS also opens up server-wide options that don't apply to shared containers, like opt-in [data-drive encryption](/whp/admin/data-drive-encryption/) for compliance or peace-of-mind scenarios.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- [Add-ons overview](/whp/add-ons/overview/)
|
- [Add-ons overview](/whp/add-ons/overview/)
|
||||||
- [Service hostnames](/whp/reference/service-hostnames/)
|
- [Service hostnames](/whp/reference/service-hostnames/)
|
||||||
|
- [Data-drive encryption (LUKS)](/whp/admin/data-drive-encryption/) — optional, new-server-only
|
||||||
|
|||||||
@@ -27,35 +27,42 @@ import Support from '~/content/partials/support-link.mdx';
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
|
|
||||||
1. From the sidebar, open **Domains → Add Domain**.
|
1. In the sidebar, click **Domains & DNS**. You'll see a searchable list of the domains on your account.
|
||||||

|

|
||||||
|
|
||||||
2. Enter your domain (for example, `example.com`). Don't include `http://` or `www.` — just the bare domain.
|
2. Click **Add Domain** in the top right. In the dialog, type your domain (for example, `example.com`) — just the bare domain, with no `http://` or `www.`
|
||||||
|

|
||||||
|
|
||||||
3. Choose the domain type:
|
3. Click **Add Domain** to confirm. The domain appears in the list with a green **Active** status.
|
||||||
- **Primary** — your account's main domain. Pick this if it's your first.
|
|
||||||
- **Add-on** — an extra domain alongside your primary.
|
|
||||||
|
|
||||||
4. Click **Add**. WHP confirms the domain and shows the nameserver values and an A record you can point at.
|
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
|
WHP creates a DNS zone with the standard set of records automatically for the new domain:
|
||||||
|
|
||||||
|
- **A record** for the apex domain → your server's IP
|
||||||
|
- **CNAME** for `www` → the apex
|
||||||
|
- **NS records** for the nameservers
|
||||||
|
- **MX record** → the mail server
|
||||||
|
- **TXT records** for SPF and DKIM
|
||||||
|
|
||||||
|
You can review and tweak any of these — click **Manage DNS** next to the domain to open the records editor. See [Manage DNS records](/whp/how-to/manage-dns-records/) for the full walkthrough.
|
||||||
|
|
||||||
## Point your DNS at us
|
## Point your DNS at us
|
||||||
|
|
||||||
There are two paths depending on where the domain is registered:
|
There are two paths depending on where the domain is registered:
|
||||||
|
|
||||||
**Registered with us:** nothing to do. We set DNS automatically when the domain was created. Skip ahead to verify.
|
**Registered with us.** Nothing to do — we set DNS automatically when the domain was added. Skip ahead to verify.
|
||||||
|
|
||||||
**Registered elsewhere:** at your registrar, do one of the following:
|
**Registered elsewhere.** At your registrar, do one of the following:
|
||||||
|
|
||||||
- Set the **nameservers** to `ns1.anhonesthost.com` and `ns2.anhonesthost.com` (recommended — gives us full DNS control, easier to support), **or**
|
- Set the **nameservers** to the values shown in **Manage DNS** (the **NS** records) — recommended, as it gives us full DNS control and is easier to support — **or**
|
||||||
- Keep your existing nameservers and add an **A record** pointing the domain to the IP address shown on the WHP confirmation screen.
|
- Keep your existing nameservers and add an **A record** pointing the domain to the IP shown on the **Dashboard** page under Server Information.
|
||||||
|
|
||||||
## Verify it worked
|
## Verify it worked
|
||||||
|
|
||||||
DNS changes can take up to 24 hours to propagate worldwide. To check status:
|
DNS changes can take up to 24 hours to propagate worldwide. To check:
|
||||||
|
|
||||||
- Run `dig example.com +short` from a terminal — once you see our IP in the result, you're live.
|
- Run `dig example.com +short` from a terminal — once you see our server's IP in the result, you're live.
|
||||||
- Or use a web tool like [whatsmydns.net](https://www.whatsmydns.net/) to see propagation across many regions.
|
- Or use a web tool like [whatsmydns.net](https://www.whatsmydns.net/) to see propagation across many regions.
|
||||||
|
|
||||||
Once DNS resolves, visiting your domain in a browser will reach WHP — though you'll see a default placeholder until you create a site.
|
Once DNS resolves, visiting your domain in a browser will reach WHP — though you'll see a default placeholder until you create a site.
|
||||||
@@ -64,12 +71,13 @@ Once DNS resolves, visiting your domain in a browser will reach WHP — though y
|
|||||||
|
|
||||||
**"Pending DNS" for more than 24 hours.** Re-check the nameservers or A record at your registrar — typos and trailing dots cause silent failures. Clear your local DNS cache to rule out client-side caching.
|
**"Pending DNS" for more than 24 hours.** Re-check the nameservers or A record at your registrar — typos and trailing dots cause silent failures. Clear your local DNS cache to rule out client-side caching.
|
||||||
|
|
||||||
**Domain returns a generic "site not found" page.** DNS works but you haven't created a site yet. [Create one](/whp/how-to/create-a-site/) and point it at this domain.
|
**Domain returns a generic placeholder.** DNS works but you haven't created a site yet. [Create one](/whp/how-to/create-a-site/) and bind this domain to it.
|
||||||
|
|
||||||
**Wrong site loads.** Another site on your account is set as the default. In **Sites**, make sure the correct site is bound to this domain.
|
**Wrong site loads.** Another site on your account is bound to this domain. Open **Sites** in the sidebar and make sure the correct site is bound to it.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
- [Manage DNS records](/whp/how-to/manage-dns-records/)
|
||||||
- [Create a site](/whp/how-to/create-a-site/)
|
- [Create a site](/whp/how-to/create-a-site/)
|
||||||
|
|
||||||
## Still stuck?
|
## Still stuck?
|
||||||
|
|||||||
@@ -1,84 +1,99 @@
|
|||||||
---
|
---
|
||||||
title: Backups
|
title: Backups
|
||||||
description: Confirm WHP is backing up your site, restore individual files, and download archives.
|
description: Run on-demand and scheduled backups of your sites and databases, and confirm they're succeeding.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 4
|
order: 5
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Steps, Aside } from '@astrojs/starlight/components';
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
import SignIn from '~/content/partials/signing-in.mdx';
|
import SignIn from '~/content/partials/signing-in.mdx';
|
||||||
import Support from '~/content/partials/support-link.mdx';
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
WHP runs **automatic daily backups** of your site files and databases. You don't have to set anything up for this to happen — but it's worth knowing how to confirm a backup ran, restore something, and download an archive when you need to.
|
The **Backup Management** page lets you trigger an on-demand backup, add a scheduled backup, manage where backups are sent, and review history.
|
||||||
|
|
||||||
## What's backed up by default
|
## What's backed up
|
||||||
|
|
||||||
- **Site files** — everything under each domain folder.
|
- **Sites** — files for each site.
|
||||||
- **Databases** — all MySQL databases attached to your account.
|
- **Databases** — every MySQL and PostgreSQL database attached to your account.
|
||||||
- **Email** — mailboxes and their contents (depending on your plan).
|
|
||||||
|
|
||||||
Retention is shown on your plan page. The default tier keeps **7 daily backups**.
|
Default retention on built-in backup targets is **5 days, up to 10 backups**.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
Automatic backups only run once the server admin has configured at least one **default backup target**. On our managed shared plans this is set up for you. On a VDS, the server operator picks the target — until they do, the **Backup Targets** table on your Backups page will be empty and you won't see scheduled backups firing.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
## Sign in to WHP
|
## Sign in to WHP
|
||||||
|
|
||||||
<SignIn />
|
<SignIn />
|
||||||
|
|
||||||
## Configure your backup schedule
|
## Run an on-demand backup
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
|
|
||||||
1. From the sidebar, open **Backups → Settings**.
|
1. In the sidebar, click **Backups**.
|
||||||

|

|
||||||
|
|
||||||
2. Confirm the **schedule** (default: daily, overnight). Adjust if your plan permits.
|
2. Under **Create New Backup**, pick a **Backup Type** (Sites, Databases, or both) and a **Backup Target** from the dropdown.
|
||||||
|
|
||||||
3. (Optional) Enable **off-server backups** to a separate storage location — recommended for anything you can't afford to lose.
|
3. Click **Start Backup**. The run appears in **Backup History** below; status updates live.
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Verify a backup actually ran
|
## Schedule a backup
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
|
|
||||||
1. Open **Backups → History**.
|
1. Scroll down to **Scheduled Backups** and click **+ Add Schedule**.
|
||||||

|
|
||||||
|
|
||||||
2. Confirm the most recent entry is from **within the last 24 hours** and shows **Success**.
|
2. Pick the cadence (daily, weekly, etc.), the type (sites / databases / both), and the target.
|
||||||
|
|
||||||
3. Click the entry to see what was included — files, databases, email — and the total size.
|
3. Save. The schedule appears in the list and runs automatically.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Where backups are stored
|
||||||
|
|
||||||
|
The **Backup Targets** table shows the destinations available to your account. Built-in targets are S3-backed (for example, `WHP01 S3 Backups`) with retention and a maximum backup count. **Global** targets are managed by us; you can also add your own external target (for example, your own S3 bucket) with **+ Add Backup Target**.
|
||||||
|
|
||||||
|
## Verify backups are succeeding
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Open **Backups**.
|
||||||
|
|
||||||
|
2. Look at the **Total Backups** and **Total Size** tiles at the top. If Total Backups stays at 0 over time, no backups are running — open a [support ticket](https://secure.anhonesthost.com/submitticket.php).
|
||||||
|
|
||||||
|
3. Check **Backup History** for recent entries. Each should show **Success** within minutes of its scheduled time.
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
<Aside type="tip">
|
<Aside type="tip">
|
||||||
Add a calendar reminder to check this monthly. Backups that fail silently are the worst kind.
|
Add a calendar reminder to check the history monthly. Backups that fail silently are the worst kind.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
## Test a restore (recommended quarterly)
|
## Test a restore (recommended quarterly)
|
||||||
|
|
||||||
The best time to discover your backup isn't working is *not* when you actually need it. Test surgically:
|
Find the time to do this before you actually need it. Open a recent backup in **Backup History** → preview the contents → restore a single file (your site's `index.php` is a fine target) → confirm it appears. If the surgical restore works, the pipeline works.
|
||||||
|
|
||||||
1. In **History**, click a recent backup → **Restore preview**.
|
|
||||||
2. Pick a single file — your site's `index.php`, for example — and restore just that one file.
|
|
||||||
3. Confirm it appears correctly. If yes, the restore pipeline works.
|
|
||||||
|
|
||||||
Don't do a full restore unless you genuinely need to — it rewrites your live site.
|
Don't do a full restore unless you genuinely need to — it rewrites your live site.
|
||||||
|
|
||||||
## Download a backup
|
|
||||||
|
|
||||||
To grab a copy off-server:
|
|
||||||
|
|
||||||
1. From **History**, click a backup → **Download**.
|
|
||||||
2. WHP packages it as `.tar.gz` and offers a one-time download link that's valid for 24 hours.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Backup failed.** Click the failed row to see the error. Common causes:
|
**Backup failed.** Click the failed row to see the error. Common causes:
|
||||||
|
|
||||||
- Disk full or close to it — check **Overview → Resource usage** and consider a [resource upgrade](/whp/add-ons/resource-upgrades/).
|
- Disk full or close to it — check **Overview → Resource usage** and consider a [resource upgrade](/whp/add-ons/resource-upgrades/).
|
||||||
- A very large mailbox slowed the run — consider the [archival email add-on](/whp/add-ons/archival-email/) to relieve mailbox pressure.
|
- A very large mailbox slowed the run — consider the [archival email add-on](/whp/add-ons/archival-email/) to relieve mailbox pressure.
|
||||||
- Transient lock or maintenance window — let it retry overnight before opening a ticket.
|
- Transient lock or maintenance window — let it retry on the next schedule before opening a ticket.
|
||||||
|
|
||||||
**Restore "succeeded" but my file isn't there.** Check the **path** shown on the backup's detail page — your restore landed where the backup says it would, which may not match your current site layout if you've moved files around.
|
**"No targets available" when starting a backup.** Your account has no backup targets attached. Open a ticket; we'll get one wired up.
|
||||||
|
|
||||||
|
## What's *not* in customer backups
|
||||||
|
|
||||||
|
Customer backups cover your sites and databases — they don't cover the underlying server, the OS, or system-level config. Full-server backups are a separate concern:
|
||||||
|
|
||||||
|
- On our managed plans, **AnHonestHost handles full-server backups** for the host.
|
||||||
|
- On a Virtual Dedicated Server (VDS) we provide, **full-server snapshots are included** at the platform level.
|
||||||
|
- If WHP is running somewhere else (your own infrastructure), full-server backups are the server operator's responsibility — WHP itself doesn't provide a host-level backup tool.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
@@ -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 />
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: Create a site
|
title: Create a site
|
||||||
description: Spin up a PHP, Node, or static HTML site on a domain you've added to WHP.
|
description: Spin up a containerized site on a domain you've added to WHP.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 2
|
order: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Steps, Aside } from '@astrojs/starlight/components';
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
@@ -10,10 +10,12 @@ import SignIn from '~/content/partials/signing-in.mdx';
|
|||||||
import Hostnames from '~/content/partials/service-hostnames.mdx';
|
import Hostnames from '~/content/partials/service-hostnames.mdx';
|
||||||
import Support from '~/content/partials/support-link.mdx';
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Every site on WHP runs in one or more containers. The Sites page lets you pick a container type (PHP, Node, static HTML, or one of the other options on the dropdown), bind one or more domains, and set how much CPU and RAM each container gets.
|
||||||
|
|
||||||
## Before you start
|
## Before you start
|
||||||
|
|
||||||
- A **domain already added** to your account ([Add a domain](/whp/how-to/add-a-domain/) covers that).
|
- A **domain already added** to your account ([Add a domain](/whp/how-to/add-a-domain/) covers that).
|
||||||
- Decide what kind of site you're building: **PHP**, **Node**, or **static HTML**.
|
- Decide what kind of site you're building. Common container types: **PHP** (WordPress, Laravel, most CMSes), **Node** (custom apps), **Static HTML** (prebuilt bundles from Astro, Hugo, Eleventy, etc.).
|
||||||
- About 5 minutes.
|
- About 5 minutes.
|
||||||
|
|
||||||
## Sign in to WHP
|
## Sign in to WHP
|
||||||
@@ -24,33 +26,33 @@ import Support from '~/content/partials/support-link.mdx';
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
|
|
||||||
1. From the sidebar, open **Sites → Add Site**.
|
1. In the sidebar, click **Sites**.
|
||||||

|

|
||||||
|
|
||||||
2. Pick the **site type**:
|
2. Fill in the **Create New Site** form on the right:
|
||||||
- **PHP** — best for WordPress, Joomla, Laravel, and most off-the-shelf CMSes.
|
- **Site Name** — a friendly label for you to recognise the site later.
|
||||||
- **Node** — for custom apps you build and deploy (Express, Next.js in standalone mode, Fastify, etc.).
|
- **Primary Domain or Subdomain** — pick from the dropdown of domains you've already added.
|
||||||
- **Static HTML** — for prebuilt site bundles from tools like Astro, Hugo, Eleventy, or Jekyll. No runtime; just files.
|
- **Container Type** — PHP, Node, static HTML, etc.
|
||||||
|
- **Number of Containers** — `1` is the right answer for most sites. Bump this up (1–10) if you need to scale a busy site horizontally; WHP load-balances traffic across the replicas.
|
||||||
|
- **Additional Domains/Subdomains (Optional)** — bind extra domains to the same site if you want them to serve the same content.
|
||||||
|
- **CPU per Container (cores)** — defaults to `0.25`. Raise this only if you need to.
|
||||||
|
- **Memory per Container (MB)** — defaults to `256`. Raise this if your app needs more RAM.
|
||||||
|
|
||||||
3. Pick the **domain**. The dropdown lists domains you've already added.
|
3. Scroll down to **SSL/HAProxy Configuration**. Before you enable HTTPS, make sure your domain's DNS already points to our server's IP — SSL certificates can't be issued for domains that aren't pointed correctly. Then check **Enable HTTPS** to have us request a Let's Encrypt certificate.
|
||||||
|
|
||||||
4. (PHP only) Pick the **PHP version**.
|
4. Click **Create Site**. WHP provisions the container(s) — usually under a minute. The new site shows up in the **Manage Sites** list on the left of the page.
|
||||||
|
|
||||||
5. (Node only) Pick the **Node version** and the **start command** (typically `npm run start`).
|
|
||||||
|
|
||||||
6. Click **Create site**. WHP provisions the container — usually under a minute. You'll see the new site in the **Sites** list when it's ready.
|
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
## Where your files go
|
## Where your files go
|
||||||
|
|
||||||
When you connect via SFTP, your account home shows one folder per domain. Inside each domain folder, the layout depends on the site type:
|
When you connect via SFTP, your account home (`/docker/users/<your-user>/`) shows one folder per domain. Inside each domain folder, the layout depends on the container type:
|
||||||
|
|
||||||
| Site type | Layout | Docroot is... |
|
| Container type | Layout inside the domain folder | Docroot is... |
|
||||||
| ------------ | ----------------------------------------------------- | ------------------------------------------------------ |
|
| -------------- | ------------------------------------------ | -------------------------------------------------------- |
|
||||||
| PHP | `public_html/`, `logs/`, `crontab` | `public_html/` |
|
| PHP | `public_html/`, `logs/`, `crontab` | `public_html/` |
|
||||||
| Node | `app/`, `logs/` | Whatever your start command serves; we run it from `app/` |
|
| Node | `app/`, `logs/` | Whatever your start command serves; we run it from `app/`|
|
||||||
| Static HTML | files directly (e.g., `index.html`, `assets/`) | The domain folder itself |
|
| Static HTML | files directly (e.g., `index.html`, `assets/`) | The domain folder itself |
|
||||||
|
|
||||||
Upload your files to the right place and the site picks them up immediately.
|
Upload your files to the right place and the site picks them up immediately.
|
||||||
|
|
||||||
@@ -63,16 +65,18 @@ Upload your files to the right place and the site picks them up immediately.
|
|||||||
Open your domain in a browser. You should see your site, or the default "no content yet" placeholder if you haven't uploaded files. For Node sites, give it 15–30 seconds after creation for the process to start.
|
Open your domain in a browser. You should see your site, or the default "no content yet" placeholder if you haven't uploaded files. For Node sites, give it 15–30 seconds after creation for the process to start.
|
||||||
|
|
||||||
<Aside type="tip">
|
<Aside type="tip">
|
||||||
Static HTML sites are the fastest path if your toolchain produces a bundle (Astro, Hugo, Eleventy, etc.) — you skip the runtime entirely.
|
Static HTML is the fastest path if your toolchain produces a bundle (Astro, Hugo, Eleventy, etc.) — you skip the runtime entirely.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**You see a default "no content" page.** Upload your files via SFTP — for PHP into `public_html/`, for Node into `app/`, for static directly into the domain folder.
|
**You see a default "no content" page.** Upload your files via SFTP — for PHP into `public_html/`, for Node into `app/`, for static directly into the domain folder.
|
||||||
|
|
||||||
**Node site returns 502.** Check that your start command runs successfully locally, and that it binds to the port shown on the site's WHP detail page (we set it via an env var; see the site's environment in WHP).
|
**Node site returns 502.** Check that your start command runs successfully locally, and that it binds to the port WHP exposes via env var to your container.
|
||||||
|
|
||||||
**PHP site shows a blank page or 500.** Check your site's error log (sidebar → Logs → pick the site). Common cause: a syntax error in `index.php` or a missing PHP extension — request extensions via a [support ticket](https://secure.anhonesthost.com/submitticket.php) if needed.
|
**PHP site shows a blank page or 500.** Check **Sites → your site → Logs** (or the `logs/` folder in SFTP). Common cause: a syntax error in `index.php` or a missing PHP extension — open a [support ticket](https://secure.anhonesthost.com/submitticket.php) if you need a non-default extension enabled.
|
||||||
|
|
||||||
|
**SSL won't issue.** The "Important: ensure your domain is pointed to our server" warning means DNS hasn't propagated yet, or it's pointed somewhere else. Re-check with `dig` and retry.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
title: Create an email account
|
title: Create an email account
|
||||||
description: Add a mailbox on one of your domains and connect your email client.
|
description: Add a mailbox on one of your domains and connect your email client.
|
||||||
sidebar:
|
sidebar:
|
||||||
order: 3
|
order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
import { Steps, Aside } from '@astrojs/starlight/components';
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
@@ -11,7 +11,7 @@ import Support from '~/content/partials/support-link.mdx';
|
|||||||
|
|
||||||
## Before you start
|
## Before you start
|
||||||
|
|
||||||
- A **domain added** to your account.
|
- A **domain added** to your account ([Add a domain](/whp/how-to/add-a-domain/) covers that).
|
||||||
- Decide what local part you want — the bit before the `@`. For example, `jane` to get `jane@example.com`.
|
- Decide what local part you want — the bit before the `@`. For example, `jane` to get `jane@example.com`.
|
||||||
- About 5 minutes.
|
- About 5 minutes.
|
||||||
|
|
||||||
@@ -23,62 +23,94 @@ import Support from '~/content/partials/support-link.mdx';
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
|
|
||||||
1. From the sidebar, open **Email → Email Accounts → Add Account**.
|
1. In the sidebar, click **Email**. The page is organized into tabs — **Email Accounts**, **Forwarders**, and **Email Domains (DNS)** — and opens on **Email Accounts**. The buttons along the top (**Webmail**, **Admin Panel**, **Setup Instructions**) open the mail server's web tools in a new tab.
|
||||||

|

|
||||||
|
|
||||||
2. Pick the **domain** for this mailbox.
|
2. On the **Email Accounts** tab, click **Create Email Account** to open the new-account form. You'll be asked for the domain, the local part, a password, and an optional mailbox size cap.
|
||||||
|
|
||||||
3. Enter the **local part** (the bit before `@`).
|
3. Set a **strong password** — at least 12 characters with a mix of upper case, lower case, numbers, and symbols. Email accounts are common attack targets.
|
||||||
|
|
||||||
4. Set a **strong password** — at least 12 characters with a mix of upper case, lower case, numbers, and symbols. Email accounts are common attack targets.
|
4. Click **Create Account**. The new account appears in the **Email Accounts** list.
|
||||||
|
|
||||||
5. Choose a **mailbox size cap**. The default is usually fine; you can raise it later or via an [email upgrade add-on](/whp/add-ons/email-upgrades/).
|
|
||||||
|
|
||||||
6. Click **Create**.
|
|
||||||
|
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
|
## Auto-configure your mail app
|
||||||
|
|
||||||
|
Most modern mail apps — Outlook, Apple Mail, Thunderbird, and the iOS and Android mail apps — can set themselves up from your domain's DNS. You enter your **full email address** and **password**, and the app finds the right servers, ports, and security settings on its own.
|
||||||
|
|
||||||
|
**If your domain uses our nameservers, this already works** — we add the necessary records automatically when you add the domain, so there's nothing for you to do.
|
||||||
|
|
||||||
|
### If your DNS is hosted elsewhere
|
||||||
|
|
||||||
|
If your domain's DNS lives at another provider (Cloudflare, GoDaddy, Namecheap, and so on), your mail app can't auto-configure until you add a few records there yourself. The Email page builds the exact records for you: open the **Email Domains (DNS)** tab, find **Autodiscovery Records (DNS)**, pick the domain, and copy them in.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Add these records to the domain's zone at your DNS provider. The names are **relative to your domain** — most providers fill in the rest automatically, so `autoconfig` becomes `autoconfig.example.com`.
|
||||||
|
|
||||||
|
| Type | Name | Priority | Weight | Port | Value |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| CNAME | `autoconfig` | — | — | — | your mail server |
|
||||||
|
| SRV | `_autodiscover._tcp` | 0 | 0 | 443 | your mail server |
|
||||||
|
| SRV | `_imaps._tcp` | 0 | 1 | 993 | your mail server |
|
||||||
|
| SRV | `_submission._tcp` | 0 | 1 | 587 | your mail server |
|
||||||
|
| SRV | `_pop3s._tcp` | 0 | 1 | 995 | your mail server |
|
||||||
|
|
||||||
|
Use the **mail server hostname shown in the Autodiscovery Records (DNS) section** as the value — it's the same host your **MX** record points at. The `_pop3s` record is only needed if you read mail over POP3 instead of IMAP. Click **Copy records** to grab them all at once in zone-file format.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
If your provider has a proxy toggle (such as Cloudflare's orange cloud), keep these records **DNS only** — proxying them stops mail clients from reading them.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
These records only help apps *find* the server. You still create the mailbox in WHP first, and your domain's **MX** record must point at our mail server for mail to be delivered.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
## Set up your email client
|
## Set up your email client
|
||||||
|
|
||||||
Use these settings in Outlook, Apple Mail, Thunderbird, or any other client:
|
Most apps configure themselves from the records above once you enter your address and password. If yours doesn't support that — or you'd rather enter the settings by hand — the exact IMAP, POP3, and SMTP hostnames are listed on the Email page: click **Setup Instructions** at the top of the page for a step-by-step that includes the right hostnames, ports, and security settings for your server.
|
||||||
|
|
||||||
|
The typical settings look like this; substitute the hostname shown in the Setup Instructions:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
IMAP (incoming)
|
IMAP (incoming)
|
||||||
Host: mail.<yourdomain>
|
Host: <see Setup Instructions>
|
||||||
Port: 993
|
Port: 993
|
||||||
Security: SSL/TLS
|
Security: SSL/TLS
|
||||||
Username: full email address (e.g., jane@example.com)
|
Username: full email address (e.g., jane@example.com)
|
||||||
Password: the one you set above
|
Password: the one you set above
|
||||||
|
|
||||||
SMTP (outgoing)
|
SMTP (outgoing)
|
||||||
Host: mail.<yourdomain>
|
Host: <see Setup Instructions>
|
||||||
Port: 587
|
Port: 465
|
||||||
Security: STARTTLS
|
Security: SSL/TLS
|
||||||
Username: full email address
|
Username: full email address
|
||||||
Password: same as IMAP
|
Password: same as IMAP
|
||||||
```
|
```
|
||||||
|
|
||||||
For per-client walkthroughs (Outlook, Apple Mail, etc.), see the Email clients section — coming soon.
|
For outgoing mail, **port 465 with SSL/TLS** is the standard. If your client prefers STARTTLS, **port 587** is the alternate submission port. (Don't use port 25 from a mail client — it's for server-to-server delivery and most networks block it.)
|
||||||
|
|
||||||
|
For per-client walkthroughs (Outlook, Apple Mail, Thunderbird, etc.), see the Email clients section — coming soon.
|
||||||
|
|
||||||
## Webmail
|
## Webmail
|
||||||
|
|
||||||
You can also access the mailbox from a browser at `https://webmail.<yourdomain>` once DNS for the mail subdomain has propagated.
|
Click **Webmail** at the top of the Email page to sign in to webmail in a new tab.
|
||||||
|
|
||||||
## Verify it worked
|
## Verify it worked
|
||||||
|
|
||||||
Send yourself a test message from another account (your personal Gmail, for example). It should arrive within a minute or two and be retrievable from both your client and webmail.
|
Send yourself a test message from another account (your personal Gmail, for example). It should arrive within a minute or two and be retrievable from both your client and webmail.
|
||||||
|
|
||||||
<Aside type="caution">
|
<Aside type="caution">
|
||||||
**SPF and DKIM records matter.** Without them, your outgoing mail will get flagged or rejected by other providers. If your domain is registered with us, we add these records automatically. If it's registered elsewhere, copy the SPF and DKIM records shown on the email account page into your registrar's DNS.
|
**SPF and DKIM records matter.** Without them, your outgoing mail will get flagged or rejected by other providers. We add an SPF record automatically when you add a domain. DKIM records are listed in the **DKIM Management** section on the **Email Domains (DNS)** tab — make sure they're present at your registrar if the domain isn't using our nameservers.
|
||||||
</Aside>
|
</Aside>
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Webmail isn't reachable.** DNS for the `mail.<yourdomain>` subdomain may still be propagating — wait an hour and try again.
|
**Webmail isn't reachable.** Webmail is hosted at our address — the **Webmail** button on the Email page opens it directly — so it doesn't depend on your domain or its DNS. If it doesn't load, it's almost always a temporary connection issue: try again in a few minutes or from another network, and open a support ticket if it persists.
|
||||||
|
|
||||||
**Outgoing mail is bouncing or going to spam.** Check the SPF and DKIM records at your registrar. The email account page in WHP shows the exact records you should have.
|
**Outgoing mail is bouncing or going to spam.** Check the SPF and DKIM records. The **DKIM Management** section on the **Email Domains (DNS)** tab shows whether DKIM is configured for each of your domains.
|
||||||
|
|
||||||
**Client can connect on IMAP but not SMTP.** Some ISPs and corporate networks block outgoing port 587. Try sending from a different network to confirm; if the issue is your network, your ISP is the place to ask.
|
**Client can connect on IMAP but not SMTP.** Some ISPs and corporate networks block outgoing mail ports. If sending fails on port 465, try the alternate submission port **587** (STARTTLS); if both fail, test from a different network to confirm it's your network, and if so, your ISP is the place to ask.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
title: Manage DNS records
|
||||||
|
description: View, add, edit, and delete DNS records for your domains using the Domains & DNS records editor in WHP.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import SignIn from '~/content/partials/signing-in.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
When we host your DNS, WHP gives you a full records editor — add, edit, or remove **A**, **CNAME**, **MX**, **TXT**, and other records yourself, no support ticket needed.
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
- A domain already added to your account. If you haven't done that yet, [add a domain](/whp/how-to/add-a-domain/) first.
|
||||||
|
- We must be running DNS for the domain (its nameservers point at us). If your DNS lives at another provider, make these changes there instead.
|
||||||
|
- A couple of minutes. Record changes apply quickly on our side, but can take up to 24 hours to propagate worldwide.
|
||||||
|
|
||||||
|
## Sign in to WHP
|
||||||
|
|
||||||
|
<SignIn />
|
||||||
|
|
||||||
|
## Open the records editor
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. In the sidebar, click **Domains & DNS**.
|
||||||
|

|
||||||
|
|
||||||
|
2. Find your domain in the list and click **Manage DNS**.
|
||||||
|

|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
Each domain starts with a standard zone created automatically when the domain was added:
|
||||||
|
|
||||||
|
| Type | What it's for |
|
||||||
|
| --- | --- |
|
||||||
|
| **A** | Points the apex domain at your server's IP. |
|
||||||
|
| **CNAME** | Aliases like `www` and `autoconfig` to the right host. |
|
||||||
|
| **MX** | Routes mail for the domain to our mail server. |
|
||||||
|
| **TXT** | SPF and DKIM records that help your mail pass authentication. |
|
||||||
|
| **NS** | The nameservers that are authoritative for the domain. |
|
||||||
|
| **SRV** | Service records such as mail autodiscovery. |
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Editing **MX**, **NS**, or the SPF/DKIM **TXT** records can break email delivery or hand DNS control away from us. Only change these if you know exactly what you're doing.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Mail autodiscovery records
|
||||||
|
|
||||||
|
When we host your DNS, the records that let mail apps configure themselves — an `autoconfig` CNAME plus a set of `_autodiscover` / `_imaps` / `_submission` / `_pop3s` **SRV** records — are already in your zone. You don't need to add them.
|
||||||
|
|
||||||
|
If your DNS is at another provider, add them there by hand. WHP builds the exact records for each domain on the **Email** page — see [Auto-configure your mail app](/whp/how-to/create-an-email-account/#auto-configure-your-mail-app).
|
||||||
|
|
||||||
|
## Add a record
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Click **Add Record**. A new, editable row appears at the top of the table.
|
||||||
|

|
||||||
|
|
||||||
|
2. Fill in the row:
|
||||||
|
- **Type** — choose the record type (A, AAAA, CNAME, MX, TXT, NS, PTR, SRV, CAA, SSHFP, or TLSA).
|
||||||
|
- **Name** — the host the record applies to. Use `@` for the domain itself, or a subdomain like `blog` for `blog.example.com`.
|
||||||
|
- **Content** — the value: an IP for an A record, a hostname for a CNAME, and so on.
|
||||||
|
- **Prio** — only used by record types that need a priority (like MX). Leave it blank otherwise.
|
||||||
|
- **TTL** — how long resolvers may cache the record, in seconds. The default of `300` (5 minutes) is fine for most records.
|
||||||
|
|
||||||
|
3. Click **Save**. The record joins the list immediately. (Click **Cancel** to discard the row without saving.)
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Edit or delete a record
|
||||||
|
|
||||||
|
In the **Actions** column on the right of each row:
|
||||||
|
|
||||||
|
- The **pencil** icon opens the record for editing in place. Change any field, then save.
|
||||||
|
- The **trash** icon deletes the record. Deletions take effect right away, so double-check before you remove anything.
|
||||||
|
|
||||||
|
## Find a record quickly
|
||||||
|
|
||||||
|
If a domain has a lot of records, use the **All types** dropdown above the table to filter by a single record type — for example, show only **MX** records while you sort out mail.
|
||||||
|
|
||||||
|
## Work with several records at once
|
||||||
|
|
||||||
|
Tick the checkboxes on the left of one or more rows to reveal the bulk-action bar:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **Change TTL** — set the same TTL on every selected record.
|
||||||
|
- **Enable/Disable** — toggle records on or off without deleting them (handy for temporarily parking a record).
|
||||||
|
- **Delete** — remove all selected records at once.
|
||||||
|
- **Clear** — clear your selection (this does *not* delete anything).
|
||||||
|
|
||||||
|
## Verify it worked
|
||||||
|
|
||||||
|
DNS changes apply on our side within moments, but resolvers elsewhere may keep serving the old answer until the record's TTL expires (up to 24 hours for unfamiliar records).
|
||||||
|
|
||||||
|
- Run `dig example.com +short` (or `dig blog.example.com A +short`) from a terminal and confirm you see the value you just set.
|
||||||
|
- Or use a web tool like [whatsmydns.net](https://www.whatsmydns.net/) to watch propagation across regions.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**My change isn't showing up yet.** Resolvers cache records for the length of their TTL. Wait for the TTL to pass, then clear your local DNS cache and check again with `dig`.
|
||||||
|
|
||||||
|
**There's no Manage DNS button for my domain.** We're not running DNS for it — its nameservers point somewhere else. Make the change at your current DNS provider, or [point the domain's nameservers at us](/whp/how-to/add-a-domain/#point-your-dns-at-us) first.
|
||||||
|
|
||||||
|
**Email stopped working after I edited records.** Restore the original **MX** and SPF/DKIM **TXT** records. If you're not sure what they should be, open a support ticket (see below) and we'll put them back.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Add a domain](/whp/how-to/add-a-domain/)
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
title: Switching your site's backend
|
||||||
|
description: Change the container type running a site — standard PHP/FPM, the premium LiteSpeed/OpenLiteSpeed tier, or Node.js.
|
||||||
|
sidebar:
|
||||||
|
order: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import SignIn from '~/content/partials/signing-in.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Every WHP site runs inside a container. The **container type** determines the engine that serves your site: PHP, LiteSpeed, or Node.js. Most sites use a standard PHP or PHP-FPM container, which handles the vast majority of WordPress and PHP workloads well. If you've enabled the **Optimized Webserver** add-on, you also have access to LiteSpeed/OpenLiteSpeed (OLS) container types — a premium engine known for its built-in full-page cache (LSCache) and lower memory usage under traffic. If you're deploying your own application code instead of PHP, Node.js container types are also available — see [Node.js](#nodejs) below.
|
||||||
|
|
||||||
|
Switching backends is a one-step change in the Sites editor, but it does briefly restart your container, so plan for a few seconds of downtime.
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
- You need **admin access** to your WHP account.
|
||||||
|
- If you want to switch **onto** a LiteSpeed backend, your account must have the [Optimized Webserver add-on](/whp/add-ons/optimized-webserver/) enabled. If it isn't, those container types won't appear in the dropdown.
|
||||||
|
- About 2 minutes, plus a brief site restart.
|
||||||
|
|
||||||
|
## Sign in to WHP
|
||||||
|
|
||||||
|
<SignIn />
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. In the sidebar, click **Sites**.
|
||||||
|
|
||||||
|
2. Find the site you want to change and click **Edit** (the pencil icon next to it).
|
||||||
|
|
||||||
|
3. In the **Edit Site** form, locate the **Container Type** (or **PHP Version / Backend**) dropdown.
|
||||||
|
|
||||||
|
4. Select the backend you want:
|
||||||
|
- **PHP** or **PHP-FPM** options — standard shared webserver tier, suitable for most WordPress and PHP sites.
|
||||||
|
- **LiteSpeed PHP** options (e.g. *LiteSpeed PHP 8.x*) — premium OLS tier. These only appear if the Optimized Webserver add-on is active on your account.
|
||||||
|
- **Node18**, **Node20**, or **Node22** — for sites running your own Node.js application code instead of PHP. See [Node.js](#nodejs) below before switching to one of these.
|
||||||
|
|
||||||
|
5. Click **Save**. WHP recreates the container with the new engine. Expect a brief moment of downtime (typically a few seconds) while the container restarts.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
After switching backends, any server-side page cache starts cold. For LiteSpeed sites this means LSCache has nothing cached yet — your first few page loads will be slightly slower while the cache warms up. This is normal and resolves on its own within minutes.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Switching onto the premium LiteSpeed backend
|
||||||
|
|
||||||
|
The LiteSpeed/OpenLiteSpeed container types are part of the **Optimized Webserver** add-on. If you open the Container Type dropdown and don't see any LiteSpeed options, the add-on isn't enabled on your account yet.
|
||||||
|
|
||||||
|
To enable it:
|
||||||
|
|
||||||
|
1. Visit the [Optimized Webserver add-on page](/whp/add-ons/optimized-webserver/) for details and pricing.
|
||||||
|
2. Enable it from your [client portal](https://secure.anhonesthost.com/clientarea.php).
|
||||||
|
3. Once active, the LiteSpeed PHP options will appear in the Container Type dropdown when editing any site.
|
||||||
|
|
||||||
|
## Node.js
|
||||||
|
|
||||||
|
Node.js container types (**Node18**, **Node20**, **Node22**) run your own application code instead of PHP — the container starts your app the same way you would locally with `npm start`. They're for React/Next.js servers, Express APIs, and other Node apps, not for WordPress or PHP sites.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Switching an **existing PHP or WordPress site** to a Node container type won't make your PHP files run — Node containers don't execute PHP at all. Node backends are for sites where you're deploying your own Node.js application code. If you're starting a brand-new Node app rather than switching an existing site, [Create a site](/whp/how-to/create-a-site/) and choose **Node.js app** under "What are you building?" is the more direct path.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
A few things behave differently for Node compared to switching between PHP tiers:
|
||||||
|
|
||||||
|
- **No PHP-FPM / LiteSpeed choice.** Node containers bundle their own web server, so the shared-vs-standalone placement option that applies to PHP and LiteSpeed containers doesn't apply here — you won't see that toggle once a Node container type is selected.
|
||||||
|
- **Higher minimum memory.** Node containers need more headroom than PHP containers. Selecting a Node container type in the editor bumps **Memory per Container** up to at least 512 MB (versus 256 MB for standard PHP). If your account doesn't have that much unused memory in its resource allowance, WHP will tell you and block the change until you free up resources or [upgrade your plan](/whp/add-ons/resource-upgrades/).
|
||||||
|
- **Optional WebSocket / real-time support.** The Edit Site form has a **WebSocket / real-time support** checkbox alongside the Container Type field. Turn it on if your Node app holds connections open (Socket.IO, live chat, streaming) — it keeps connections alive for up to 6 hours instead of the standard 5-minute timeout. This is unrelated to the container type itself and works whether or not you just switched to Node.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once you've switched and saved, upload your app to the `app/` folder for that domain via SFTP — see [Create a site](/whp/how-to/create-a-site/#where-your-files-go) for the file layout and how WHP starts your app.
|
||||||
|
|
||||||
|
## Before cancelling the Optimized Webserver add-on
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
You must move **all** of your sites off the LiteSpeed backend before you can cancel or disable the Optimized Webserver add-on. The system will block the cancellation and show an error listing every site that still needs to be moved.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
To prepare for cancellation, switch each affected site back to a standard PHP or PHP-FPM container type:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. In the sidebar, click **Sites**.
|
||||||
|
|
||||||
|
2. For each site currently on a LiteSpeed container type, click **Edit**.
|
||||||
|
|
||||||
|
3. Change the **Container Type** to a standard **PHP** or **PHP-FPM** option.
|
||||||
|
|
||||||
|
4. Click **Save** and wait for the container to restart before moving to the next site.
|
||||||
|
|
||||||
|
5. Repeat until no sites are on a LiteSpeed backend.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
Once all sites are on standard backends, you can cancel or disable the add-on from your [client portal](https://secure.anhonesthost.com/clientarea.php) without errors.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**LiteSpeed options don't appear in the dropdown.** The Optimized Webserver add-on is not enabled on your account. See [Optimized Webserver](/whp/add-ons/optimized-webserver/) to add it.
|
||||||
|
|
||||||
|
**Site doesn't come back up after switching.** Wait 30 seconds and reload — the container may still be starting. If the site is still down after a minute, contact support.
|
||||||
|
|
||||||
|
**Cancelling the add-on shows an error listing sites.** Switch each listed site to a standard PHP/PHP-FPM backend first (see the steps above), then retry the cancellation.
|
||||||
|
|
||||||
|
**LiteSpeed cache not serving cached pages after the switch.** This is expected — the cache starts empty after every container recreation. It warms up automatically as visitors load pages.
|
||||||
|
|
||||||
|
**"Your account does not have enough resources left" when selecting a Node type.** Node containers need at least 512 MB of memory. Free up resources by removing or resizing another site, or [upgrade your plan](/whp/add-ons/resource-upgrades/).
|
||||||
|
|
||||||
|
**Site shows a 502 after switching to Node.** Give the container 15–30 seconds to start your app. If it's still down, confirm your app listens on the port WHP passes it via environment variable — see [Create a site](/whp/how-to/create-a-site/#troubleshooting).
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create a site](/whp/how-to/create-a-site/)
|
||||||
|
- [Optimized Webserver add-on](/whp/add-ons/optimized-webserver/)
|
||||||
|
- [Resource upgrades](/whp/add-ons/resource-upgrades/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
---
|
||||||
|
title: Node + Nginx locally
|
||||||
|
description: Run the cloud-node-container image on your laptop for Express, custom Node apps, and PM2-managed processes.
|
||||||
|
sidebar:
|
||||||
|
order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
[`cloud-node-container`](https://repo.anhonesthost.net/cloud-hosting-platform/cloud-node-container) (image: **`cnoc`**) is the same Node + Nginx image we use for hosted Node sites. It's based on **AlmaLinux 9**, ships with **Node 18, 20, and 22** side-by-side (default 20), uses **PM2** as the process manager, and fronts your app with **Nginx (SSL + HTTP→HTTPS redirect)**.
|
||||||
|
|
||||||
|
## What's included
|
||||||
|
|
||||||
|
- **Multiple Node versions** — 18, 20, 22, switchable via `NODEVER` env var or the `-a` flag.
|
||||||
|
- **PM2** for production-grade process management — automatic restart, log rotation.
|
||||||
|
- **Nginx reverse proxy** with SSL and HTTP→HTTPS redirect.
|
||||||
|
- **Memcached** for sessions, automatic backups, log rotation.
|
||||||
|
- **`/ping` health endpoint** baked into the proxy config.
|
||||||
|
|
||||||
|
## Image tags
|
||||||
|
|
||||||
|
Pull from `repo.anhonesthost.net/cloud-hosting-platform/cnoc:<tag>`. The most useful tags:
|
||||||
|
|
||||||
|
- `cnoc:latest` — the default (Node 20).
|
||||||
|
- Version-pinned tags follow the same pattern (`cnoc:node18`, `cnoc:node20`, `cnoc:node22`).
|
||||||
|
|
||||||
|
Check the repo for the current tag list.
|
||||||
|
|
||||||
|
## Quick start with `local-dev.sh`
|
||||||
|
|
||||||
|
The repo ships a `local-dev.sh` script that handles the Docker run, creates the bind-mount layout, generates helper scripts, and scaffolds a sample Express app if none exists.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Clone the repo and `cd` in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://repo.anhonesthost.net/cloud-hosting-platform/cloud-node-container.git
|
||||||
|
cd cloud-node-container
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Start a local instance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./local-dev.sh -n local-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The script will:
|
||||||
|
- Create user + log directories (`nginx/`, `nodejs/`).
|
||||||
|
- Scaffold a default Express app under `user/app/` if you haven't dropped your own in yet.
|
||||||
|
- Start the container with the right env vars.
|
||||||
|
- Generate helper scripts (`instance_start`, `instance_stop`, `instance_logs`, `instance_shell`).
|
||||||
|
|
||||||
|
4. Open `http://localhost/` in a browser — the sample Express app's response page should be there. Or hit `http://localhost/ping` to confirm the health endpoint.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
| Flag | Purpose | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `-n` | Container name (required) | — |
|
||||||
|
| `-p` | HTTP port | `80` |
|
||||||
|
| `-s` | HTTPS port | `443` |
|
||||||
|
| `-r` | Root path for files | current directory |
|
||||||
|
| `-a` | Node version (`18`, `20`, `22`) | `20` |
|
||||||
|
| `-v` | Verbose mode | off |
|
||||||
|
| `-h` | Show help | — |
|
||||||
|
|
||||||
|
Example — run a Node 22 instance on port 3000:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./local-dev.sh -n my-node22-app -a 22 -p 3000 -s 3443
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Docker usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p local-development/domain.tld
|
||||||
|
cd local-development/domain.tld
|
||||||
|
mkdir user
|
||||||
|
mkdir -p user/{app,logs/{nginx,nodejs}}
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
-p 80:80 -p 443:443 \
|
||||||
|
-e NODEVER=20 -e environment=DEV \
|
||||||
|
--mount type=bind,source="$(pwd)"/user,target=/home/$(whoami) \
|
||||||
|
-e uid=$(id -u) -e user=$(whoami) -e domain=localhost \
|
||||||
|
--name local-dev \
|
||||||
|
repo.anhonesthost.net/cloud-hosting-platform/cnoc:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get a shell inside the container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it local-dev /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful for running `pm2 ls`, `pm2 logs`, or `npm` commands against your bind-mounted app folder.
|
||||||
|
|
||||||
|
## Where your code goes
|
||||||
|
|
||||||
|
Your Node application lives at `user/app/` on your laptop. Inside the container that's `/home/<user>/app/`. The container is configured to run your app from there via PM2.
|
||||||
|
|
||||||
|
Minimum required files in `user/app/`:
|
||||||
|
|
||||||
|
- **`package.json`** — describes your app and dependencies; **must define a start command** (`"start": "node server.js"` in `scripts`).
|
||||||
|
- **A main JavaScript file** — typically `server.js` or `index.js`.
|
||||||
|
|
||||||
|
After dropping files in, restart the container so PM2 picks up the new app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker restart local-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Your app should bind to the **port shown by the container's env var** (usually `process.env.PORT`). Nginx forwards traffic to that port internally and proxies SSL.
|
||||||
|
|
||||||
|
## Logs
|
||||||
|
|
||||||
|
| Where | What |
|
||||||
|
|---|---|
|
||||||
|
| `user/logs/nginx/` | Nginx access + error logs |
|
||||||
|
| `user/logs/nodejs/` | PM2's stdout/stderr capture from your Node app |
|
||||||
|
|
||||||
|
Tail them from your laptop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tail -F user/logs/nodejs/*.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Or via the helper script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./instance_logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stop / start / clean up
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./instance_stop # stop the container
|
||||||
|
./instance_start # start it again
|
||||||
|
./instance_logs # view container logs
|
||||||
|
./instance_shell # exec into the container
|
||||||
|
```
|
||||||
|
|
||||||
|
To wipe everything:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker rm -f local-dev
|
||||||
|
rm -rf local-development/
|
||||||
|
```
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
The `/ping` endpoint is wired by Nginx independently of your app — useful for `docker healthcheck` config when you start adapting the manual command to your own setup.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
[`cloud-node-container` on Gitea](https://repo.anhonesthost.net/cloud-hosting-platform/cloud-node-container) — Dockerfile, Nginx + PM2 configs, entrypoint, and the `local-dev.sh` script.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [PHP + Apache locally](/whp/local-dev/php-apache/)
|
||||||
|
- [Create a site](/whp/how-to/create-a-site/) — the hosted side.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
title: Develop locally with our containers
|
||||||
|
description: Run the same Apache/PHP and Node containers locally that we use to host your site in production.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
The same container images we use to host your site in production are published as open-source images you can pull and run on your laptop. If you're comfortable with Docker, this gives you a development environment that matches production exactly — same OS, same Apache/Nginx config, same PHP or Node version.
|
||||||
|
|
||||||
|
## Why use our containers locally
|
||||||
|
|
||||||
|
- **Dev/prod parity.** Anything that runs in the container on your machine will run the same way in production. No "works on my Mac" surprises.
|
||||||
|
- **Quick start.** Each container ships a `local-dev.sh` script that handles the Docker incantation, volume layout, and helper commands.
|
||||||
|
- **No account required.** The images are public — pull and run.
|
||||||
|
|
||||||
|
<Aside type="note">
|
||||||
|
This page is for customers who are comfortable with Docker. You don't need to use these images to host with us — the WHP panel handles everything for the hosted version. This is for **local development** only.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Available containers
|
||||||
|
|
||||||
|
| Stack | Image | Use case |
|
||||||
|
|---|---|---|
|
||||||
|
| **PHP + Apache** | `repo.anhonesthost.net/cloud-hosting-platform/cac` | WordPress, Laravel, traditional PHP apps |
|
||||||
|
| **Node + Nginx** | `repo.anhonesthost.net/cloud-hosting-platform/cnoc` | Express, custom Node apps, PM2-managed processes |
|
||||||
|
|
||||||
|
Both images live in our public Gitea organisation at [repo.anhonesthost.net/cloud-hosting-platform/](https://repo.anhonesthost.net/cloud-hosting-platform/) — the source Dockerfiles, build configs, and helper scripts are all there too. Issues and PRs welcome.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Docker** (or a compatible runtime like Podman / OrbStack / Rancher Desktop). Docker Desktop on Mac/Windows or `docker.io` on Linux both work.
|
||||||
|
- A terminal where you can run `git`, `docker`, and basic shell scripts.
|
||||||
|
- Ports `80` and `443` free on your machine (the local-dev scripts bind these by default; both can be overridden with flags).
|
||||||
|
|
||||||
|
## Pick your stack
|
||||||
|
|
||||||
|
- **[PHP + Apache locally](/whp/local-dev/php-apache/)** — for WordPress and any PHP application.
|
||||||
|
- **[Node + Nginx locally](/whp/local-dev/node/)** — for Express, Next.js standalone, or any Node app.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create a site](/whp/how-to/create-a-site/) — how the same images run as hosted sites in WHP.
|
||||||
|
- [What is containerized hosting?](/whp/getting-started/what-is-containerized-hosting/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
---
|
||||||
|
title: PHP + Apache locally
|
||||||
|
description: Run the cloud-apache-container image on your laptop for WordPress and other PHP apps.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
[`cloud-apache-container`](https://repo.anhonesthost.net/cloud-hosting-platform/cloud-apache-container) (image: **`cac`**) is the same Apache + PHP image we use for hosted PHP sites. It's based on **AlmaLinux 9**, ships with **PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, and 8.5** side-by-side (default 8.3), and uses **Apache with mod_ssl**.
|
||||||
|
|
||||||
|
## Image tags
|
||||||
|
|
||||||
|
Pre-built tags are pushed on every change:
|
||||||
|
|
||||||
|
- `cac:latest` — the default (PHP 8.3).
|
||||||
|
- `cac:php74`, `cac:php80`, `cac:php81`, `cac:php82`, `cac:php83`, `cac:php84`, `cac:php85` — pin to a specific PHP version.
|
||||||
|
|
||||||
|
Pull from `repo.anhonesthost.net/cloud-hosting-platform/cac:<tag>`.
|
||||||
|
|
||||||
|
## Quick start with `local-dev.sh`
|
||||||
|
|
||||||
|
The repo ships a `local-dev.sh` script that handles the Docker incantation, creates the volume + log directories, generates helper scripts, and installs a fresh WordPress in the web root.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Clone the repo and `cd` in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://repo.anhonesthost.net/cloud-hosting-platform/cloud-apache-container.git
|
||||||
|
cd cloud-apache-container
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Start a local instance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./local-dev.sh -n local-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The script will:
|
||||||
|
- Create a user directory and log folders (`apache/`, `system/`).
|
||||||
|
- Create a Docker volume for MySQL.
|
||||||
|
- Start the container with the right env vars.
|
||||||
|
- Generate helper scripts in your root path (`instance_start`, `instance_stop`, `instance_logs`, `instance_db_info`).
|
||||||
|
- Install WordPress in your web root.
|
||||||
|
- Print the MySQL credentials it generated.
|
||||||
|
|
||||||
|
4. Open `http://localhost/` in a browser — WordPress's setup screen should be there.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
| Flag | Purpose | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `-n` | Container name (required) | — |
|
||||||
|
| `-p` | HTTP port | `80` |
|
||||||
|
| `-s` | HTTPS port | `443` |
|
||||||
|
| `-r` | Root path for files and DB | current directory |
|
||||||
|
| `-a` | PHP version (`74`, `80`, `81`, `82`, `83`, `84`, `85`) | `83` |
|
||||||
|
| `-v` | Verbose mode | off |
|
||||||
|
| `-h` | Show help | — |
|
||||||
|
|
||||||
|
Example — run a PHP 8.5 instance on port 8080:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./local-dev.sh -n my-php85-site -a 85 -p 8080 -s 8443
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Docker usage
|
||||||
|
|
||||||
|
If you'd rather skip the script and run it yourself:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p local-development/domain.tld
|
||||||
|
cd local-development/domain.tld
|
||||||
|
mkdir user
|
||||||
|
mkdir -p user/logs/{apache,system}
|
||||||
|
|
||||||
|
docker run -d -it \
|
||||||
|
-p 80:80 -p 443:443 \
|
||||||
|
-e PHPVER=84 -e environment=DEV \
|
||||||
|
--mount type=bind,source="$(pwd)"/user,target=/home/myuser \
|
||||||
|
-v"$name-mysql":/var/lib/mysql \
|
||||||
|
-e uid=30001 -e user=myuser -e domain=localhost \
|
||||||
|
--name local-dev \
|
||||||
|
repo.anhonesthost.net/cloud-hosting-platform/cac:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get a shell inside the container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it local-dev /bin/bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful for running `wp-cli`, tailing logs from inside, or checking PHP modules.
|
||||||
|
|
||||||
|
## Where things live
|
||||||
|
|
||||||
|
| Inside the container | On your laptop (via bind mount) |
|
||||||
|
|---|---|
|
||||||
|
| `/home/myuser/public_html/` — Apache docroot | `local-development/domain.tld/user/public_html/` |
|
||||||
|
| `/home/myuser/logs/apache/` — Apache logs | `local-development/domain.tld/user/logs/apache/` |
|
||||||
|
| `/var/lib/mysql/` — MySQL data | Named Docker volume (`<name>-mysql`) |
|
||||||
|
| `/home/myuser/mysql_creds` | Same path on the bind mount |
|
||||||
|
|
||||||
|
## WordPress
|
||||||
|
|
||||||
|
`local-dev.sh` installs WordPress automatically. If you started manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it local-dev bash
|
||||||
|
cat /home/myuser/mysql_creds # see the credentials
|
||||||
|
cd /home/myuser/public_html
|
||||||
|
wp core download
|
||||||
|
wp config create --dbname=... --dbuser=... --dbpass=... --dbhost=localhost
|
||||||
|
wp core install --url=http://localhost --title="Local Dev" --admin_user=admin --admin_email=you@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stop / start / clean up
|
||||||
|
|
||||||
|
The helper scripts the local-dev script writes are the easy path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./instance_stop # stop the container
|
||||||
|
./instance_start # start it again
|
||||||
|
./instance_logs # tail Apache logs
|
||||||
|
./instance_db_info # show MySQL credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
To wipe everything:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker rm -f local-dev
|
||||||
|
docker volume rm local-dev-mysql
|
||||||
|
rm -rf local-development/
|
||||||
|
```
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Want to test the exact PHP version of your hosted WHP site locally? Match the `-a` flag to your site's PHP version on the [Sites page](/whp/how-to/create-a-site/).
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
[`cloud-apache-container` on Gitea](https://repo.anhonesthost.net/cloud-hosting-platform/cloud-apache-container) — Dockerfile, entrypoint, build configs, and the `local-dev.sh` script.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Node + Nginx locally](/whp/local-dev/node/)
|
||||||
|
- [Create a site](/whp/how-to/create-a-site/) — the hosted side.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: Service hostnames
|
||||||
|
description: Quick reference for connecting to MySQL, PostgreSQL, and Valkey from inside your container.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
import Hostnames from '~/content/partials/service-hostnames.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
Use these hostnames from inside your WHP container — they resolve to the internal services your site can talk to. They don't work from your laptop; see the note below.
|
||||||
|
|
||||||
|
<Hostnames />
|
||||||
|
|
||||||
|
## Where do I get credentials?
|
||||||
|
|
||||||
|
- **MySQL** — sidebar → **MySQL Management**. Create users and databases there; the **phpMyAdmin** link below it opens an SSO'd phpMyAdmin in a new tab.
|
||||||
|
- **PostgreSQL** — sidebar → **PostgreSQL**. Same shape as MySQL Management; the **Adminer** link below it opens an SSO'd Adminer.
|
||||||
|
- **Valkey** — sidebar → **Valkey Cache**. Click **Enable Valkey**, pick a memory cap and mode, and you're done — no password needed because connections are network-isolated to your account.
|
||||||
|
|
||||||
|
## Mail servers
|
||||||
|
|
||||||
|
Mail server hostnames are not per-domain. Your server has one mail server hostname (for example `mail01.cloud-hosting.io`) that handles IMAP, POP3, and SMTP for every domain on that server. Find your exact mail server hostname on the **Dashboard** page in WHP under **Server Information** — that's also where you'll find your server's IP, primary nameservers, and your home directory.
|
||||||
|
|
||||||
|
For step-by-step email-client setup, the Email page has a **Setup Instructions** button that lays out the exact host, port, and security settings.
|
||||||
|
|
||||||
|
## Connecting from your laptop
|
||||||
|
|
||||||
|
Direct external connections to these internal services are not exposed. Use the panel's tools instead:
|
||||||
|
|
||||||
|
- **MySQL** — **phpMyAdmin** in the panel for ad-hoc queries.
|
||||||
|
- **PostgreSQL** — **Adminer** in the panel.
|
||||||
|
- **Valkey** — there's no first-party browser tool; use a Valkey/Redis client inside your site's **Terminal** (sidebar → **Terminal**).
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Create a site](/whp/how-to/create-a-site/)
|
||||||
|
- [Create an email account](/whp/how-to/create-an-email-account/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
title: Blocks & pages
|
||||||
|
description: The Site Builder block library, plus how to add pages and edit the shared header and footer.
|
||||||
|
sidebar:
|
||||||
|
order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Beta from '~/content/partials/beta-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<Beta />
|
||||||
|
|
||||||
|
## The block library
|
||||||
|
|
||||||
|
The **Blocks** panel on the left of the editor groups every available content block into five collapsible categories. Drag any block onto the canvas to add it.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Basic
|
||||||
|
|
||||||
|
Building-block primitives for any page:
|
||||||
|
|
||||||
|
- **Heading** — H1–H6, with the size set from the inline toolbar.
|
||||||
|
- **Text** — paragraph copy. Supports bold, italic, links, and lists.
|
||||||
|
- **Button** — call-to-action with label, link, and style.
|
||||||
|
- **Logo** — drop in your site logo from Assets.
|
||||||
|
- **Menu** — the site's main navigation (most commonly placed in the Header).
|
||||||
|
- **Footer** — quick footer wrapper (or use the dedicated Footer editor; see below).
|
||||||
|
- **Divider** — horizontal rule.
|
||||||
|
- **Spacer** — adjustable vertical whitespace.
|
||||||
|
- **Icon** — pick from a built-in icon set.
|
||||||
|
- **Star Rating** — five-star widget for reviews.
|
||||||
|
- **Social Links** — links to your social profiles with icons.
|
||||||
|
- **HTML** — drop in raw HTML when nothing else fits.
|
||||||
|
|
||||||
|
### Layout
|
||||||
|
|
||||||
|
Structural blocks that hold other blocks:
|
||||||
|
|
||||||
|
- **Section** — a full-width strip of the page.
|
||||||
|
- **Container** — a centered, max-width wrapper.
|
||||||
|
- **1 / 2 / 3 / 4 / 5 / 6 Columns** — pre-set column rows.
|
||||||
|
- **Sidebar Left / Right** — content area with a sidebar.
|
||||||
|
|
||||||
|
### Sections
|
||||||
|
|
||||||
|
Pre-composed page sections — Hero, Features, CTA, Accordion, Tabs, Pricing, Gallery, Countdown, and more. Drop one in and edit the placeholder content.
|
||||||
|
|
||||||
|
### Media
|
||||||
|
|
||||||
|
Image, video, audio, and gallery blocks. Uploaded files live in the **Assets** panel and can be re-used across pages.
|
||||||
|
|
||||||
|
### Forms
|
||||||
|
|
||||||
|
Contact form, newsletter signup, and similar input blocks.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Selecting any block on the canvas swaps the right-hand panel from **Styles** to **Block settings**, so you can edit its content, colour, padding, and link target without leaving the editor.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Editing blocks
|
||||||
|
|
||||||
|
Click a block on the canvas to select it. From there:
|
||||||
|
|
||||||
|
- **Inline toolbar** — quick text formatting (bold, italic, link, alignment).
|
||||||
|
- **Right panel** — block-specific settings (colour, padding, link, size, alt text, etc.).
|
||||||
|
- **Layers panel** (left) — see the block tree, drag to re-parent or re-order.
|
||||||
|
|
||||||
|
To delete a block, select it and press **Delete** on your keyboard, or use the trash control in the inline toolbar.
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
|
||||||
|
Most templates ship as multi-page sites. Manage pages from the **Pages** tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The **PAGES** list shows every page on the site, with its path (e.g. `Home /index`). The currently-edited page is highlighted.
|
||||||
|
|
||||||
|
### Add a page
|
||||||
|
|
||||||
|
1. In the Pages tab, click **+ Add Page**.
|
||||||
|
2. Give the page a name (used in navigation) and confirm the slug (used in the URL).
|
||||||
|
3. The new page opens, empty, ready for blocks.
|
||||||
|
|
||||||
|
### Edit the shared header
|
||||||
|
|
||||||
|
The **Edit Header** button at the top of the Pages tab opens the header in isolation. Whatever you put there appears on every page. The same applies to **Edit Footer**.
|
||||||
|
|
||||||
|
This is the right place to drop your Menu, Logo, and Social Links blocks — once, instead of once per page.
|
||||||
|
|
||||||
|
### Reorder or delete pages
|
||||||
|
|
||||||
|
Click a page in the list to edit it. The menu beside the page name lets you rename, change the slug, duplicate, or delete. Deleting a page removes it from the site on the next publish.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Renaming a slug changes the URL. If the page is already published and indexed, set up a redirect (or hold off until you can update inbound links).
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Layers panel
|
||||||
|
|
||||||
|
The **Layers** tab on the left shows the block tree for the current page — useful when blocks are nested inside containers and clicking on the canvas keeps selecting the wrong level. Click any layer to select that block; drag a layer to re-parent it.
|
||||||
|
|
||||||
|
## Assets panel
|
||||||
|
|
||||||
|
The **Assets** tab is where uploaded images, videos, and other media live. Drag an image directly from this panel into an image block, or use the **Upload** button to add new files.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Getting started](/whp/site-builder/getting-started/)
|
||||||
|
- [Styling your site](/whp/site-builder/styling/)
|
||||||
|
- [Publishing & code injection](/whp/site-builder/publishing/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
---
|
||||||
|
title: Getting started
|
||||||
|
description: Open Site Builder, pick a template or start from scratch, make your first edits, and publish.
|
||||||
|
sidebar:
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Steps, Aside } from '@astrojs/starlight/components';
|
||||||
|
import Beta from '~/content/partials/beta-callout.mdx';
|
||||||
|
import SignIn from '~/content/partials/signing-in.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<Beta />
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
- A **site** added in WHP that you want Site Builder to manage. Any container type works — Site Builder writes its output to the site's docroot when you publish, so a **Static HTML** site is the cleanest fit.
|
||||||
|
- About 10–15 minutes for your first walkthrough.
|
||||||
|
|
||||||
|
## Sign in to WHP
|
||||||
|
|
||||||
|
<SignIn />
|
||||||
|
|
||||||
|
## Open Site Builder
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. In the sidebar, click **Site Builder**. You'll see a card for each site on your account.
|
||||||
|

|
||||||
|
|
||||||
|
2. Click **Build Site** on the site you want to edit. The visual editor opens.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Pick a template (or start blank)
|
||||||
|
|
||||||
|
The editor opens onto an empty canvas, but the fastest way to get a real-looking site is to start from one of the 16 included templates.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. Click **Templates** in the top bar.
|
||||||
|

|
||||||
|
|
||||||
|
2. Browse the categories: **Business**, **Creative**, **Personal**, **Community**, or **All**. Each card shows the template name, a tag (Business / Creative / etc.), a short description, and the page count.
|
||||||
|
|
||||||
|
3. Click a template to preview and apply. Most templates ship as multi-page sites with their own header, footer, and a few sample pages so you can see structure right away.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Applying a template replaces the current site contents. If you've already made edits, save first (or duplicate the site) so you can roll back.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
If you'd rather start from scratch, skip Templates — the empty canvas with just a Header placeholder is the right starting point.
|
||||||
|
|
||||||
|
## Make your first edit
|
||||||
|
|
||||||
|
The editor has three panels:
|
||||||
|
|
||||||
|
- **Left:** the **Blocks** panel (also **Pages**, **Layers**, **Assets**).
|
||||||
|
- **Centre:** the canvas — what your visitors will see.
|
||||||
|
- **Right:** the **Styles** panel.
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
|
||||||
|
1. From the **Blocks** panel, drag a **Heading** block onto the canvas.
|
||||||
|
|
||||||
|
2. Click the heading on the canvas. An inline toolbar appears; type to replace the text.
|
||||||
|
|
||||||
|
3. Drag a **Text** block underneath and add a sentence or two.
|
||||||
|
|
||||||
|
4. Drag a **Button** block next. Click it, set its label and link in the right-side panel.
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
You've built your first page. The header at the top is the **shared header** — every page on the site uses it.
|
||||||
|
|
||||||
|
## Preview the result
|
||||||
|
|
||||||
|
The top bar has three device toggles next to the site name: **desktop**, **tablet**, **mobile**. Click each to see how your page looks at that width. The **Preview** button opens a full-window preview without the editor chrome.
|
||||||
|
|
||||||
|
## Save vs. publish
|
||||||
|
|
||||||
|
Two distinct actions:
|
||||||
|
|
||||||
|
- **Save** stores a draft. Your changes are kept on the server but the live site is unchanged.
|
||||||
|
- **Publish** writes the rendered HTML and assets to the site's docroot. Your visitors see the new version on the next request.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Save often as you work — every Save is a checkpoint. Publish only when you're happy with what you've built.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Get back to the Site Builder list
|
||||||
|
|
||||||
|
Click **Back to Panel** in the top-left of the editor. You can come back any time and continue from where you left off.
|
||||||
|
|
||||||
|
## What's next
|
||||||
|
|
||||||
|
- **[Blocks & pages](/whp/site-builder/blocks-and-pages/)** — the rest of the block library, and how to add more pages.
|
||||||
|
- **[Styling your site](/whp/site-builder/styling/)** — set your colours and fonts in one place.
|
||||||
|
- **[Publishing & code injection](/whp/site-builder/publishing/)** — add analytics, custom fonts, or global CSS.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: Site Builder overview
|
||||||
|
description: Build a website visually inside WHP — drag-and-drop blocks, ready-made templates, draft / publish workflow.
|
||||||
|
sidebar:
|
||||||
|
order: 1
|
||||||
|
badge:
|
||||||
|
text: Beta
|
||||||
|
variant: tip
|
||||||
|
---
|
||||||
|
|
||||||
|
import Beta from '~/content/partials/beta-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<Beta />
|
||||||
|
|
||||||
|
**Site Builder** is a visual editor that lives inside WHP. You don't need to know HTML or CSS to use it — drag content blocks onto a page, tweak text and colours in place, and click **Publish** when you're ready. It builds a real static website that's hosted on the WHP site you point it at, so the published output is fast and works exactly like any other site you'd host with us.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## What you get
|
||||||
|
|
||||||
|
- **50+ drag-and-drop content blocks** across Basic, Layout, Sections, Media, and Forms categories.
|
||||||
|
- **16 ready-made templates** — restaurant, small business, SaaS landing, agency, medical/dental, portfolio, photography, content creator, event/conference, and more.
|
||||||
|
- **Multi-page sites** with a shared header and footer that update everywhere at once.
|
||||||
|
- **Image and video uploads** managed in the **Assets** panel.
|
||||||
|
- **Mobile-responsive preview** — desktop, tablet, and mobile views from one toggle.
|
||||||
|
- **Site Design Tokens** — set your primary, secondary, and accent colours plus heading and body fonts once, and they apply across every block on the site.
|
||||||
|
- **Custom head code injection** for analytics, custom fonts, or global CSS.
|
||||||
|
- **Save drafts & publish when ready** — your edits don't go live until you click Publish.
|
||||||
|
|
||||||
|
## When Site Builder is the right tool
|
||||||
|
|
||||||
|
- You want a landing page, marketing site, or small multi-page brochure.
|
||||||
|
- You don't want to touch HTML/CSS, but you want more flexibility than a one-size template.
|
||||||
|
- You want to ship a draft to a colleague for review before it goes live.
|
||||||
|
|
||||||
|
## When it isn't
|
||||||
|
|
||||||
|
- You're running a CMS (WordPress, Drupal, etc.) — those run as regular WHP sites of their own, not inside Site Builder.
|
||||||
|
- You need server-side dynamic behaviour beyond simple forms — Site Builder produces static pages.
|
||||||
|
- You already have a build pipeline (Astro, Hugo, Next.js static export) — keep using that; just deploy the output to a regular WHP site.
|
||||||
|
|
||||||
|
## In this section
|
||||||
|
|
||||||
|
- **[Getting started](/whp/site-builder/getting-started/)** — open the editor, pick a template or start blank, make your first edit, save and publish.
|
||||||
|
- **[Blocks & pages](/whp/site-builder/blocks-and-pages/)** — the block library, adding pages, editing the shared header and footer.
|
||||||
|
- **[Styling your site](/whp/site-builder/styling/)** — Site Design Tokens, fonts, advanced styling controls.
|
||||||
|
- **[Publishing & code injection](/whp/site-builder/publishing/)** — drafts vs. publish, the device previews, adding custom `<head>` code.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
title: Publishing & code injection
|
||||||
|
description: Drafts vs. publish, device previews, and adding analytics, custom fonts, or global CSS via the custom head code panel.
|
||||||
|
sidebar:
|
||||||
|
order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Beta from '~/content/partials/beta-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<Beta />
|
||||||
|
|
||||||
|
## Drafts vs. publish
|
||||||
|
|
||||||
|
Site Builder separates "save my edits" from "make these edits live."
|
||||||
|
|
||||||
|
- **Save** stores your edits as a draft on the server. You can close the editor and come back; your work is preserved. The live site doesn't change.
|
||||||
|
- **Publish** renders the site, writes the resulting HTML and assets to the site's docroot, and serves it to visitors on the next request.
|
||||||
|
|
||||||
|
The two buttons are in the top right of the editor.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Treat **Save** as your "ctrl-S" and **Publish** as your release button. Save liberally; publish when you're ready.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Preview before publishing
|
||||||
|
|
||||||
|
Two previews are available, both without affecting the live site:
|
||||||
|
|
||||||
|
- **Inline preview** — the desktop/tablet/mobile toggle on the top bar swaps the canvas viewport so you can see how the page reflows.
|
||||||
|
- **Full-window preview** — click **Preview** at the top to see your draft rendered without the editor chrome. Use this to read the page like a visitor.
|
||||||
|
|
||||||
|
If you want to share a preview with someone outside your account, save the draft, then publish to a staging site (a separate WHP site you keep around for review).
|
||||||
|
|
||||||
|
## Custom head code
|
||||||
|
|
||||||
|
The **Code** button at the top of the editor opens a modal where you can paste content into the `<head>` of every page on the site.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Common uses:
|
||||||
|
|
||||||
|
- **Google Analytics, Plausible, Fathom, or other analytics** — paste the tracking snippet they give you.
|
||||||
|
- **Custom fonts** — `<link>` tags pointing at Google Fonts, Fontsource, or your own hosted font.
|
||||||
|
- **Global CSS overrides** — inline `<style>` blocks for tweaks the Styles panel can't reach.
|
||||||
|
- **Verification tags** — `<meta>` tags for Google Search Console, Bing Webmaster, etc.
|
||||||
|
|
||||||
|
Click **Done** to close the modal. The code is injected on the next publish.
|
||||||
|
|
||||||
|
<Aside type="caution">
|
||||||
|
Whatever you paste here runs on every page. Test analytics in your browser's dev tools before publishing widely, and keep any inline `<script>` content small — slow scripts make every page slower.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Undo / redo
|
||||||
|
|
||||||
|
The two arrows in the top toolbar (left of **Templates**) walk your edit history backwards and forwards. They cover the current editing session — once you close the editor and come back, the undo stack resets.
|
||||||
|
|
||||||
|
## Reverting a published version
|
||||||
|
|
||||||
|
Site Builder writes a clean copy on each publish, so there's no built-in "rollback" button. If you publish a version you regret:
|
||||||
|
|
||||||
|
- Make the corrective edits and publish again — usually the fastest fix.
|
||||||
|
- For more serious mistakes, restore from a [WHP backup](/whp/how-to/backups/). The site's domain folder is included in your scheduled site backups.
|
||||||
|
|
||||||
|
## Submitting feedback
|
||||||
|
|
||||||
|
Site Builder is in beta. The top bar has a **Submit feedback** link — please use it for anything weird, missing, or broken. The team reads every report.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Getting started](/whp/site-builder/getting-started/)
|
||||||
|
- [Blocks & pages](/whp/site-builder/blocks-and-pages/)
|
||||||
|
- [Styling your site](/whp/site-builder/styling/)
|
||||||
|
- [Backups](/whp/how-to/backups/)
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
title: Styling your site
|
||||||
|
description: Set colours, fonts, and link styles once with Site Design Tokens; reach for Advanced when you need finer control.
|
||||||
|
sidebar:
|
||||||
|
order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
import Beta from '~/content/partials/beta-callout.mdx';
|
||||||
|
import Support from '~/content/partials/support-link.mdx';
|
||||||
|
|
||||||
|
<Beta />
|
||||||
|
|
||||||
|
The **Styles** panel on the right side of the editor controls site-wide design. Set a colour or font here and every block that uses it updates everywhere — no need to edit each page.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Site Design Tokens (Basic)
|
||||||
|
|
||||||
|
The default **Basic** view exposes the most common controls:
|
||||||
|
|
||||||
|
- **Primary Color** — used for buttons, accents, and important highlights.
|
||||||
|
- **Secondary Color** — used for secondary buttons, hover states, and supporting accents.
|
||||||
|
- **Accent Color** — used for callouts, badges, and small decorative touches.
|
||||||
|
- **Heading Font** — applies to all heading blocks (H1–H6).
|
||||||
|
- **Body Font** — applies to paragraph text and other body copy.
|
||||||
|
- **Link Color** — used for inline links inside text blocks.
|
||||||
|
|
||||||
|
Each colour has a hex input plus an eight-swatch quick-pick row (a curated palette of vibrant brand colours). Click a swatch to apply it, or paste your brand hex into the input.
|
||||||
|
|
||||||
|
<Aside type="tip">
|
||||||
|
Tokens are saved with your site, so visiting the editor again preserves your palette and fonts. Changes apply immediately on the canvas — but, like everything else in Site Builder, they go live only when you click **Publish**.
|
||||||
|
</Aside>
|
||||||
|
|
||||||
|
## Advanced
|
||||||
|
|
||||||
|
Click **Advanced** in the Site Design Tokens header to expose finer controls:
|
||||||
|
|
||||||
|
- Extended palette — backgrounds, surfaces, borders, muted text, and inversions for dark sections.
|
||||||
|
- Type scale — adjust the relative size between H1–H6 and body text.
|
||||||
|
- Spacing scale — tune the default padding and margin used by sections and containers.
|
||||||
|
|
||||||
|
If you make a mess, click **Reset to Defaults** at the bottom of the panel — it restores the original token set without touching your content.
|
||||||
|
|
||||||
|
## Per-block overrides
|
||||||
|
|
||||||
|
The tokens above are the default for the whole site. When you need a single block to deviate (a one-off red CTA, for instance), select that block on the canvas; its block-settings panel on the right has its own colour controls that take precedence over the site tokens.
|
||||||
|
|
||||||
|
Use per-block overrides sparingly. The point of tokens is consistency — if you find yourself overriding the same value on every block, change the token instead.
|
||||||
|
|
||||||
|
## Picking fonts
|
||||||
|
|
||||||
|
The Heading and Body font dropdowns include a curated list of web fonts. If you need a font that isn't in the list:
|
||||||
|
|
||||||
|
1. Add a `<link>` to the font's CSS via [Custom head code](/whp/site-builder/publishing/#custom-head-code).
|
||||||
|
2. Type the font's exact family name into the font picker.
|
||||||
|
|
||||||
|
The dropdown is forgiving about custom names — anything you type will be applied as the `font-family`.
|
||||||
|
|
||||||
|
## Live preview at different widths
|
||||||
|
|
||||||
|
The desktop / tablet / mobile toggles at the top of the editor switch the canvas viewport. Layout decisions (column counts, padding, font size) often look fine at one width and break at another — switch through all three before publishing.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Blocks & pages](/whp/site-builder/blocks-and-pages/) — what the tokens apply to.
|
||||||
|
- [Publishing & code injection](/whp/site-builder/publishing/) — add a custom font via `<head>`.
|
||||||
|
|
||||||
|
## Still stuck?
|
||||||
|
|
||||||
|
<Support />
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Super admin access is granted to the `root` user only. Sign in **directly** at `https://<your-server-hostname>:8443` with the root credentials.
|
||||||
|
|
||||||
|
The WHMCS client portal route doesn't apply for super admin — it signs you in as the linked customer, not as root.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<div class="anhh-accent-callout">
|
||||||
|
**Beta feature.** Site Builder is in active development. Some controls are still being refined and templates are being expanded. We'd love your feedback — use the **Submit feedback** link in the Site Builder header.
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Aside } from '@astrojs/starlight/components';
|
||||||
|
|
||||||
|
<Aside type="caution" title="Draft — work in progress">
|
||||||
|
This page is a draft. Some details may not match the panel yet, and screenshots are still pending. We're publishing it as a placeholder so the structure is in place; we'll fill in the gaps as we go.
|
||||||
|
</Aside>
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
Some applications need to connect to databases, caches, or other internal services. Use these hostnames from inside your site — they only resolve within your hosting container, so they don't work from your laptop.
|
Some applications need to connect to databases or caches. Use these hostnames from inside your site — they only resolve within your hosting container, so they don't work from your laptop.
|
||||||
|
|
||||||
| Service | Hostname | Default port | Notes |
|
| Service | Hostname | Default port | Notes |
|
||||||
| ---------------------- | ----------------------- | ------------ | ------------------------------------------------------ |
|
| ----------- | ----------- | ------------ | -------------------------------------------------------------------------------- |
|
||||||
| MySQL | `mysql` | 3306 | Username, password, and database from your WHP panel. |
|
| MySQL | `mysql` | 3306 | Username, password, and database from the **MySQL Management** page. |
|
||||||
| PostgreSQL | `postgres` | 5432 | Available on most plans; ask support if it's missing. |
|
| PostgreSQL | `postgres` | 5432 | Username, password, and database from the **PostgreSQL** page. |
|
||||||
| Memcached | `memcache` | 11211 | No auth; isolated per container. |
|
| Valkey | `valkey` | 6379 | Redis wire-compatible (phpredis, Predis, ioredis work unchanged). No password — the connection is network-isolated to your account. Enable on the **Valkey Cache** page first. |
|
||||||
| Redis | `redis` | 6379 | Single DB per site; password in panel. |
|
|
||||||
| SMTP (outbound) | `smtp.anhonesthost.com` | 587 | Use your full email address as the username. |
|
For **email**, the mail server hostname is a per-server value (e.g. `mail01.cloud-hosting.io`) and is shown on the **Dashboard** page under Server Information.
|
||||||
|
|
||||||
**Connecting from your laptop?** Use the panel's **phpMyAdmin** (MySQL) or **Adminer** (PostgreSQL) for ad-hoc queries — direct external connections to these internal services are not exposed for security reasons.
|
**Connecting from your laptop?** Use the panel's **phpMyAdmin** (MySQL) or **Adminer** (PostgreSQL) for ad-hoc queries — direct external connections to these internal services are not exposed for security reasons.
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ import { URLS } from './urls.mdx';
|
|||||||
|
|
||||||
You can sign in to WHP two ways:
|
You can sign in to WHP two ways:
|
||||||
|
|
||||||
1. **Through your client portal** (recommended). Go to [{URLS.whmcs}]({URLS.whmcs}), sign in to your account, open **Services → My Services**, click your hosting plan, then click **Login to WHP**. No extra password to remember.
|
1. **Through your client portal** (recommended). Go to <a href={URLS.whmcs}>{URLS.whmcs}</a>, sign in to your account, open **Services → My Services**, click your hosting plan, then click **Login to WHP**. No extra password to remember.
|
||||||
2. **Directly with your WHP credentials.** Visit `https://<your-server-hostname>:8443` and sign in with the WHP username and password you set up. Your server hostname is in your welcome email and on the service page in the client portal.
|
2. **Directly with your WHP credentials.** Visit `https://<your-server-hostname>:8443` and sign in with the WHP username and password you set up. Your server hostname is in your welcome email and on the service page in the client portal.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<div class="anhh-accent-callout">
|
||||||
|
**Requires WHP super admin access.** These features are unlocked for customers with a WHP super admin role on the server — for example, anyone running a [Virtual Dedicated Server](https://anhonesthost.com/vds). Customers without super admin won't see these pages.
|
||||||
|
</div>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { URLS } from './urls.mdx';
|
import { URLS } from './urls.mdx';
|
||||||
|
|
||||||
<div class="anhh-accent-callout">
|
<div class="anhh-accent-callout">
|
||||||
**Still stuck?** Open a [support ticket]({URLS.whmcsTicket}) and our team will help.
|
**Still stuck?** Open a <a href={URLS.whmcsTicket}>support ticket</a> and our team will help.
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="astro/client" />
|
||||||
|
|
||||||
|
// Side-effect imports of CSS shipped by @fontsource-variable packages
|
||||||
|
declare module '@fontsource-variable/*';
|
||||||
|
declare module '@fontsource/*';
|
||||||
@@ -23,10 +23,25 @@ const PRODUCT_META: Record<string, { title: string; blurb: string; firstSection:
|
|||||||
blurb: 'Tips and tricks for getting the most out of WordPress on WHP.',
|
blurb: 'Tips and tricks for getting the most out of WordPress on WHP.',
|
||||||
firstSection: 'index',
|
firstSection: 'index',
|
||||||
},
|
},
|
||||||
'email-clients': {
|
email: {
|
||||||
title: 'Email clients',
|
title: 'Email',
|
||||||
blurb: 'Configure Outlook, Apple Mail, Thunderbird, and mobile clients.',
|
blurb: 'Set up Outlook, Apple Mail, Thunderbird, and mobile clients — plus news about our filtering.',
|
||||||
firstSection: 'index',
|
firstSection: 'set-up-your-email-client',
|
||||||
|
},
|
||||||
|
domains: {
|
||||||
|
title: 'Domains',
|
||||||
|
blurb: 'Transfer a domain to us, point it at your hosting, and sort out DNS problems.',
|
||||||
|
firstSection: 'transferring-a-domain-to-us',
|
||||||
|
},
|
||||||
|
cpanel: {
|
||||||
|
title: 'cPanel hosting',
|
||||||
|
blurb: 'Nameservers, SSL certificates, permissions, and email for our cPanel shared and reseller plans.',
|
||||||
|
firstSection: 'nameservers',
|
||||||
|
},
|
||||||
|
support: {
|
||||||
|
title: 'Support',
|
||||||
|
blurb: 'Working with our support team, including remote-assistance sessions.',
|
||||||
|
firstSection: 'remote-support',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,10 +71,10 @@ const visibleProducts: Product[] = Array.from(byProduct.entries())
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>An Honest Host Knowledge Base</title>
|
<title>AnHonestHost Knowledge Base</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Customer documentation for WHP and other An Honest Host services."
|
content="Customer documentation for WHP and other AnHonestHost services."
|
||||||
/>
|
/>
|
||||||
<link rel="canonical" href="https://kb.anhonesthost.com/" />
|
<link rel="canonical" href="https://kb.anhonesthost.com/" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
@@ -75,10 +90,52 @@ const visibleProducts: Product[] = Array.from(byProduct.entries())
|
|||||||
}
|
}
|
||||||
header,
|
header,
|
||||||
footer {
|
footer {
|
||||||
padding: 1.5rem 2rem;
|
padding: 1.25rem 2rem;
|
||||||
}
|
}
|
||||||
header {
|
header {
|
||||||
border-bottom: 1px solid var(--anhh-border-color, #334155);
|
border-bottom: 1px solid var(--anhh-border-color, #334155);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
a.brand {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.ext-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
.ext-link {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--anhh-text-secondary, #94a3b8);
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ext-link:hover,
|
||||||
|
.ext-link:focus {
|
||||||
|
color: var(--anhh-accent, #00d4aa);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
@media (max-width: 40em) {
|
||||||
|
.ext-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.kb-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--anhh-text-secondary, #94a3b8);
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
border-left: 1px solid var(--anhh-border-color, #334155);
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
footer {
|
footer {
|
||||||
border-top: 1px solid var(--anhh-border-color, #334155);
|
border-top: 1px solid var(--anhh-border-color, #334155);
|
||||||
@@ -152,7 +209,21 @@ const visibleProducts: Product[] = Array.from(byProduct.entries())
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header><strong>An Honest Host</strong> · Knowledge Base</header>
|
<header>
|
||||||
|
<a href="/" class="brand" aria-label="AnHonestHost Knowledge Base">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 32" fill="none" height="28" role="img">
|
||||||
|
<path d="M12 4L3 16L12 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 4L33 16L24 28" stroke="#00d4aa" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<line x1="12" y1="16" x2="24" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
|
||||||
|
<text x="44" y="21" font-family="Inter, system-ui, -apple-system, sans-serif" font-size="18" font-weight="800" fill="currentColor" letter-spacing="-0.5">AnHonest<tspan fill="#00d4aa">Host</tspan></text>
|
||||||
|
</svg>
|
||||||
|
<span class="kb-label">Knowledge Base</span>
|
||||||
|
</a>
|
||||||
|
<nav class="ext-nav" aria-label="AnHonestHost links">
|
||||||
|
<a href="https://anhonesthost.com/" class="ext-link">Get a Plan</a>
|
||||||
|
<a href="https://secure.anhonesthost.com/clientarea.php" class="ext-link">Client Portal</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<h1>Knowledge Base</h1>
|
<h1>Knowledge Base</h1>
|
||||||
<p class="lede">Pick a product to get started.</p>
|
<p class="lede">Pick a product to get started.</p>
|
||||||
|
|||||||
@@ -51,18 +51,26 @@
|
|||||||
--anhh-bg-elevated: #ffffff;
|
--anhh-bg-elevated: #ffffff;
|
||||||
--anhh-bg-surface: #e2e8f0;
|
--anhh-bg-surface: #e2e8f0;
|
||||||
--anhh-text-primary: #0f172a;
|
--anhh-text-primary: #0f172a;
|
||||||
--anhh-text-secondary: #475569;
|
--anhh-text-secondary: #334155; /* bumped from #475569 for AA contrast on muted UI text */
|
||||||
--anhh-text-muted: #94a3b8;
|
--anhh-text-muted: #475569; /* bumped from #94a3b8 — same reason */
|
||||||
--anhh-border-color: #cbd5e1;
|
--anhh-border-color: #cbd5e1;
|
||||||
--anhh-accent-dim: rgba(0, 212, 170, 0.08);
|
--anhh-accent-dim: rgba(0, 212, 170, 0.08);
|
||||||
--anhh-accent-border: rgba(0, 212, 170, 0.3);
|
--anhh-accent-border: rgba(0, 212, 170, 0.3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Darker accent for light mode so inline accent-coloured text and links
|
||||||
|
* (Starlight binds --sl-color-text-accent) pass WCAG AA against the bg.
|
||||||
|
* The brand teal #00d4aa is only ~1.7:1 on the cream background; #047857
|
||||||
|
* is ~6:1, which clears AA.
|
||||||
|
*/
|
||||||
|
--anhh-accent-on-light: #047857;
|
||||||
|
|
||||||
--sl-color-bg: var(--anhh-bg);
|
--sl-color-bg: var(--anhh-bg);
|
||||||
--sl-color-bg-nav: var(--anhh-bg-elevated);
|
--sl-color-bg-nav: var(--anhh-bg-elevated);
|
||||||
--sl-color-bg-sidebar: var(--anhh-bg-elevated);
|
--sl-color-bg-sidebar: var(--anhh-bg-elevated);
|
||||||
--sl-color-bg-inline-code: var(--anhh-bg-surface);
|
--sl-color-bg-inline-code: var(--anhh-bg-surface);
|
||||||
--sl-color-text: var(--anhh-text-primary);
|
--sl-color-text: var(--anhh-text-primary);
|
||||||
--sl-color-text-accent: var(--anhh-accent);
|
--sl-color-text-accent: var(--anhh-accent-on-light);
|
||||||
--sl-color-gray-1: var(--anhh-text-primary);
|
--sl-color-gray-1: var(--anhh-text-primary);
|
||||||
--sl-color-gray-2: var(--anhh-text-secondary);
|
--sl-color-gray-2: var(--anhh-text-secondary);
|
||||||
--sl-color-gray-3: var(--anhh-text-muted);
|
--sl-color-gray-3: var(--anhh-text-muted);
|
||||||
@@ -83,3 +91,24 @@
|
|||||||
body {
|
body {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Active sidebar item — Starlight default uses --sl-color-text-accent as the
|
||||||
|
* background, which on our teal (#00d4aa) gave only ~3:1 contrast against the
|
||||||
|
* dark gray text. Override with our primary blue + white text so contrast
|
||||||
|
* exceeds WCAG AA in both modes.
|
||||||
|
*
|
||||||
|
* Starlight scopes its rules via Astro's data-astro-cid-* attributes, so we
|
||||||
|
* need at least matching specificity. `:is(html)` adds a class-equivalent.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Specificity bump: customCss loads BEFORE Starlight's component-scoped CSS,
|
||||||
|
* and Starlight's rule has specificity (0,2,0) via its scoped class. We use
|
||||||
|
* a doubled attribute selector + html prefix for (0,2,1) to override.
|
||||||
|
*/
|
||||||
|
html [aria-current='page'][aria-current='page'],
|
||||||
|
html [aria-current='page'][aria-current='page']:hover,
|
||||||
|
html [aria-current='page'][aria-current='page']:focus {
|
||||||
|
background-color: var(--anhh-primary);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Screenshot pipeline
|
||||||
|
|
||||||
|
Captures real WHP screenshots into `src/assets/screenshots/whp/`.
|
||||||
|
|
||||||
|
**Local-only. Never runs in CI.** CI builds the static site; it never opens a browser or hits WHP.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. A demo WHP account (prod recommended for accuracy; staging works internally).
|
||||||
|
2. Network access to that WHP host.
|
||||||
|
3. Node 20+ and Playwright Chromium installed: `npx playwright install chromium`.
|
||||||
|
|
||||||
|
## Configure
|
||||||
|
|
||||||
|
Create `tools/screenshots/.env` (gitignored):
|
||||||
|
|
||||||
|
```
|
||||||
|
WHP_BASE=https://whp01.cloud-hosting.io:8443
|
||||||
|
WHP_USER=demo-kb
|
||||||
|
WHP_PASS=…
|
||||||
|
```
|
||||||
|
|
||||||
|
The script reads these via `process.env`; pass them in your shell or use a `.env` loader like `dotenv-cli`.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Load .env into your shell (one option):
|
||||||
|
set -a; source tools/screenshots/.env; set +a
|
||||||
|
npm run screenshots
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs one PNG per entry in `shots.config.ts` to `src/assets/screenshots/whp/<id>.png`. Existing files are overwritten.
|
||||||
|
|
||||||
|
## Capture rules
|
||||||
|
|
||||||
|
- **Viewport: 1440×900.** Locked. No `fullPage`. Playwright's viewport screenshots never include browser chrome — no address bar, no tab strip.
|
||||||
|
- **Mask list:** defaults (account ID, server hostname, user IP, billing column) plus per-shot additions.
|
||||||
|
- **No address bar in any image.** Multi-server fleet — we don't want a specific host in any screenshot.
|
||||||
|
- Use `selector` to clip to a region (e.g., just the sidebar) when the full viewport is noisier than useful.
|
||||||
|
|
||||||
|
## Refresh workflow
|
||||||
|
|
||||||
|
UI changed? → run the capture (see below) locally → review the diffs (`git diff --stat` shows changed PNGs) → **open each changed PNG and eyeball it for accidental leakage** (server hostname, IP, account ID, customer domains/usernames) → commit → push.
|
||||||
|
|
||||||
|
- **A page covered by `shots.config.ts`** (a plain navigate-and-shoot page): `npm run screenshots`.
|
||||||
|
- **A page that needs interaction** — opening a modal, ticking checkboxes, switching tabs — lives in a **section capture script** (see below). Re-run that script instead.
|
||||||
|
|
||||||
|
When a section is *reworked* (not just restyled), also: re-walk the new UI to find every state worth a screenshot, update the section script's steps, add/rename the `whp-<section>-*` ids, then refresh the `.mdx` references and run `npm run build` to confirm links and images resolve.
|
||||||
|
|
||||||
|
## Section capture scripts
|
||||||
|
|
||||||
|
`shots.config.ts` + `run.ts` only do navigate → redact → screenshot. Anything that needs **interaction or per-section redaction** gets its own `capture-<section>.ts`, run directly with `tsx`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set -a; source tools/screenshots/.env; set +a
|
||||||
|
npx tsx tools/screenshots/capture-dns.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
| Script | Covers | Auth |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `capture-admin.ts` | Server Settings tabs, admin pages | `WHP_ADMIN_USER` |
|
||||||
|
| `capture-site-builder.ts` | Site Builder editor states | `WHP_USER` |
|
||||||
|
| `capture-dns.ts` | Domains & DNS list, Add Domain modal, records editor, bulk toolbar | `WHP_USER` |
|
||||||
|
| `capture-email.ts` | Email page "Mail Client Setup" section (autodiscovery DNS records) | `WHP_USER` |
|
||||||
|
|
||||||
|
Each script carries its own `redact()` (text-node + input-value swaps) so fleet hostnames, IPs, and customer data become neutral placeholders while brand/demo domains stay visible. Copy the closest existing script when adding a new section — match its viewport (1440×900), `deviceScaleFactor: 2`, and **read-only** discipline (open modals and tick boxes for the shot, but never save/delete/submit).
|
||||||
|
|
||||||
|
## Adding a new shot
|
||||||
|
|
||||||
|
1. **Static page?** Add an entry to `shots.config.ts` with a stable `id`, then `npm run screenshots`.
|
||||||
|
2. **Interactive state?** Add the step to the relevant `capture-<section>.ts` (or copy one for a new section), then `npx tsx tools/screenshots/capture-<section>.ts`.
|
||||||
|
3. Reference the new file in your `.mdx`: ``.
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"text": "Logout",
|
||||||
|
"href": "index.php?whp-action=logout"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Dashboard",
|
||||||
|
"href": "index.php?page=dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Sites",
|
||||||
|
"href": "index.php?page=sites"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Traffic",
|
||||||
|
"href": "index.php?page=site-traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Site Builder BETA",
|
||||||
|
"href": "index.php?page=site-builder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Domains",
|
||||||
|
"href": "index.php?page=domains"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "WordPress",
|
||||||
|
"href": "index.php?page=wordpress"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "File Manager",
|
||||||
|
"href": "./filemanager/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Terminal",
|
||||||
|
"href": "index.php?page=terminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "cPanel Import",
|
||||||
|
"href": "index.php?page=cpanel-import"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "MySQL Management",
|
||||||
|
"href": "index.php?page=database-management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "phpMyAdmin",
|
||||||
|
"href": "./phpmyadmin-sso.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "PostgreSQL",
|
||||||
|
"href": "index.php?page=postgresql-management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Adminer",
|
||||||
|
"href": "./adminer-sso.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Valkey Cache",
|
||||||
|
"href": "index.php?page=account-valkey"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Email",
|
||||||
|
"href": "index.php?page=email-management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Backups",
|
||||||
|
"href": "index.php?page=backups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Site Monitoring",
|
||||||
|
"href": "index.php?page=ai-monitor-customer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Delegated Users",
|
||||||
|
"href": "index.php?page=delegated-users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Active Sessions",
|
||||||
|
"href": "index.php?page=active-sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Cloud Apache Container",
|
||||||
|
"href": "https://repo.anhonesthost.net/cloud-hosting-platform/cloud-apache-container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Cloud Node Container",
|
||||||
|
"href": "https://repo.anhonesthost.net/cloud-hosting-platform/cloud-node-container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Manage Domains",
|
||||||
|
"href": "index.php?page=domains"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Manage Databases",
|
||||||
|
"href": "index.php?page=database-management"
|
||||||
|
}
|
||||||
|
]
|
||||||