Server Requirements
| Requirement | Minimum | Recommended | Notes |
|---|---|---|---|
| PHP | 8.1 | 8.2+ | Required |
| MySQL / MariaDB | 8.0 / 10.4 | MySQL 8.0+ | Required |
| Web Server | — | — | Nginx, Apache, or IIS (Windows Server) |
| Composer | 2.x | Latest | Optional — only needed to add/update PHP packages |
| Node.js | 18.x | 20.x LTS | Optional — only needed to rebuild frontend assets |
| npm | 9.x | Latest | Optional — bundled with Node.js |
vendor/) and compiled frontend assets (public/build/). Neither Composer nor Node.js is required to run the application.You only need them if you want to customise the codebase:
• Composer — to add, remove, or update PHP packages:
composer install• Node.js / npm — to modify and rebuild the frontend:
npm install && npm run build
Required PHP Extensions
BCMath, Ctype, cURL, DOM, Fileinfo, JSON, Mbstring, OpenSSL, PDO, PDO_MySQL, Tokenizer, XML, Zip
Most extensions are bundled with PHP by default. If the installer flags any as missing, install them using the commands below.
Ubuntu / Debian
sudo apt update
sudo apt install -y \
php8.2-bcmath \
php8.2-curl \
php8.2-dom \
php8.2-fileinfo \
php8.2-mbstring \
php8.2-mysql \
php8.2-tokenizer \
php8.2-xml \
php8.2-zip \
php8.2-opcache
sudo systemctl restart php8.2-fpm # or apache2
Replace php8.2 with your installed PHP version (e.g. php8.3). ctype, json, and openssl are built into PHP and do not need a separate package.
CentOS / RHEL / AlmaLinux
sudo dnf install -y \
php-bcmath \
php-curl \
php-dom \
php-fileinfo \
php-mbstring \
php-mysqlnd \
php-tokenizer \
php-xml \
php-zip \
php-opcache
sudo systemctl restart php-fpm
cPanel / Shared Hosting
Go to cPanel → Select PHP Version (or MultiPHP Manager), select your domain, then enable the required extensions from the list and click Save. All extensions listed above are available in cPanel's PHP selector.
Windows Server (IIS)
Install PHP via the Web Platform Installer or manually download from windows.php.net. Then enable the required extensions:
- Open your
php.inifile (usually inC:\PHP\php.inior the path shown byphp --ini). - Uncomment (remove the leading
;) each required extension:extension=bcmath extension=curl extension=fileinfo extension=mbstring extension=openssl extension=pdo_mysql extension=tokenizer extension=xml extension=zip
- Save
php.iniand restart IIS:iisreset
Alternatively, if you installed PHP via PHP Manager for IIS, open IIS Manager → select your site → PHP Manager → Enable or disable an extension, then enable each extension from the list.
Verify extensions are loaded:
php -m | findstr /i "bcmath curl fileinfo mbstring pdo_mysql xml zip"
Verify installed extensions
php -m | grep -E "bcmath|curl|dom|fileinfo|mbstring|pdo|pdo_mysql|tokenizer|xml|zip"
Installation
-
Upload files
Upload all files to your server's web root (e.g.public_html/). Thepublic/folder should be your document root. -
Create a database
Create a new MySQL database and user via cPanel, phpMyAdmin, or your host's control panel. -
Run the installer
Visithttps://yourdomain.com/installand follow the on-screen wizard to configure your environment, test the database connection, and run migrations automatically. -
Build frontend assets (optional)
The release zip ships with pre-compiled assets inpublic/build/— this step is only needed if you have modified the frontend source.npm install npm run build
-
Set permissions
The web server must be able to write to
storage/,bootstrap/cache/,public/uploads/, and.env. Run the appropriate commands for your server OS:Linux (Ubuntu / Debian / CentOS)
# storage, bootstrap/cache & uploads chown -R www-data:www-data \ /path/to/app/storage \ /path/to/app/bootstrap/cache \ /path/to/app/public/uploads chmod -R 775 \ /path/to/app/storage \ /path/to/app/bootstrap/cache \ /path/to/app/public/uploads # .env chown www-data:www-data /path/to/app/.env chmod 644 /path/to/app/.envReplace
www-datawith your web server user if different (e.g.nginx,apache). To check your web server user:ps aux | grep -E 'nginx|apache|php-fpm' | awk '{print $1}' | grep -v root | head -1macOS (local development)
# storage, bootstrap/cache & uploads sudo chown -R $(whoami):staff \ /path/to/app/storage \ /path/to/app/bootstrap/cache \ /path/to/app/public/uploads chmod -R 775 \ /path/to/app/storage \ /path/to/app/bootstrap/cache \ /path/to/app/public/uploads # .env sudo chown $(whoami):staff /path/to/app/.env chmod 644 /path/to/app/.envWindows (IIS)
# storage, bootstrap/cache & uploads icacls "C:\path\to\app\storage" /grant "IIS_IUSRS:(OI)(CI)F" /T icacls "C:\path\to\app\bootstrap\cache" /grant "IIS_IUSRS:(OI)(CI)F" /T icacls "C:\path\to\app\public\uploads" /grant "IIS_IUSRS:(OI)(CI)F" /T # .env icacls "C:\path\to\app\.env" /grant "IIS_IUSRS:F"
cPanel / Shared Hosting
Go to cPanel → File Manager, navigate to your app root:
- Right-click
storage,bootstrap/cache, andpublic/uploads→ Change Permissions → set to755or775. - Right-click
.env→ Change Permissions → set to644.
Alternatively use SSH and run the Linux commands above.
- Right-click
- Set up cron and queue — see the next section.
/admin/login. Sign in with the email and password you entered during the installer's admin account step.
Login URLs & Security
WPress Zone uses two separate login pages — one for regular users and one for administrators. Keeping them separate prevents admins from accidentally logging in via the public-facing form, and makes it straightforward to restrict admin access at the server level.
| URL | Who can sign in | Notes |
|---|---|---|
/login | Regular users only | Linked from the public landing page and footer |
/admin/login | Admins only | Not linked publicly — share only with your team |
Each endpoint validates both credentials and role. If an admin's email and password are submitted to
/login, authentication is rejected with a generic error — no information is leaked about whether the account exists or has a different role.
Restricting Admin Access by IP (recommended)
For an extra layer of protection, allow only trusted IP addresses to reach /admin/login. Example configurations:
Nginx
location /admin {
allow 203.0.113.10; # your office IP
deny all;
}
Apache (.htaccess)
<Location /admin>
Require ip 203.0.113.10
</Location>
203.0.113.10 with your actual IP address. If you are on a dynamic IP, consider using a VPN with a fixed exit IP instead.
Upload Security
The public/uploads/ directory is web-accessible, so it is important to prevent malicious files from being uploaded and executed. The following measures are applied:
| Measure | Where enforced | Details |
|---|---|---|
| Allowed file types | Server-side (controller) | Only PNG, JPG, SVG, WebP, and ICO are accepted. All other types are rejected before saving. |
| File size limit | Server-side (controller) | Logos are limited to 2 MB, favicons to 512 KB. Requests exceeding these limits are rejected. |
| Random filenames | Application | Uploaded files are saved with a random 12-character name, making them impossible to guess. |
| Script execution blocked | Web server config | PHP and other script files are blocked from executing inside public/uploads/ even if one were somehow uploaded. |
Block Script Execution in Uploads Directory (required)
Even though only image types are accepted, it is strongly recommended to configure your web server to block script execution inside public/uploads/ as a defence-in-depth measure.
Nginx — add inside your server {} block:
location ~* ^/uploads/.*\.(php|php5|phtml|pl|py|jsp|sh|cgi)$ {
deny all;
}
Apache — create public/uploads/.htaccess:
<FilesMatch "\.(php|php5|phtml|pl|py|jsp|sh|cgi)$">
Deny from all
</FilesMatch>
IIS — add a request filtering rule in your web.config:
<requestFiltering>
<fileExtensions>
<add fileExtension=".php" allowed="false" />
<add fileExtension=".pl" allowed="false" />
<add fileExtension=".py" allowed="false" />
<add fileExtension=".sh" allowed="false" />
</fileExtensions>
</requestFiltering>
Cron & Queue Setup
Laravel Scheduler (required)
WPress Zone relies on the Laravel task scheduler for uptime checks, vulnerability scans, and domain/SSL monitoring. Add a single cron entry to your server:
* * * * * cd /path/to/your/project && php artisan schedule:run >> /dev/null 2>&1
Replace /path/to/your/project with your actual installation path (e.g. /home/username/public_html).
On cPanel, go to Cron Jobs, set the interval to Every Minute (*), and paste the command above.
Scheduled Tasks
| Task | Frequency | Description |
|---|---|---|
monitor:uptime | Every minute | Checks uptime for all monitored sites |
domain:status | Every minute | Checks domain expiry and SSL certificate status (per-site checks are throttled to every 6 hours inside the command) |
vulnerabilities:populate | Daily 02:00 | Syncs latest vulnerability definitions from WPVulnerability.net |
plugin-vuln:check | Daily 03:00 | Scans plugins against vulnerability database |
theme-vuln:check | Daily 03:15 | Scans themes against vulnerability database |
core-vuln:check | Daily 03:30 | Scans WordPress core versions for known CVEs |
site-vuln:check | Daily 03:45 | Runs full vulnerability report per site |
logs:delete-old | Weekly (Sun) | Removes logs older than the user's plan retention period |
Queue Worker
Alert notifications (Slack, Telegram, Discord, email, etc.) are dispatched as queued jobs. WPress Zone supports two queue modes — choose the one that matches your hosting environment.
| Mode | Best for | Worker required |
|---|---|---|
sync (default) | Shared hosting, cPanel, low site count | No |
database | VPS, dedicated server, 50+ sites | Yes |
Option 1 — Sync / Inline (shared hosting)
Jobs run immediately during the same HTTP request. No background worker or extra process is needed. This is the default and works out of the box on any shared host.
Your .env should have (or default to):
QUEUE_CONNECTION=sync
Option 2 — Database queue (VPS / dedicated server)
Jobs are written to the jobs database table and processed by a background worker. This keeps plugin responses fast, retries failed notifications automatically, and exposes failed jobs in the System Health page.
Step 1 — Set the queue driver in .env:
QUEUE_CONNECTION=database
Step 2 — Create the jobs table (skip if already migrated):
php artisan queue:table php artisan migrate
Step 3 — Run the worker:
php artisan queue:work --sleep=3 --tries=3 --max-time=3600
Step 4 — Keep the worker alive with Supervisor (recommended on Linux VPS):
[program:wpress-zone-worker] command=php /home/username/public_html/artisan queue:work --sleep=3 --tries=3 --max-time=3600 directory=/home/username/public_html autostart=true autorestart=true stopasgroup=true killasgroup=true user=www-data numprocs=1 redirect_stderr=true stdout_logfile=/home/username/public_html/storage/logs/worker.log stopwaitsecs=3600
Install and reload Supervisor:
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start wpress-zone-worker
php artisan queue:restart
Switching between modes
You can switch modes at any time by changing QUEUE_CONNECTION in .env. If switching from database back to sync, stop the worker first. If switching to database, make sure the jobs table exists before starting the worker.
Change Admin Email & Password
Your admin email and password are set during installation. If you need to update them after going live, follow these steps.
Change your email and password
-
Log in at
https://yourdomain.com/admin/loginusing the credentials you created during installation. - Click your name in the top-right corner to open the dropdown menu, then click Edit profile.
- Update the Email field if needed.
- Scroll down to the Change Password section. Enter your current password, then enter and confirm your new password.
- Click Update Profile to save.
php artisan tinker
>>> App\Models\User::where('role', 'admin')->first()->update(['email' => 'you@yourdomain.com', 'password' => bcrypt('YourNewPassword')])
App Settings
Visit Admin → Settings to configure:
| Setting | Description |
|---|---|
| Logo | Upload a single logo or separate light & dark versions (shown in navbar, login, and invoices) |
| Favicon | Upload a favicon shown in browser tabs |
| Support Email | Shown on invoices and in Terms / Privacy pages |
| Footer Text | Short tagline or copyright text shown in emails and pages |
| SMTP / Mail | See Email (SMTP) section below |
APP_NAME in your .env file and run php artisan config:clear.
Stripe Payments
- Create a Stripe account and go to Developers → API keys.
- Copy your Publishable key and Secret key.
- In the Admin panel, go to Gateways, enable Stripe, and click Edit to paste the keys.
-
Webhook (optional but recommended) — in Stripe Dashboard go to Developers → Webhooks, add endpoint:
https://yourdomain.com/stripe/webhook
Select events:invoice.payment_succeeded,invoice.payment_failed. Copy the generated Webhook Signing Secret and paste it into the Stripe gateway settings in the Admin panel.
pk_test_ / sk_test_ keys during development and testing.
PayPal Payments
- Create or log in to your PayPal Developer account and go to My Apps & Credentials.
- Create a new app (or use an existing one) and copy the Client ID and Secret for both Sandbox and Live modes.
- In the Admin panel, go to Gateways, enable PayPal, and click Edit to paste the Client ID, Secret, and select the mode (sandbox or live).
-
Webhook (optional but recommended) — in your PayPal Developer Dashboard go to your app → Webhooks, add endpoint:
https://yourdomain.com/paypal/webhook
Select events:PAYMENT.SALE.COMPLETED,BILLING.SUBSCRIPTION.CANCELLED,BILLING.SUBSCRIPTION.EXPIRED,BILLING.SUBSCRIPTION.PAYMENT.FAILED.
Bank Transfer / Manual Payment
The Bank Transfer gateway allows customers to pay by wire transfer. Orders are placed with a pending status and the admin manually confirms payment.
- In the Admin panel, go to Gateways and enable Bank Transfer.
- Edit the gateway details to add your bank account information (IBAN, account name, bank name, reference instructions). This text is shown to the customer at checkout and included in their invoice email.
- When a customer completes a bank transfer order, go to Admin → Orders, find the order, and mark it as Paid to activate their subscription.
Email (SMTP)
WPress Zone sends emails for alert notifications, invoice delivery, and ticket replies.
SMTP credentials are set in your .env file, located in the root of your installation
(e.g. /var/www/your-app/.env). You can configure SMTP during installation or edit
the file directly on the server at any time.
Finding and editing .env
SSH into your server and open the file with any text editor:
nano /var/www/your-app/.env
Locate the MAIL_* block (it is already present — just fill in the values) and save the file. No restart is needed; changes take effect on the next request.
Gmail
Use an App Password (not your regular Gmail password). Generate one at Google Account → Security → 2-Step Verification → App passwords.
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=you@gmail.com
MAIL_PASSWORD=your_app_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=you@gmail.com
MAIL_FROM_NAME="${APP_NAME}"
Amazon SES
Create SMTP credentials in the AWS Console under SES → SMTP Settings → Create SMTP Credentials. Make sure the sending address or domain is verified in SES first.
MAIL_MAILER=smtp
MAIL_HOST=email-smtp.us-east-1.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=your_ses_smtp_username
MAIL_PASSWORD=your_ses_smtp_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="${APP_NAME}"
Replace us-east-1 with your SES region (e.g. eu-west-1).
Mailgun
Find your SMTP credentials in the Mailgun dashboard under Sending → Domain Settings → SMTP credentials.
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@mg.yourdomain.com
MAIL_PASSWORD=your_mailgun_smtp_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="${APP_NAME}"
SendGrid
Create an API key in the SendGrid dashboard with Mail Send permission. Use the literal string apikey as the username.
MAIL_MAILER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=apikey
MAIL_PASSWORD=your_sendgrid_api_key
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="${APP_NAME}"
Disabling email (log only)
To suppress all outgoing email and write messages to the log file instead (useful during testing), set:
MAIL_MAILER=log
Legal Pages (Terms & Privacy)
You can edit the Terms of Service and Privacy Policy pages directly from the admin panel — no code changes needed.
- In the Admin panel, go to Settings → Legal Pages.
- Enter your Terms of Service and Privacy Policy content. Plain text and basic HTML are supported.
- Click Save Pages.
The pages are publicly accessible at:
https://yourdomain.com/terms https://yourdomain.com/privacy
These URLs are already linked in the app footer and on the registration/checkout pages.
Plans & Pricing
Go to Admin → Plans → Add Plan, fill in the fields, and save. Plans marked Active show up on the public pricing page immediately.
| Field | Notes |
|---|---|
| Name | Shown on the pricing page (e.g. Starter, Pro, Agency) |
| Max Sites | Set to 0 for unlimited |
| Monitoring Interval | Minimum minutes between uptime checks |
| Log Retention | Days before scan and alert logs are automatically deleted |
| Alert Channels | Unchecked channels are hidden from the user's integrations page |
| Active | Controls visibility on the pricing page |
| Featured | Highlights the plan — use for your recommended tier |
Pricing entries
Each plan can have multiple pricing entries. From the plan's detail page, click Add Pricing and fill in the fields below. This is what the user sees at checkout and what maps to the Stripe price or PayPal plan ID.
| Field | Notes |
|---|---|
| Period | Monthly, Yearly, Lifetime, or Free — see sections below. |
| Price | The amount charged at checkout. |
| Currency | Must match the currency configured in your Stripe / PayPal price. |
To stop new sign-ups without touching existing subscriptions, uncheck Active on the plan — it drops off the pricing page but current subscribers are unaffected.
Free plans
To offer a free tier, create a plan with the Paid toggle off and add a pricing entry with period set to Free and price 0. Leave the Stripe and PayPal ID fields blank. Every new account is automatically placed on the first free plan found — no checkout is involved and there is no renewal.
Lifetime (LTD) plans
Set the pricing period to Lifetime. The user is charged once and never billed again. On Stripe this is processed as a one-time payment; on PayPal it creates a single-cycle billing plan. Users on a lifetime plan won't see upgrade or renewal prompts anywhere in the UI.
User Management
Search and browse accounts at Admin → Users. Click a user's name to open their profile, where you can see their connected sites, active subscription, and order history.
| Action | How | Notes |
|---|---|---|
| Suspend / Activate | Toggle the status switch | Blocks login while preserving all data. Reversible. |
| Delete | Delete button on the profile page | Permanent — removes the account, sites, logs, and tickets. |
If you might need to reinstate someone, suspend rather than delete.
Confirming a bank transfer
Bank transfer orders stay Pending until you approve them manually. Go to Admin → Orders, find the order, and click Mark as Paid — this activates the subscription.
Artisan Commands
| Command | Description |
|---|---|
monitor:uptime | Dispatch uptime check jobs for all sites |
domain:status | Check domain expiry and SSL certificate for all sites |
vulnerabilities:populate | Sync vulnerability definitions from WPVulnerability.net |
plugin-vuln:check | Check all plugins for known vulnerabilities |
theme-vuln:check | Check all themes for known vulnerabilities |
core-vuln:check | Check WordPress core versions for known CVEs |
site-vuln:check | Full vulnerability report per site |
logs:delete-old | Delete logs older than each user's plan retention period |
FAQ
Uptime checks are not running
Make sure the cron entry is added to your server and the scheduler is running. You can verify with:
php artisan schedule:list
Alerts are not being sent
Check that: (1) the queue worker is running or QUEUE_CONNECTION=sync is set, (2) the alert integration is correctly configured, (3) your plan includes the alert channel you're using.
Stripe payments not working
Verify that the Stripe gateway is enabled in Admin → Gateways and that the Publishable Key, Secret Key, and Webhook Signing Secret are correctly entered there. Stripe credentials are stored in the database, not in .env.
How do I reset the admin password?
php artisan tinker
>>> App\Models\User::where('role', 'admin')->first()->update(['password' => bcrypt('YourNewPassword')])
Site shows "Disconnected" after connecting the plugin
Ensure the API key was saved correctly in the WordPress plugin settings. Also check that your WPress Zone server URL is accessible from the WordPress site (not blocked by firewall).
Can I use this on shared hosting?
Yes. Set QUEUE_CONNECTION=sync in .env and add the cron entry via cPanel. Some features (like real-time alerts) will be slightly delayed but fully functional.
Where do I edit the Terms of Service and Privacy Policy?
Go to Admin → Settings → Legal Pages. Content is stored in the database — no file editing needed. Pages are live at /terms and /privacy.
How do I enable Bank Transfer payments?
Go to Admin → Gateways, enable Bank Transfer, and click Edit to enter your bank account details. These details are shown to customers at checkout and in their invoice email.