Configuration reference
Every setting the application reads from the .env file, what it means, and what may safely go in it.
Configuration lives in one file called .env in the package root. The
installer writes it for you from .env.example, keeping the comments. You can
edit it afterwards with any text editor or the File Manager in your control panel.
How the file works
- One
KEY=valueper line. No spaces around the=. - Quote a value that contains a space or a
#:DB_PASS="pa ss#word". - Lines starting with
#are comments. - Changes take effect on the next request. Nothing needs restarting.
trueandfalseare the boolean values.1and0are not.
Never let .env be downloadable. It holds your database
password. The included .htaccess denies it, and pointing the document root at
public/ puts it out of reach entirely. If you run Nginx with the root at the
package folder, add the deny rules shown in installation.
Some settings are owned by the application instead. The sender address
and the SMTP connection can be saved on the email settings screen, and what is saved there
takes precedence over the MAIL_* keys below. Branding, translations and the
interface language a user picks are also stored in the database, not here.
The keys
Application
| Key | Default | Meaning | Safe values |
|---|---|---|---|
DEMO_PREVIEW_IFRAME | false | Allows only the CodeCanyon preview origin to frame an explicitly enabled HTTPS demo. Demo cookies use secure partitioned storage; ordinary installations keep deny-framing and SameSite=Lax. | false for ordinary installations. true requires APP_ENV=demo, PUBLIC_DEMO_MODE=true, trusted HTTPS and owned demo provisioning. Browsers without partitioned-cookie support can open a separate trial using the new-tab link. |
APP_NAME | CertiFlow | Display name of the installation, used in the browser title and in email. | Any text up to 120 characters. Quote it if it contains spaces. |
APP_ENV | production | Deployment mode. Production hides error detail from visitors. | production on an ordinary live site, local while developing, demo only in a separately provisioned demo deployment. |
PUBLIC_DEMO_MODE | false | Enables explicitly selected demo-owned runtime contexts. Ordinary installations keep their full functionality with this disabled. | false in the buyer installation. true requires APP_ENV=demo and separately provisioned demo database and private storage ownership; it does not provision data by itself. |
APP_DEBUG | false | Shows error detail in the browser. Never enable it on a live site. | false in production, true only while diagnosing on a private copy. |
APP_URL | empty | Public base address. Verification links and QR codes are built from it, so a wrong value produces certificates nobody can check. | Full address with scheme and no trailing slash, for example https://certificates.example.com. |
ALLOWED_HOSTS | localhost,127.0.0.1 | Host names this installation answers on. A request arriving under any other host is refused, which stops host-header attacks. | Comma-separated host names without ports. Include the www. form if the site answers on it. |
FORCE_HTTPS | derived from the scheme in APP_URL | Redirects every plain HTTP request to HTTPS. | true once a certificate is installed. Leave it false until then, or the site becomes unreachable. |
APP_TIMEZONE | UTC | Timezone for stored and displayed dates and times. | Any PHP timezone identifier, for example Europe/Berlin or Asia/Ulaanbaatar. Changing it later does not rewrite existing rows. |
APP_LOCALE | en | Interface language for visitors who have not chosen one. | en or mn. Both ship complete. |
APP_LOCALE_FROM_BROWSER | true | Use the browser language before APP_LOCALE when no account or session language was chosen. | false in new installations so the installer language wins; true for browser negotiation. Omitted on older installations means true for compatibility. |
APP_FALLBACK_LOCALE | en | Language a missing translation key is served from, with the gap written to the log. Not present in the shipped example file; add it only to change the fallback. | en or mn. |
APP_KEY | empty | Encryption key for SMTP credentials, two-factor and webhook secrets, and the optional Gumroad licence/cache. The installer generates it. Rotation makes all of these unreadable. | At least 32 characters. The installer writes base64: followed by 32 random bytes. |
PUBLIC_CERTIFICATE_INDEXING | false | Allows search engines to index public certificate pages. | false keeps certificate pages out of search results, which is the privacy-safe default. |
GUMROAD_PRODUCT_ID | empty | Optional update/support licence verification for the exact Gumroad product. Certificate operations remain available without it. | Exact product ID from Gumroad. Empty disables checks. Licence keys are entered on System diagnostics and stored encrypted. |
Database
| Key | Default | Meaning | Safe values |
|---|---|---|---|
DB_HOST | localhost | Database server address. | localhost or 127.0.0.1 on shared hosting, otherwise the host your provider gives you. |
DB_PORT | 3306 | Database server port. | 3306 unless your provider says otherwise. |
DB_NAME | empty | Name of the database. It must exist and be empty before installation. | Up to 64 characters. Shared hosting often prefixes it with your account name. |
DB_USER | empty | Database user with full rights on that database. | Up to 64 characters. |
DB_PASS | empty | Password for that user. | Any text. The installer quotes it, so spaces and # survive. |
DB_CHARSET | utf8mb4 | Connection character set. | Leave it at utf8mb4. Anything narrower breaks non-Latin names. |
Sessions
| Key | Default | Meaning | Safe values |
|---|---|---|---|
SESSION_NAME | certificate_verification_session | Name of the session cookie. | Letters, digits and underscores. Change it only to run two installations on one host name. |
SESSION_LIFETIME | 30 | Minutes of inactivity before a signed-in user is logged out. | 15 to 60. Shorter is safer on shared computers. |
SESSION_ABSOLUTE_LIFETIME | 480 | Minutes a session may live even while in use, after which signing in again is required. | 240 to 720. It must be larger than SESSION_LIFETIME. |
SESSION_SECURE_COOKIE | true when APP_ENV is production | Sends the session cookie over HTTPS only. | true once HTTPS works. On plain HTTP it stops sign-in from working at all. |
| Key | Default | Meaning | Safe values |
|---|---|---|---|
MAIL_HOST | empty | SMTP server. The administration screen overrides this once SMTP is saved there. | Host name of your mail provider. Empty means the guarded PHP mail fallback. |
MAIL_PORT | 587 | SMTP port. | 587 with tls, or 465 with ssl. |
MAIL_USERNAME | empty | SMTP user name. | Usually the full mailbox address. |
MAIL_PASSWORD | empty | SMTP password. Prefer the administration screen, which stores it encrypted in the database. | Any text. Quote it if it contains spaces. |
MAIL_ENCRYPTION | tls | Transport encryption. | tls or ssl. Never send mail unencrypted. |
MAIL_FROM_ADDRESS | empty | Sender address on outgoing mail. | A mailbox on your own domain. Addresses on domains you do not control are rejected by receiving servers. |
MAIL_FROM_NAME | the organisation name from the active locale | Sender display name. | Your organisation name. |
Authentication
| Key | Default | Meaning | Safe values |
|---|---|---|---|
TOTP_ISSUER | the application name from the active locale | Label an authenticator app shows beside the account. | Your organisation or product name. |
LOGIN_MAX_ATTEMPTS | 5 | Failed sign-in attempts per account before it is locked out temporarily. | 3 to 10. |
LOGIN_IP_MAX_ATTEMPTS | 25 | Failed sign-in attempts from one address before that address is locked out. | 20 to 50. Set it well above the per-account limit so one office does not lock itself out. |
LOGIN_LOCK_MINUTES | 15 | Length of a sign-in lockout. | 10 to 30. |
TWO_FACTOR_MAX_ATTEMPTS | 5 | Wrong authenticator codes before the second step locks. | 3 to 10. |
TWO_FACTOR_LOCK_MINUTES | 10 | Length of that lockout. | 5 to 30. |
TWO_FACTOR_PENDING_MINUTES | 10 | Minutes allowed between a correct password and a correct code before the attempt expires. | 5 to 15. Allow enough time to pair an authenticator on first sign-in. |
ACTIVATION_MAX_ATTEMPTS | 5 | Failed attempts on one activation link before it locks. | 3 to 10. |
ACTIVATION_LOCK_MINUTES | 15 | Length of an activation lockout. | 10 to 30. |
PASSWORD_RESET_MAX_ATTEMPTS | 5 | Failed attempts on one reset link before it locks. | 3 to 10. |
PASSWORD_RESET_LOCK_MINUTES | 15 | Length of a reset lockout. | 10 to 30. |
API
| Key | Default | Meaning | Safe values |
|---|---|---|---|
API_AUTH_MAX_ATTEMPTS | 30 | Malformed or invalid Bearer-token attempts allowed from one address before temporary refusal. | 20 to 60. |
API_AUTH_LOCK_MINUTES | 10 | Window used for invalid API authentication rate limiting. | 5 to 30 minutes. |
API_IDEMPOTENCY_TTL_HOURS | 168 | How long completed write responses remain available for safe retry replay. | At least as long as the external client may retry, normally 168 hours. |
WEBHOOK_TIMEOUT_SECONDS | 10 | Total connection and response timeout for one webhook attempt. | 10 is recommended; allowed runtime range is 1 to 30 seconds. |
WEBHOOK_MAX_ATTEMPTS | 8 | Webhook attempts before a delivery is marked failed for manual retry. | Normally 8; allowed runtime range is 1 to 25. |
WEBHOOK_ALLOW_PRIVATE_URLS | false | Allows HTTP and private network webhook endpoints only for isolated local testing. | Keep false. It is ignored unless APP_ENV=local. |
Public verification
| Key | Default | Meaning | Safe values |
|---|---|---|---|
PUBLIC_LOOKUP_MAX_ATTEMPTS | 20 | Verification-form submissions allowed from one address per window. It is what stops a stranger from guessing certificate numbers. | 10 to 40. Lower it if you see scripted lookups in the log. |
PUBLIC_TOKEN_MAX_ATTEMPTS | 120 | Direct certificate-link or QR opens allowed from one address per window. | 60 to 200. Keep it well above the form limit: one certificate page loads several times legitimately. |
PUBLIC_PDF_MAX_ATTEMPTS | 30 | Public PDF downloads allowed from one address per window. | 20 to 60. |
Limits
| Key | Default | Meaning | Safe values |
|---|---|---|---|
EMAIL_MAX_ATTEMPTS | 5 | Delivery attempts for one queued message before it is marked failed. | 3 to 10. |
UPLOAD_MAX_MB | 10 | Largest accepted upload, for import files and images. | 5 to 20. It must not exceed the upload_max_filesize your host allows. |
IMAGE_MAX_DIMENSION | 12000 | Largest accepted width or height of an uploaded image, in pixels. | 4000 to 12000. Certificate backgrounds are usually well under it. |
IMAGE_MAX_PIXELS | 40000000 | Largest accepted total pixel count, which stops decompression-bomb images. | 20000000 to 40000000. |
IMPORT_MAX_ROWS | 5000 | Largest accepted number of data rows in one import file. | 1000 to 5000. Split larger lists into several files. |
IMPORT_XLSX_MAX_UNCOMPRESSED_MB | 50 | Largest uncompressed size of an .xlsx upload, which stops zip bombs. | 20 to 100. |
REQUEST_MAX_MB | 12 | Largest accepted request body. | Two megabytes above UPLOAD_MAX_MB, and no larger than the post_max_size your host allows. |
EXPORT_FILE_LIFETIME_HOURS | 24 | Hours a generated export file stays on disk before the cleanup job deletes it. | 6 to 48. |
Audit
| Key | Default | Meaning | Safe values |
|---|---|---|---|
AUDIT_ARCHIVE_ENABLED | true | Archives completed calendar years of audit log out of the database into compressed files. | true unless your auditor requires every row to stay in the database. |
AUDIT_ARCHIVE_CHECK_INTERVAL_HOURS | 24 | Hours between archive checks, which run inside the email-queue job. | 12 to 48. |
The three settings that break an installation
| Setting | What goes wrong |
|---|---|
APP_URL | Verification links and QR codes are built from it. A wrong value produces certificates whose links go nowhere, including on PDFs already delivered. |
ALLOWED_HOSTS | A request under a host name that is not listed is refused with an explanation. Add every name the site answers on, including the www. form. |
SESSION_SECURE_COOKIE | true on a site still served over plain HTTP means the browser never returns the session cookie, so sign-in appears to do nothing. Enable it together with HTTPS. |
Changing the application key
APP_KEY encrypts SMTP credentials, enrolled two-factor and webhook signing
secrets, and the optional Gumroad licence/cache. Rotation makes them unreadable. Plan to
re-enter SMTP credentials, re-enrol affected users, replace webhook secrets with each
receiver, and remove/re-enter the licence from System diagnostics. Certificates and
public verification remain available.
Optional Gumroad licence
Set GUMROAD_PRODUCT_ID to the exact product ID in your server environment.
A System Admin can then enter the purchased licence key on System diagnostics.
The key is encrypted and never shown again. Leave the input blank to check the saved key;
use the confirmed Remove action to clear it.
Checks verify the configured product and reject refunds, disputes, chargebacks and ended subscriptions. Results are cached for 24 hours, capped by any subscription cancellation deadline. Network failures preserve the last definitive result with a stale notice and a five-minute retry delay. Only explicit checks contact Gumroad; opening the page does not. This optional status covers update/support entitlement. Certificate issuance, imports, PDF generation and public verification work in every licence state.