Handpress on cPanel
Ordinary shared hosting is the best place to run this: the files are right there, so a save is live the moment the client presses it.
On Cloudflare a save waits for a deploy. Here there is nothing to wait for — the editor writes the HTML file, and the next visitor gets it. Git is still used for history, when the host allows it.
What you need
| Required | cPanel with PHP 8.1 or newer and the curl extension (both are standard), and a domain or subdomain with HTTPS — cPanel's AutoSSL does this free. |
|---|---|
| Nice to have | Terminal or SSH access, and Git Version Control in cPanel. With them, every save is also a git commit pushed to your repository. Without them, Handpress keeps timestamped backups instead — see Without SSH. |
| On your own machine | Node, to run the two handpress commands. Nothing is built or compiled; you are only copying files and adding keys. |
A word on layout. On cPanel your site lives in public_html (or
public_html/subdomain), and your home directory is one level above it. That
distinction matters: the settings file holds your keys and must sit in the
home directory, where the web server will not serve it.
/home/youruser/
├── handpress-config.php ← settings and keys, never reachable from the web
├── handpress-sessions/ ← created automatically
├── handpress-backups/ ← created automatically
└── public_html/ ← the site itself
├── index.html
├── about.html
├── editor.js
├── admin.html
├── api.php
└── uploads/
1. Put the site on the server
With Git Version Control (preferred)
In cPanel, open Git Version Control → Create, tick Clone a Repository,
give it your repository URL and set the path to public_html. For a private
repository, first add the server's key to it: cPanel → SSH Access → Manage SSH Keys,
generate a key, then add the public key to your repository as a deploy key with write access.
Write access matters: it is what lets each save push back.
Without git
Zip your site, upload it in File Manager, and extract it into public_html.
Saves will still work; they just won't be committed.
2. Add the editor
On your own machine, in your site's repository:
npm i -D handpress
npx handpress install ./public_html --php
npx handpress keys "public_html/*.html" --global "header, footer"
The --php flag adds the server half: api.php, the two Apache
templates, and a sample settings file placed one level above the site.
Then add this to your site's own JavaScript, so visitors never load the editor:
if (/(?:^|;\s*)ed=1/.test(document.cookie)) {
var s = document.createElement('script');
s.src = '/editor.js'; s.defer = true;
document.head.appendChild(s);
}
handpress keys reformats your HTML as well as adding keys. Commit that on its
own, before any content change — otherwise your first real edit is buried in a whole-file diff.
Commit and push, then pull on the server (Git Version Control → Manage → Pull or Deploy), or upload the changed files by File Manager.
3. Settings above the web root
In File Manager, go up from public_html to your home directory,
rename handpress-config.sample.php to handpress-config.php, and edit it:
return [
'admin_emails' => ['owner@example.com'],
'google_client_id' => '…apps.googleusercontent.com',
'pages' => ['index.html', 'about.html'],
'data_files' => ['data.js' => 'Opening times'],
'global_blocks' => ['header', 'footer'],
'upload_dir' => 'uploads',
'git' => true, // false if the host has no shell access
'backups' => true,
];
Set its permissions to 600 (File Manager → right-click → Change Permissions,
or chmod 600). Only your account needs to read it.
pages is the allowlist. A page that is not listed cannot be read or written by
anyone, including a signed-in admin.
4. Apache rules
In File Manager, turn on Settings → Show hidden files (dotfiles), then:
-
The site's rules
Rename
htaccess.txtinpublic_htmlto.htaccess. If you already have one — a WordPress install, a redirect — paste the Handpress block at the top of the existing file instead of replacing it.It routes
/api/…toapi.php, gives you clean URLs, and returns 404 for the parts of your repository that are not the website (.git,node_modules, Markdown files, the settings file). -
The uploads rules
Create the
uploadsdirectory insidepublic_htmlif it does not exist, and moveuploads-htaccess.txtinto it as.htaccess.This is the one that matters most on shared hosting: it means whatever lands in that directory is served but never executed.
5. PHP version
cPanel → Select PHP Version (sometimes MultiPHP Manager) → choose
8.1 or newer for this domain. In the extensions list, make sure
curl is ticked: sign-in uses it to check the token with Google.
If Select PHP Version offers disable_functions, leave
shell_exec enabled if you want git commits. Handpress works without it and simply
skips the commit.
6. Google sign-in
-
Create the OAuth client
In the Google Cloud console: APIs & Services → Credentials → Create credentials → OAuth client ID → Web application.
-
List your site as an origin
Under Authorized JavaScript origins, add
https://yourdomain.com(and the staging subdomain, if you have one). No redirect URIs are needed. -
Put the client ID in the settings file
Copy it into
google_client_id, and put the owner's Google address inadmin_emails. That address is the whole of the access control: anyone on the list may edit, everyone else is refused.
Make sure HTTPS is on first — cPanel → SSL/TLS Status → Run AutoSSL. The session cookie is marked Secure, so sign-in will not stick over plain HTTP.
7. Check it
Visit https://yourdomain.com/admin and sign in. The site should reappear with
an editor bar along the bottom. Then run through this list — it takes two minutes and catches
the things that matter:
| Edit text on two pages and save | The change is live immediately. If git is on, a commit appears in your repository. |
| Edit the footer and open another page | The change is on every page. |
| Duplicate a card or list item, then delete it | Both survive a save. |
| Replace an image | The new file appears in uploads/ and is served. |
Open yourdomain.com/uploads/ | Should not list the directory. |
Open yourdomain.com/.git/config and /handpress-config.php | Both must return 404. If either downloads, stop and fix the .htaccess. |
| Open the site in a private window | editor.js must not load, and /api/me must return 401. |
Without SSH
Plenty of cheap cPanel plans have no Terminal and no git. Handpress still runs; you lose the commit history, not the editing.
Set 'git' => false in the settings file. Every save then keeps a timestamped
copy of each file it is about to overwrite, in handpress-backups/ above the web
root, and the last hundred saves are kept.
To restore something, open File Manager, find the dated folder, and copy the file back into
public_html. It is cruder than git revert, but it has saved the same
afternoon of work.
If your plan has git but no shell, cPanel's Git Version Control can still pull deployments from your repository — but it cannot push the client's edits back. Treat the server as the source of truth in that case, and pull from it when you next work locally, or you will overwrite their changes.
Troubleshooting
| What you see | What it usually is |
|---|---|
| Sign-in page loads, but the Google button never appears | google_client_id is empty, or your origin is not listed in the OAuth client. Both are exact-match: https://www.example.com and https://example.com are different origins. |
| "Google sign-in could not be verified" | The client ID in the settings file does not match the one the page used, or curl is disabled for this PHP version. |
| You sign in, then immediately get "Please sign in again" | The session is not persisting: the site is not on HTTPS, or the home directory is not writable for handpress-sessions. |
404 on /api/config | .htaccess is missing (hidden files are off in File Manager), or mod_rewrite is off. Test with /api.php?route=config — if that works, it is the rewrite. |
| "This page can't be edited" | The page is not in pages, or it has no keys because handpress keys was not run, or the file you uploaded is not the keyed one. |
| "Could not write … Check the folder's permissions" | Files should be 644 and directories 755, owned by your cPanel user. If you uploaded as a different user, fix ownership in File Manager. |
| Save works, but nothing is committed | Expected without shell access. With it: check .git exists in public_html, and that the deploy key has write access. |
| The editor sits on "Publishing…" | Something is serving a cached copy — often a CDN or a cPanel cache in front of the site. The editor waits for the page it just wrote. |
| Clean URLs broke an existing app | Delete the clean-URL block from .htaccess; only the /api/ rule is required. |