Installation

Upload the files, create an empty database, and answer five screens. No shell access is needed.

Before you start, have three things ready: the folder your site is served from, a new empty MySQL database with a user that has full rights on it, and the SMTP details of a mailbox on your own domain. The email step can be skipped and completed later.

1. Upload the files

If your download contains Main-Files/, first unzip the application ZIP inside that folder. The other top-level folders contain documentation, licensing guidance and samples for local use. For an application-only download, unzip it directly.

Upload everything inside the extracted application folder into your web root. The result should look like this, with app/, public/ and vendor/ at the top level:

public_html/
  app/  bin/  bootstrap/  config/  cron/  database/  lang/
  public/  resources/  storage/  vendor/
  .env.example  .htaccess  .user.ini  composer.json  composer.lock
  LICENSE.txt  README.txt  SECURITY.md  THIRD-PARTY-LICENSES.md  VERSION
  Documentation/  Licensing/  Sample-Files/

In cPanel, the File Manager can upload the ZIP and extract it in place, which is much faster than uploading thousands of files over FTP. If you upload by FTP, use binary mode and check afterwards that vendor/ arrived complete.

Documentation/, Licensing/ and Sample-Files/ are for you rather than for visitors. Nothing serves them, and you may delete them from the server once you have a copy.

Where the document root should point

Point the document root at public/ if your hosting lets you. Everything else then sits outside the served folder, which is the safest arrangement.

If you cannot change the document root, leave it at the folder you uploaded into. The included .htaccess forwards every request into public/ and denies direct access to the configuration and documentation files. This is the normal case on shared hosting and it works.

2. Make the storage folder writable

The application writes generated PDFs, logs, exports and uploaded images under storage/. Set that folder and everything in it to 0755, or 0775 if your host runs PHP under a different user than the file owner. The installer checks each folder it needs and names any that is not writable.

3. Create the database

  1. In cPanel, open MySQL Databases and create a new database.
  2. Create a new database user with a strong password.
  3. Add that user to that database with All Privileges.
  4. Write down the database name, user name and password. Shared hosting usually prefixes the first two with your account name.

The database must be empty. The installer creates the tables itself.

4. Run the installer

Open https://your-domain/install. The wizard has five steps.

StepWhat it asksWhat it does
1. RequirementsNothingRuns every check on the requirements page and refuses to continue while a hard requirement fails.
2. DatabaseHost, port, name, user, passwordConnects before accepting the values, so a typo is caught here rather than three screens later.
3. ApplicationName, address, host names, timezone, languageThe address is what verification links and QR codes are built from. Get it right, including https.
4. EmailSMTP host, port, encryption, user, password, senderCan be skipped. You can set it later on the email settings screen.
5. AdministratorName, email address, passwordWrites .env, creates the tables, seeds the roles, creates your account and closes the installer.

The installer closes behind itself. When step 5 finishes, a file called storage/installed.lock is written and every installer address answers 404 from then on. If a step fails part way through, fix the cause and open /install again: an installation that never reached an administrator account reopens the wizard, and running it again is safe.

5. First sign-in

Open /login and sign in with the account you just created. Two-factor authentication is required for it, and no authenticator is paired yet, so the next screen shows a QR code.

  1. Scan the QR code with an authenticator application, or type the secret it shows.
  2. Enter the six-digit code the application generates.
  3. Save the recovery codes. They are shown once. Each one works once, and they are the only way back into the account if you lose the authenticator.

6. Finish the setup

  1. Add the scheduled jobs. The email job is the one the product needs to function.
  2. Install an HTTPS certificate, then set FORCE_HTTPS=true and SESSION_SECURE_COOKIE=true in .env.
  3. Set your logo, colours and browser title.
  4. Check /admin/diagnostics once, so you know what a healthy report looks like.

Apache

The package includes the rewrite rules it needs. If you manage the virtual host yourself, this is the arrangement to aim for.

<VirtualHost *:443>
    ServerName certificates.example.com
    DocumentRoot /var/www/certiflow/public

    <Directory /var/www/certiflow/public>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/log/apache2/certiflow-error.log
    CustomLog /var/log/apache2/certiflow-access.log combined
</VirtualHost>

mod_rewrite must be enabled. If AllowOverride is None, the .htaccess files are ignored and every address except the home page returns 404.

Nginx

Nginx does not read .htaccess, so the rules have to be in the server block. Adjust the PHP socket to the version you run.

server {
    listen 443 ssl;
    server_name certificates.example.com;
    root /var/www/certiflow/public;
    index index.php;

    client_max_body_size 12m;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\. { deny all; }
}

With the root at public/ nothing outside it is reachable. If you must point the root at the package folder instead, add a deny rule for the application directories:

location ~ ^/(app|bin|bootstrap|config|cron|database|lang|resources|routes|storage|vendor)/ {
    deny all;
}

Keep client_max_body_size at or above REQUEST_MAX_MB, or large imports fail with an Nginx error page rather than a message from the product.

Installing from the command line instead

If you do have shell access, the same installation can be done without the browser. Copy .env.example to .env, fill in the database and application values, generate a key, then run:

php bin/migrate.php      # create the tables
php bin/seed.php         # roles, permissions and default settings
php bin/create-admin.php # create the first administrator

Write storage/installed.lock afterwards, or the wizard will still be open. The browser installer does all of this in one pass and is the supported route.