# Runtime hardening, uploads, and backups

## HTTP and sessions

- The front controller sends `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`, and a nonce-based Content Security Policy. The script policy has no `unsafe-eval`; inline scripts receive a per-response nonce. The stylesheet policy permits inline styles used by the current storefront customizer.
- HSTS is sent only for requests identified as HTTPS. When TLS terminates at a reverse proxy, forward `X-Forwarded-Proto: https` only from a trusted proxy.
- Session cookies are HttpOnly, SameSite=Lax, and Secure on HTTPS. Strict mode and cookie-only sessions are enabled. The configurable idle timeout defaults to 30 minutes and has a 60-second minimum.
- The router applies database-backed IP limits to login, registration, checkout, and payment actions. Login also retains the existing failed-credential lockout. Expired rate-limit rows are purged by daily cron.
- Application exceptions are logged under `storage/logs/app.log`; production responses use the translated friendly error pages.

## Apache and upload files

The root and `public/.htaccess` deny application source/config/storage/database paths, hidden files, SQL dumps, and logs. `public/uploads/.htaccess` disables CGI/PHP handlers and denies script extensions. Keep Apache `AllowOverride` enabled for these protections; when using another web server, configure equivalent deny and no-execute rules there.

Product, logo, and theme image uploads require PHP GD. The pipeline validates the decoded image, rejects images over 30 megapixels, writes a main image no larger than 1200 px and a 400 px thumbnail, and uses WebP when the installed GD build supports it (JPEG otherwise). Storefront listing images use the thumbnail where one exists and are lazy-loaded. OG images keep their existing 1200 × 630 crop/resize flow.

## Static assets and database indexes

CSS and JavaScript URLs include each file's modification time as a version query; Apache gives static assets long-lived expiry headers. `mod_deflate` compresses text responses. The schema/migrations include product indexes for `(store_id, slug)` and `(store_id, status)`, orders `(store_id, created_at)`, and a unique domain index.

## Daily job and backups

Configure `CRON_TOKEN` with a long random secret. Web requests to cron entrypoints must use POST and `X-Cron-Token`; tokens are compared with `hash_equals`. The hosting scheduler can run the PHP scripts locally from CLI.

Run `public/cron/daily.php` daily to process subscription/account work, notifications, expired limiter cleanup, and due domain checks.

Run `public/cron/backup.php` daily (or on the desired backup schedule). It writes a ZIP under `storage/backups/` containing a SQL schema/data dump and the `public/uploads/` tree, then keeps the newest seven ZIPs. Backup storage is private and is not web-downloadable. The database dump contains sensitive customer/account data; restrict filesystem access and copy backups to an encrypted offsite destination according to the operator's retention policy. The application does not implement offsite replication or encryption-at-rest for the backup archive.

## Verification

Run `php -d extension=php_gd.dll tests/domains_hardening_test.php` on the bundled XAMPP PHP to exercise the image pipeline on installations where GD is available but disabled by default. It runs against an isolated temporary MySQL database and includes host-header routing, status downgrade, rate limiting, headers/CSP, session settings and idle expiry, image resizing/WebP output, upload-handler configuration, SQL backup archive contents, seven-backup retention, and schema index checks.
