Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

Hardening for hosts

The hardening a host applies to every Exponential site, layer by layer: served files, secrets, debug output, the administration, passwords, sessions, headers and TLS, proxies, file ownership, Velocity, mail, the audit…

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/help/hosting/security/hardening-for-hosts.md

On this page

Hardening for hosts

This page is for system administrators and security staff of a host who build the server template that every Exponential site is created from. It goes through the layers of a site from the outside in, says what Exponential does by default, and names the setting or command that the host sets or checks. It follows the order of chapter 13 of the installation book, which has every detail, and the threat-by-threat view of Security hardening.

Warning

.htaccess slows down every request. When .htaccess files are enabled (AllowOverride anything but None), let alone used, Apache looks for them and reads and evaluates them on every request, in every directory of the path, and page loading times go up drastically (measured). The only real fix is to switch the feature off for the virtual host: AllowOverride None in the HTTPS virtual host, and the rules in the virtual host's own configuration as mod_rewrite rules. That is far faster in production speed tests and well worth the time it takes to optimize a production configuration. Switch over right away: copy the rules of the example HTTPS virtual host into your virtual host (with the static cache, also staticcache_offload.conf), set AllowOverride None, run apachectl configtest, reload Apache and test the site. The same rules for other servers: nginx, LiteSpeed, Caddy, IIS, Velocity (built in); where virtual host rules go in a control panel: Plesk, cPanel, DirectAdmin, ISPConfig. On LiteSpeed or OpenLiteSpeed, the server's own page cache can also answer anonymous visitors and crawlers without PHP: Page caching with LiteSpeed and OpenLiteSpeed.

Note

Settings are changed in overrides, never in the shipped files: settings/override/<file>.ini.append.php for the whole installation, settings/siteaccess/<siteaccess>/<file>.ini.append.php for one siteaccess. After a change, clear the INI cache (php bin/php/ezcache.php --clear-tag=ini), and under Velocity run exp:velocity deploy (chapter 13).

Contents

  1. The principles
  2. Served files
  3. Secrets
  4. Debug output
  5. Administration, passwords and sessions
  6. Headers, TLS and proxies
  7. Files, users and processes
  8. Watching
  9. A shield in front
  10. Updates
  11. Server-template checklist

The principles

Security hardening measures Exponential against the OpenBSD way of working: secure by default, least privilege, privilege separation, minimal attack surface, audit, and fixing forward. For a host that means one rule above all: do not loosen a default to make something work; find the cause. A support ticket that ends with "we switched the rewrite rule off" has opened a hole on that site.

Served files

The document root of Exponential is the installation root, so settings/, var/, vendor/ and the SQLite database lie inside it. Safety comes from routing: only a fixed list of asset paths is served as files, everything else goes to index.php (13.2).

Web serverWhat the host does
Apache, LiteSpeed with .htaccesscp .htaccess_root .htaccess or ./console exp:setup:htaccess --apply; compare after every upgrade and merge new rules (./console exp:setup:htaccess prints the diff and exits 1 while the file differs, a drift check for monitoring)
nginxthe derived server block of 8.6, with the dot-file and script rules ahead of every static location
Velocitynothing; FollowSymlinks=disabled (the default) also refuses links out of the root
anynever a generic "serve the file if it exists" configuration

Check from outside after every change of the web server's configuration:

for p in /.git/config /.env /settings/site.ini /composer.json /kernel/classes/ezini.php /var/log/error.log; do
  printf '%-32s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://example.com$p)"
done

Each line must show 404, or 200 with an Exponential page and never the file's content. Web server details: Choosing a web server and Chapter 8.

Secrets

settings/override/ holds the database password (site.ini.append.php), the audit signing and pseudonym keys (audit.ini.append.php), the site secret of the mail preferences (mailpreferences.ini.append.php, which also keys the session password stamp) and, when set, Velocity's dashboard token (13.3).

  • Mode 0640 or stricter, never in version control, never in a public place.
  • Back it up as a secret, encrypted, with the database (Backups: what to back up).
  • Copy it with every move of the site. A copy without mailpreferences.ini.append.php generates a new site secret: old unsubscribe links stop matching and every user is signed out once.
  • Remove kickstart.ini and var/log/initial-admin-password after provisioning (First sign-in and default passwords).

config.php in the installation root can hold the cluster database password (CLUSTER_STORAGE_PASS). Treat it like settings/override/:

  • Mode 0640 with the web server's group (chmod 0640 config.php); later edits with exp:config:set keep that mode. Setting a secret in a config.php every user can read prints a WARN: line.
  • Never in version control: /config.php in the project's .gitignore (the Exponential repository tracks only config.php-RECOMMENDED).
  • The console masks the values of names with PASS, SECRET, KEY, TOKEN, CREDENTIAL or PWD as ********; --show-secrets shows them, so do not paste such output into a ticket.
  • exp:setup:* and exp:config:* back up every file they replace or edit to var/backup/setup/<time>/ or var/backup/config/<time>/ (directories 0700, never deleted by the commands). Include var/backup/ in what you protect and clean it up (Setting up .htaccess and config.php from the console).

Debug output

[DebugSettings] DebugOutput=disabled and [TemplateSettings] Debug=disabled ship off. Keep them off. When support must debug a live site, limit it to the support desk's address and remove the entry again:

# settings/override/site.ini.append.php
[DebugSettings]
DebugOutput=enabled
DebugByIP=enabled
DebugIPList[]
DebugIPList[]=203.0.113.10

In PHP-FPM's php.ini or pool: display_errors=Off, log_errors=On, expose_php=Off (13.4). Behind a proxy, DebugByIP is only as reliable as TrustedProxies[] (see TLS and HTTPS).

Administration, passwords and sessions

TopicDefaultHost's part
Administration siteaccessRequireUserLogin=trueits own host name, network limits: Restricting the administration
Setup wizardclosed on an installed site; CheckValidity=false written by the installerkeep [SetupSettings] AllowWizardOnInstalledSite=disabled
PasswordsMinPasswordLength=10, WeakPasswordCheck=enabled, bcryptoffer a policy such as MinPasswordLength=15, ForbidLogin, ForbidCurrentPassword (13.6)
LockoutMaxNumberOfFailedLogin=0 (off)decide with the customer; a lockout also lets anyone lock a known account
Two-step sign-inextension required by composer.json and activated by the installers; Enforce2FA=disabledrecommend Enforce2FA for administrators: Two-step sign-in
Session cookieHttpOnly, SameSite=Lax, CookieSecure=autoCookieSecure=true on HTTPS-only sites (13.7)
Form tokensezformtoken activekeep it in ActiveExtensions[] (13.8)
Configuration from GitManagedByDeployment=disabledenabled on production administration siteaccesses when settings and templates come from version control

A password change signs out every other session of that user ([PasswordSettings] EndOtherSessions=enabled). Keep it on: it is what makes a password change useful after an account was taken over.

Headers, TLS and proxies

Five security headers go out on every page (X-Content-Type-Options, X-Frame-Options, Content-Security-Policy: frame-ancestors 'self', Referrer-Policy, Permissions-Policy); HSTS is off until it is switched on. Apache and nginx add nothing to static files by themselves. The host's part:

  • HTTPS on every name, HTTP redirected, HSTS raised once everything works: TLS and HTTPS.
  • The full policy (script-src, style-src) is report-only on the administration as shipped: Content Security Policy.
  • Behind a load balancer, CDN or panel proxy: list exactly your proxies in site.ini [HTTPHeaderSettings] TrustedProxies[] (Velocity: Q.webserver.proxy.trusted), and make sure the application server cannot be reached around them (13.10).

Files, users and processes

"If the PHP process can change only var/ and settings/, and the web server refuses to run anything below the root, a flaw in an extension cannot plant code" (13.11).

  • One user writes: PHP-FPM pool, Velocity workers, cronjobs and console scripts run as the site's user.
  • The code (kernel/, lib/, vendor/, index.php) belongs to another user and is read-only to the site's user.
  • No world-writable files. The shipped [FileSettings] values are 0777 and 0666; set the hardened ones:
# settings/override/site.ini.append.php
[FileSettings]
StorageDirPermissions=0770
StorageFilePermissions=0660
TemporaryPermissions=0770
LogFilePermissions=0660
  • TemporaryDir ships as /tmp/, shared with every user of the machine; give each site one of its own below var/.
  • Velocity: [ServerSettings] Host=127.0.0.1 when a proxy is in front, User and Group set to the site's user, AllowRootWorkers=disabled, dashboard token and control panel password set or the views kept local (13.12).

How this scales to many customers: Per-customer users and pools and Writable directories and permissions.

Watching

Hardening without watching only delays the moment you learn of an attack.

SourceWhat it recordsHost's page
var/log/security.logthe request classifier's verdicts: probes, traversal, injection, attack tools, 404 burstsSecurity log
the audit trailsign-ins, role and setting changes, removals, commands; hash-chainedAudit trail
var/log/auth.logevery step of two-step sign-in (never a code or a secret)Two-step sign-in
var/log/csp.logContent Security Policy violationsContent Security Policy

Run the frequent cronjob group so the audit's alert rules run, forward the trail off the machine with a sink, and read security.log weekly (Security hardening).

A shield in front

request-shield, a small web application firewall for PHP by the CJW Network, refuses scanners and floods before the kernel starts. It is recommended for every site that can be reached from the internet and is not part of Exponential's composer.json. A host can offer it as part of a managed plan: request-shield and request rules.

Updates

Only the 6.0 line receives security fixes, published as the next 6.0.x release. Follow releases, update a copy first, check file consistency after every update, and keep PHP, the database server, the operating system and Velocity's engine current as well: Security updates and disclosure.

Server-template checklist

  • To do: Rewrite rules: a current .htaccess_root copy, the derived nginx block, or Velocity; the outside check returns no file.
  • To do: SOAP only where a customer needs it: index_soap.php let through (it was soap.php before 6.0.15) and limited to the callers' addresses; soap.php refused once nothing calls it (the SOAP entry script is now index_soap.php).
  • To do: settings/override/ mode 0640 or stricter, out of version control, backed up encrypted.
  • To do: config.php mode 0640, out of version control; var/backup/ protected and cleaned.
  • To do: Provisioning removes kickstart.ini and var/log/initial-admin-password, and never leaves a known password.
  • To do: DebugOutput=disabled; PHP display_errors=Off, expose_php=Off.
  • To do: Administration on its own host name, restricted by network, VPN or the shield's browser check.
  • To do: CheckValidity=false, AllowWizardOnInstalledSite=disabled.
  • To do: ezformtoken active; EndOtherSessions=enabled.
  • To do: HTTPS on every name, HTTP redirected, CookieSecure=true, HSTS raised once stable.
  • To do: TrustedProxies[] lists exactly your proxies, or stays at the loopback default.
  • To do: One operating-system user per site; code read-only to it; hardened [FileSettings]; private TemporaryDir.
  • To do: Velocity bound to 127.0.0.1 behind a proxy; workers never root.
  • To do: frequent cronjob group running as the site's user; audit alert recipients checked; a sink configured.
  • To do: An update routine with named owners.

The setting-by-setting go-live list is 13.16; the recurring review is the Security review checklist.

Demo mode stays off. EXP_DEMO_MODE=1 installs and keeps a known administrator password; set it only for a public demo's own pool, virtual host or unit, never server-wide or in a template, and alert on demo/mode lines in the security.log of customer sites. Demo mode for hosts.

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x