Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
113c795c83 | ||
|
|
0e3ae941eb | ||
|
|
1e3e0bd546 | ||
|
|
a28e20f881 | ||
|
|
063f992516 | ||
|
|
e4ea0484fd |
@@ -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 ###
|
||||
|
||||
@@ -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.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 358 KiB |
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Switching your site's backend
|
||||
description: Change the web engine (container type) running a site — standard PHP/FPM or the premium LiteSpeed/OpenLiteSpeed tier.
|
||||
description: Change the container type running a site — standard PHP/FPM, the premium LiteSpeed/OpenLiteSpeed tier, or Node.js.
|
||||
sidebar:
|
||||
order: 6
|
||||
---
|
||||
@@ -9,7 +9,7 @@ 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 web engine that serves your site's files and runs your PHP code. 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.
|
||||
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.
|
||||
|
||||
@@ -36,6 +36,7 @@ Switching backends is a one-step change in the Sites editor, but it does briefly
|
||||
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.
|
||||
|
||||
@@ -55,6 +56,24 @@ To enable it:
|
||||
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">
|
||||
@@ -89,6 +108,10 @@ Once all sites are on standard backends, you can cancel or disable the add-on fr
|
||||
|
||||
**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/)
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Sites "backend" capture — the Edit Site modal's Container Type field,
|
||||
* captured with a Node.js container type selected (for the "switching your
|
||||
* site's backend" how-to's Node.js section).
|
||||
*
|
||||
* Captures, as the demo customer:
|
||||
* - whp-sites-edit-node.png Edit Site modal, Container Type = Node22,
|
||||
* showing the auto-adjusted Memory per
|
||||
* Container / Total Resources readout.
|
||||
*
|
||||
* Viewport-only (1440x900), redacted for our multi-server fleet: server /
|
||||
* mail / nameserver hostnames, IPs, and the navbar brand strip become
|
||||
* neutral or hidden, while the brand demo domain (whp-demo.anhh.co) stays
|
||||
* visible on purpose.
|
||||
*
|
||||
* Read-only: opens the Edit modal and changes the Container Type dropdown
|
||||
* for the shot, but never clicks Save.
|
||||
*/
|
||||
import { chromium, type Page } from 'playwright';
|
||||
import { mkdir, readFile } from 'node:fs/promises';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const OUT_DIR = resolve(__dirname, '../../src/assets/screenshots/whp');
|
||||
|
||||
async function loadEnv(): Promise<void> {
|
||||
const envPath = resolve(__dirname, '.env');
|
||||
const content = await readFile(envPath, 'utf-8');
|
||||
for (const line of content.split('\n')) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith('#')) continue;
|
||||
const eq = trimmed.indexOf('=');
|
||||
if (eq === -1) continue;
|
||||
const key = trimmed.slice(0, eq).trim();
|
||||
const val = trimmed.slice(eq + 1).trim();
|
||||
if (!process.env[key]) process.env[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
function need(name: string): string {
|
||||
const v = process.env[name];
|
||||
if (!v) throw new Error(`missing env: ${name}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
const HIDE_CSS = `.navbar-text, .brand-full { visibility: hidden !important; }`;
|
||||
|
||||
async function login(page: Page, base: string, user: string, pass: string) {
|
||||
await page.goto(`${base}/login.php`, { waitUntil: 'domcontentloaded' });
|
||||
await page.fill('input[name="user"]', user);
|
||||
await page.fill('input[name="password"]', pass);
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Neutralise fleet-identifying text before the screenshot. The brand demo
|
||||
* domain (anhh.co) is intentionally preserved; everything that names a
|
||||
* specific server, mail host, nameserver, or IP is swapped for a placeholder.
|
||||
*/
|
||||
async function redact(page: Page) {
|
||||
await page.addStyleTag({ content: HIDE_CSS });
|
||||
await page.evaluate(() => {
|
||||
const swaps: [RegExp, string][] = [
|
||||
[/ns[12]\.whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, 'ns<n>.<your-server>.cloud-hosting.io'],
|
||||
[/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '<your-server>.cloud-hosting.io'],
|
||||
[/mail\d+\.cloud-hosting\.io/gi, '<mail-server>.cloud-hosting.io'],
|
||||
[/WHP\d+(-[A-Z0-9]+)?\b/g, '<YOUR-SERVER>'],
|
||||
[/whp\d+(-[a-z0-9]+)?\b/gi, '<your-server>'],
|
||||
// Public IPv4 (skip RFC1918 — those read fine as generic examples)
|
||||
[/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '<server-IP>'],
|
||||
[/demo-user/g, 'your-username'],
|
||||
];
|
||||
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
||||
const nodes: Text[] = [];
|
||||
let n: Node | null = walker.nextNode();
|
||||
while (n) { nodes.push(n as Text); n = walker.nextNode(); }
|
||||
for (const node of nodes) {
|
||||
let v = node.nodeValue ?? '';
|
||||
for (const [re, rep] of swaps) v = v.replace(re, rep);
|
||||
if (v !== node.nodeValue) node.nodeValue = v;
|
||||
}
|
||||
document.querySelectorAll<HTMLInputElement>('input').forEach((el) => {
|
||||
if (el.type === 'password' || !el.value) return;
|
||||
let v = el.value;
|
||||
for (const [re, rep] of swaps) v = v.replace(re, rep);
|
||||
if (v !== el.value) el.value = v;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function shot(page: Page, id: string) {
|
||||
await page.waitForTimeout(400);
|
||||
await redact(page);
|
||||
const path = resolve(OUT_DIR, `${id}.png`);
|
||||
await page.screenshot({ path, fullPage: false });
|
||||
console.log(`captured ${id} -> ${path}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await loadEnv();
|
||||
const BASE = need('WHP_BASE');
|
||||
const USER = need('WHP_USER');
|
||||
const PASS = need('WHP_PASS');
|
||||
const DOMAIN = process.env.WHP_DEMO_DOMAIN ?? 'whp-demo.anhh.co';
|
||||
|
||||
await mkdir(OUT_DIR, { recursive: true });
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const ctx = await browser.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
viewport: { width: 1440, height: 900 },
|
||||
deviceScaleFactor: 2,
|
||||
});
|
||||
const page = await ctx.newPage();
|
||||
try {
|
||||
await login(page, BASE, USER, PASS);
|
||||
|
||||
await page.goto(`${BASE}/index.php?page=sites`, { waitUntil: 'networkidle' });
|
||||
|
||||
// Select the demo site from the "Select a Site" widget (Choices.js).
|
||||
const siteChoicesContainer = page.locator('div.choices:has(#siteDropdown)').first();
|
||||
await siteChoicesContainer.click();
|
||||
await page.waitForTimeout(300);
|
||||
const item = page
|
||||
.locator('.choices__list--dropdown .choices__item--choice', { hasText: DOMAIN.split('.')[0] })
|
||||
.first();
|
||||
await item.click();
|
||||
|
||||
// Wait for the async site-details fetch, then open Edit (Manage).
|
||||
await page.locator('#siteActionsContainer').waitFor({ state: 'visible', timeout: 10000 });
|
||||
await page.waitForTimeout(300);
|
||||
await page.locator('#manageSiteBtn').click();
|
||||
await page.waitForTimeout(800);
|
||||
|
||||
// Switch the Container Type to Node22 (the newest of the three Node
|
||||
// options) to show the memory floor auto-adjust to 512 MB.
|
||||
const editSelect = page.locator('#edit_container_type_id');
|
||||
await editSelect.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await editSelect.scrollIntoViewIfNeeded();
|
||||
const nodeOption = editSelect.locator('option', { hasText: 'Node22' });
|
||||
const nodeValue = await nodeOption.getAttribute('value');
|
||||
if (!nodeValue) throw new Error('Node22 option not found in edit_container_type_id');
|
||||
await editSelect.selectOption(nodeValue);
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
await shot(page, 'whp-sites-edit-node');
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user