# 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//`, 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.