How to Create a Staging Site for OpenCart 3 (cPanel)
A staging site is a private copy of your live store where you test changes before they touch production. The whole job is: copy the files, copy the database, then point every path, URL and database setting at the copy.
This guide covers OpenCart 3.0.x (tested on 3.0.3.9 with the Journal theme) on a cPanel server, with staging in its own cPanel account. Example values used throughout:
| Item | Live | Staging |
|---|---|---|
| Domain | demo.com | staging.demo.com |
| cPanel account | demo | stagingdemo |
| Web root | /home/demo/public_html/ | /home/stagingdemo/public_html/ |
| Storage folder | /home/demo/storage/ | /home/stagingdemo/storage/ |
| Admin folder | admin | admin |
| Database | demo_oc | stagingdemo_oc |
The golden rule: once setup is done, nothing on staging may point at a live path, the live database, live payment keys or real customer email. Most staging errors come from one leftover /home/demo/ path.
Step 1: Prepare the account, subdomain and DNS
- In WHM, create a new cPanel account for staging (e.g.
stagingdemo) withstaging.demo.comas its domain. A separate account keeps staging isolated, so it can’t read or break live files. - In cPanel → MultiPHP Manager, set the same PHP version as live (e.g. PHP 8.2). Also match the PHP extensions the live site uses (ionCube, zip, gd, curl, mbstring).
- In your DNS (e.g. Cloudflare), add one A record:
staging→ your server IP. Set it to DNS only (grey cloud) while setting up. - Make sure there is only one
stagingA record. A leftover record pointing at an old server causes random 522 errors. - In cPanel → SSL/TLS Status, run AutoSSL so https works.
Step 2: Copy the files
You need to copy two things: the web root and the storage folder. OpenCart 3 usually keeps storage outside public_html, which makes it easy to forget.
The fastest way is over SSH: zip on the live side, then unzip on staging.
cd /home/demo
zip -r /home/stagingdemo/site.zip public_html -x 'public_html/image/cache/*'
zip -r /home/stagingdemo/storage.zip storage -x 'storage/cache/*' 'storage/modification/*' 'storage/logs/*'
cd /home/stagingdemo
unzip -o site.zip && unzip -o storage.zip
rm site.zip storage.zip
Without SSH: in cPanel File Manager on the live account, compress public_html and storage, download both, upload them into /home/stagingdemo/, then extract.
Leaving out image/cache, storage/cache and storage/modification saves a lot of space, and OpenCart rebuilds them automatically. On a large catalogue, image/cache alone can be several GB.
Step 3: Copy the database
- In the staging cPanel → MySQL Databases, create a database (e.g.
stagingdemo_oc) and a user. Add the user to the database with ALL PRIVILEGES. - Export the live database. For large stores use SSH, because phpMyAdmin often times out:
mysqldump -u LIVE_DB_USER -p demo_oc > /home/stagingdemo/live.sql - Import it into the staging database:
mysql -u stagingdemo_user -p stagingdemo_oc < /home/stagingdemo/live.sql rm /home/stagingdemo/live.sql
Without SSH: use phpMyAdmin → Export (Quick, SQL) on the live account, then Import on staging. Compress the file as .gz if it’s large.
Never point staging at the live database. Every change you make on staging would change your real products, orders and customers.
Step 4: Edit both config.php files
OpenCart has two config files, and both must be updated:
- Replace
/home/demo/with/home/stagingdemo/in the paths. - Replace
demo.comwithstaging.demo.comin the URLs. - Change the DB lines to the staging database.
Front end: /home/stagingdemo/public_html/config.php
define('HTTP_SERVER', 'https://staging.demo.com/');
define('HTTPS_SERVER', 'https://staging.demo.com/');
define('DIR_APPLICATION', '/home/stagingdemo/public_html/catalog/');
define('DIR_SYSTEM', '/home/stagingdemo/public_html/system/');
define('DIR_IMAGE', '/home/stagingdemo/public_html/image/');
define('DIR_STORAGE', '/home/stagingdemo/storage/');
// DIR_LANGUAGE, DIR_CACHE, etc. are built from these, leave them
define('DB_HOSTNAME', 'localhost');
define('DB_USERNAME', 'stagingdemo_user');
define('DB_PASSWORD', 'your-staging-db-password');
define('DB_DATABASE', 'stagingdemo_oc');
define('DB_PREFIX', 'oc_'); // same as live
Admin: /home/stagingdemo/public_html/admin/config.php (or your renamed admin folder)
define('HTTP_SERVER', 'https://staging.demo.com/admin/');
define('HTTP_CATALOG', 'https://staging.demo.com/');
define('HTTPS_SERVER', 'https://staging.demo.com/admin/');
define('HTTPS_CATALOG', 'https://staging.demo.com/');
define('DIR_APPLICATION', '/home/stagingdemo/public_html/admin/');
define('DIR_SYSTEM', '/home/stagingdemo/public_html/system/');
define('DIR_IMAGE', '/home/stagingdemo/public_html/image/');
define('DIR_STORAGE', '/home/stagingdemo/storage/');
define('DIR_CATALOG', '/home/stagingdemo/public_html/catalog/');
// same DB_ lines as the front-end config
Don’t edit index.php or system/startup.php. They build their paths from these constants, so fixing the config fixes them.
Next, search for any live paths left over in custom code, .htaccess files or extensions:
grep -rln "/home/demo/" /home/stagingdemo/public_html /home/stagingdemo/storage 2>/dev/null
grep -rn "demo.com" /home/stagingdemo/public_html/.htaccess
Make the same replacement in every file the search lists. In .htaccess, update any RewriteCond %{HTTP_HOST} rule or redirect that forces demo.com, otherwise staging will redirect to your live site.
Step 5: Fix ownership and permissions
Files copied as root, or from another account, keep the wrong owner. PHP runs as the stagingdemo user, so it can’t write to cache, modification, session or image/cache. The most common symptom is that Refresh in Modifications silently does nothing.
Run as root (WHM → Terminal):
chown -R stagingdemo:stagingdemo /home/stagingdemo/public_html/* /home/stagingdemo/public_html/.htaccess /home/stagingdemo/storage
chown stagingdemo:nobody /home/stagingdemo/public_html
find /home/stagingdemo/public_html /home/stagingdemo/storage -type d -exec chmod 755 {} \;
find /home/stagingdemo/public_html /home/stagingdemo/storage -type f -exec chmod 644 {} \;
chmod 750 /home/stagingdemo/public_html
To check, run ls -l /home/stagingdemo/storage. Every folder should show stagingdemo stagingdemo as the owner. Never use 777.
Step 6: Log in, update settings and rebuild caches
Log in at https://staging.demo.com/admin/ with your live admin username and password. They work because the database is a copy.
- Rebuild modifications: go to Extensions → Modifications and click Refresh (the blue button, top right).
storage/modification/should now fill with folders. - Clear the theme cache: Dashboard → gear icon (top right) → refresh both the theme cache and the SASS cache.
- Clear your theme’s own cache if it has one. For Journal, use the Journal menu in the admin sidebar.
- Check store URLs if you run more than one store:
SELECT store_id, name, url, ssl FROM oc_store; - Look for live URLs saved in settings. Themes, SEO extensions and others sometimes store them:
SELECT `code`, `key` FROM oc_setting WHERE `value` LIKE '%://demo.com%';
Fix any results through the admin screens, not with SQL. Some of these values are serialized, and a raw find-and-replace can break them.
Step 7: Make staging safe
- Password-protect the whole site: cPanel → Directory Privacy →
public_html→ enable it and add a user. This keeps out Google, bots and customers. - Block indexing as a backup. Replace
robots.txtwith:User-agent: * Disallow: / - Payments: Extensions → Payments → disable live gateways or switch them to sandbox/test keys.
- Email: System → Settings → Mail → point it to a test inbox or remove the SMTP details, so customers don’t get order or newsletter emails.
- Turn off outside connections that use live credentials: Google Merchant feeds, marketplace sync, SMS, courier APIs and cPanel cron jobs.
- Remove debug lines (
error_reporting,display_errors) from config.php once everything works. For later problems, checkstorage/logs/error.loginstead.
Troubleshooting Common Errors
| Symptom | Cause | Fix |
|---|---|---|
| Cloudflare 522 | Duplicate or old A record, or server firewall | Keep one staging A record pointing to the right IP; open ports 80/443 |
Failed opening required /home/demo/... Permission denied |
config.php still has live paths | Change /home/demo/ to /home/stagingdemo/ in both config files |
| Blank HTTP 500 | Error is hidden | Temporarily add display_errors to config.php, or read cPanel → Errors / error_log |
| Admin gives 500 but the front end works | Admin config.php not updated | Edit admin/config.php (Step 4) |
Modifications Refresh does nothing, storage/modification stays empty |
Wrong file owner, or DIR_STORAGE points elsewhere | Run the Step 5 chown and check DIR_STORAGE in both configs |
| Session or cache errors | Storage folder not copied | Copy /home/demo/storage to /home/stagingdemo/storage |
| Staging redirects to demo.com | .htaccess rule or store URL | Fix the .htaccess host rules and HTTP_SERVER |
| Database connection error | Wrong DB name, user or password | Use the staging values and make sure the user is added to the DB |
| Pages load without styling | Theme cache | Refresh the theme/SASS cache and your theme’s own cache |
| “Not secure” warning | No SSL on the account | Run AutoSSL in cPanel |
Final Checklist
- One
stagingDNS record, SSL working - Files in
/home/stagingdemo/public_html/, storage in/home/stagingdemo/storage/ - Staging database imported, database user added
- Both config.php files use staging URLs,
/home/stagingdemo/paths and the staging DB grepfinds no/home/demo/paths left- Files owned by
stagingdemo - Admin login works, modifications refreshed, theme cache cleared
- Site password-protected, robots.txt blocks everything
- Payments in test mode, emails off, feeds and crons off
- Debug lines removed from config.php
Need a staging site set up for your OpenCart store? Contact us and we’ll handle it for you.

