How To Get Free SSL Working on Namecheap Shared Hosting
My site (mnrdsgn.com) was flagged “at risk” in cPanel — no SSL certificate installed. Simple problem, I figured. Click into the SSL/TLS Wizard, generate a free cert, done.
Instead I got this:
“There are no SSL/TLS products available at this time. SSL/TLS providers can be enabled by the server administrator.”
Checking SSL/TLS Status directly wasn’t any more encouraging — both mnrdsgn.com and www.mnrdsgn.com showed “No certificate available.” I own a Namecheap Stellar Plus shared hosting plan, on cPanel 134 with Apache 2.4.68. I have SSH access. I don’t have root. And apparently, that combination means cPanel’s built-in tools have nothing to offer me for free.
Turns out that’s expected, not broken: Namecheap shared hosting doesn’t support AutoSSL. AutoSSL — the feature that would normally auto-issue and auto-renew free Let’s Encrypt certificates — is only available on Namecheap’s VPS and Dedicated plans, where you have root. On shared hosting, their own answer is to sell a PositiveSSL cert through a cPanel plugin — free for the first year, then a recurring charge forever after.
I wasn’t interested in paying an annual fee for something Let’s Encrypt gives away for free. So I did it myself over SSH, using a lightweight tool called acme.sh, and hit a couple of real, non-obvious failures along the way that I want to walk through — not just the happy path, because the happy path lied to me twice before it actually worked.
What You’ll Need
- SSH access enabled on your cPanel account (cPanel → Security → SSH Access)
- Your actual cPanel username — mine turned out to be different from my local Mac username, which tripped me up for a minute. Check cPanel’s General Information page, not your terminal prompt.
- Your domain’s document root (usually
~/public_html, but differs for addon domains — more on that below) - About 15 minutes if nothing goes wrong, longer if you hit the same two snags I did
Step 1: Connect via SSH
First surprise: Namecheap shared hosting uses a non-standard SSH port, 21098, not the usual 22. I didn’t know that going in.
bash
ssh yourcpanelusername@your-server-ip -p 21098
You’ll find your server’s shared IP on the cPanel dashboard. I authenticated automatically with an SSH key I already had authorized in cPanel; if you haven’t set one up, it’ll prompt for your cPanel password instead.
Once connected, my prompt changed from mnrdsgn@Mac ~ % (my laptop) to [mnrdjmke@premium63 ~]$ (the actual server) — that’s the tell you’re really in.
Step 2: Install acme.sh
acme.sh is a pure shell-script ACME client. Unlike Certbot, it doesn’t assume you have root, which makes it a natural fit for shared hosting.
bash
curl https://get.acme.sh | sh -s email=you@example.com
This installs itself entirely inside your home directory (~/.acme.sh), adds an alias to your .bashrc, and — importantly — sets up a cron job that will handle renewals automatically going forward.
Reload your shell so the acme.sh command becomes available:
bash
source ~/.bashrc
Step 3: Confirm Your Document Root
Before issuing anything, I made sure I knew the exact folder my site serves from. For my primary domain that was simply:
bash
ls ~/public_html
— which showed a WordPress install (wp-config.php, wp-content, etc.), confirming it was the right spot.
For a second domain I fixed later on the same account (shop.clayspacebk.com, an addon domain), public_html wasn’t the answer — I had to search for it:
bash
find ~ -maxdepth 2 -iname 'shop.clayspacebk*' -type d
This turned up several lookalikes I had to rule out — ~/etc/shop.clayspacebk.com and ~/mail/shop.clayspacebk.com are cPanel’s internal config and mail folders, not the actual site. The real webroot was ~/shop.clayspacebk, no .com at the end and not nested in public_html at all. Worth double-checking rather than assuming.
Step 4: Issue the Certificate
Now the actual request, using the webroot validation method: acme.sh drops a temporary file into a .well-known/acme-challenge folder inside the site, Let’s Encrypt fetches it over plain HTTP to confirm domain ownership, no port-binding or root required.
bash
acme.sh --issue -d mnrdsgn.com -d www.mnrdsgn.com -w /home/mnrdjmke/public_html
It came back clean — Cert success, with paths to four files: the cert, the private key, the intermediate CA cert, and the full chain, all sitting in:
/home/mnrdjmke/.acme.sh/mnrdsgn.com_ecc/
Note that _ecc suffix. It doesn’t look important. It became the entire source of my second failure.
Step 5: Deploy the Certificate — Attempt One (Failed)
My first instinct was to install the cert directly via cPanel’s uapi command, feeding it the file contents:
bash
uapi SSL install_ssl domain=mnrdsgn.com \
cert="$(cat ~/.acme.sh/mnrdsgn.com_ecc/mnrdsgn.com.cer)" \
key="$(cat ~/.acme.sh/mnrdsgn.com_ecc/mnrdsgn.com.key)" \
cabundle="$(cat ~/.acme.sh/mnrdsgn.com_ecc/ca.cer)"
This failed:
errors:
- "The system could not parse the certificate because of an error: Invalid base64: must be a multiple of 4 in length.\n"
- "need \"key_algorithm\"! at /usr/local/cpanel/Cpanel/Crypt/Algorithm.pm line 96.\n"
status: 0
cPanel’s command-line UAPI tool doesn’t handle multi-line PEM content passed directly as shell arguments cleanly, no matter how carefully you quote it. Don’t fight this — there’s a purpose-built alternative.
Step 5b: Deploy the Certificate — The Right Way
acme.sh ships a built-in cPanel deploy hook that handles all the encoding correctly under the hood:
bash
acme.sh --deploy --domain mnrdsgn.com --deploy-hook cpanel_uapi
This reported success — Succcessfully deployed to mnrdsgn.com, Success. I checked the live site. Still showing the old, years-expired certificate. The deploy hook lied to me, or so it seemed.
Here’s what was actually happening: acme.sh’s --deploy command, without any extra flags, looks for the certificate in a default folder path based on domain name alone — ~/.acme.sh/mnrdsgn.com/, no _ecc suffix. My account happened to have an old, abandoned certificate sitting in exactly that folder from 2023. So the deploy hook was faithfully re-deploying the wrong, expired certificate — it just did so successfully, which made the failure much harder to spot than an error message would have been.
The actual fix was one flag:
bash
acme.sh --deploy --domain mnrdsgn.com --deploy-hook cpanel_uapi --ecc
--ecc tells acme.sh to look in the correct _ecc folder — the one where the certificate I’d actually just issued was sitting. Checking the live server afterward confirmed it:
issuer=C = US, O = Let's Encrypt, CN = YE2
notBefore=Aug 1 02:30:09 2026 GMT
notAfter=Oct 30 02:30:08 2026 GMT
If you take away one thing from this post, take away this: always include --ecc on both the --issue and --deploy steps if your acme.sh install defaults to ECC certificates (most current versions do). Skipping it doesn’t always throw an error — sometimes it just silently succeeds at doing the wrong thing.
You’ll also likely see noisy Perl warnings in the output mentioning NcCustomHooks::SSL or NameCheap::RestApiClient. These come from Namecheap’s own internal notification hooks misfiring on their end — harmless, and unrelated to whether your cert actually installed. Ignore them and look at the actual result lines instead:
Succcessfully deployed to mnrdsgn.com
Successfully deployed certificate to 1 of 1 sites via UAPI
Success
Step 6: Verify It’s Actually Live — Don’t Trust “Success”
Given what had just happened, I stopped trusting acme.sh’s own success messages and started checking the server directly instead:
bash
echo | openssl s_client -connect mnrdsgn.com:443 -servername mnrdsgn.com 2>/dev/null | openssl x509 -noout -issuer -dates
This talks to the live web server exactly as a browser would, and shows you the actual certificate being handed over — not what cPanel’s API claims was installed. This is the check that caught the stale-cert problem in the first place, and I’d recommend running it after every deploy, not just the first one. I also cross-checked against cPanel’s own certificate store with uapi SSL list_certs, which lists every cert on the account along with its issuer and expiry — useful for spotting old, forgotten certificates like the one that tripped me up.
You can also just open https://yourdomain.com in a browser and check for the padlock, though the browser sometimes caches an old warning for a minute after a fix — the openssl command is more reliable in the moment.
Step 7: Confirm Auto-Renewal Will Actually Work
Let’s Encrypt certs only last about 90 days, so renewal isn’t optional. Once a deploy succeeds with --deploy-hook cpanel_uapi, acme.sh remembers that setting inside the certificate’s own config file:
bash
cat ~/.acme.sh/mnrdsgn.com_ecc/mnrdsgn.com.conf | grep -i deployhook
Mine showed:
Le_DeployHook='cpanel_uapi,'
That means the cron job acme.sh installed back in Step 2 will automatically redeploy to cPanel on every future renewal — no manual --ecc flag needed at that point, since it’s already operating inside the correct ECC-specific config.
I didn’t want to just take that on faith either, so I force-tested the entire renewal-and-redeploy cycle right then, without waiting 90 days:
bash
acme.sh --renew -d mnrdsgn.com --ecc --force
It re-issued a fresh certificate (new serial number, new dates) and auto-deployed it via the saved hook, unattended, exactly as it’s supposed to work in September when the real renewal comes due. That was the point I actually trusted the setup.
Doing It Again for a Second Domain
I had a second site on the same account, shop.clayspacebk.com, with its own cert expiring in a couple of days. Since the tooling was already installed, fixing it was just repeating Steps 3–6 with the right webroot:
bash
acme.sh --issue -d shop.clayspacebk.com -d www.shop.clayspacebk.com -w /home/mnrdjmke/shop.clayspacebk
acme.sh --deploy --domain shop.clayspacebk.com --deploy-hook cpanel_uapi --ecc
Note I included --ecc on the deploy from the start this time — no reason to repeat the same mistake twice. Confirmed live in under two minutes, no surprises.
If you’re managing several domains on one cPanel account, acme.sh --list shows everything currently being tracked — a good way to spot old abandoned certs (like the one that caused my first failure) before they cause the same confusion for you.
Why This Works (and Why cPanel’s Wizard Doesn’t)
cPanel’s SSL/TLS Wizard is built around AutoSSL and paid reseller providers — both of which Namecheap disables on shared hosting. But cPanel’s underlying API (uapi SSL install_ssl) doesn’t actually care where a certificate came from. As long as you can prove domain ownership (the webroot HTTP challenge does that) and format the request correctly (the cpanel_uapi deploy hook does that), cPanel installs and serves it exactly the same as if you’d paid for it through their plugin.
What I’d Do Differently Next Time
- Always pass
--eccon both--issueand--deploy, every time, without exception. It’s the one flag that turned a “success” message into a silent failure when I skipped it. - Verify with
openssl s_clientafter every deploy, not just the first one — cPanel and acme.sh will both report success even when the wrong certificate got installed. - Check for old, abandoned certs before troubleshooting —
uapi SSL list_certsandacme.sh --listwould have shown me the stale 2023 cert immediately, saving a debugging detour. - Force-test renewal once, right after setup, with
--renew --force. Trusting a cron job to work correctly 60 days from now, untested, isn’t worth the risk for something this easy to verify immediately.
Summary
| Step | Command |
|---|---|
| Connect | ssh user@ip -p 21098 |
| Install acme.sh | curl https://get.acme.sh | sh -s email=you@example.com |
| Issue cert | acme.sh --issue -d domain.com -d www.domain.com -w /path/to/webroot |
| Deploy to cPanel | acme.sh --deploy --domain domain.com --deploy-hook cpanel_uapi --ecc |
| Verify | openssl s_client -connect domain.com:443 -servername domain.com | openssl x509 -noout -issuer -dates |
| Test renewal | acme.sh --renew -d domain.com --ecc --force |
Once it’s set up, this requires zero ongoing maintenance — no support tickets, no yearly fees, no calendar reminders. Just a free, auto-renewing certificate quietly doing its job in the background of a Namecheap shared hosting account that was never supposed to have free SSL in the first place.