A freshly installed server rarely ships in a secure state: leftover sample files, chatty error pages, wide-open PHP functions and generous file permissions are the rule rather than the exception out of the box. This is exactly where server hardening comes in -- the systematic reduction of the attack surface before an attacker exploits it. That the effort pays off is shown by the OWASP Top 10: in the Security Misconfiguration category, around 90 percent of tested applications were checked for some form of misconfiguration, with over 208,000 recorded weakness occurrences (OWASP Top 10 2021). This guide works through it layer by layer: PHP, web server, file permissions, security headers and access -- and shows how ongoing security updates sustain that hardening over time.
Why Hardening Is Not a One-Time Project
Hardening describes the principle of allowing a system only what it actually needs for its task -- and switching off everything else. This is not a single measure but an attitude that runs through every layer: from the operating system through the web server into the shop's PHP configuration. Its opposite is misconfiguration, and it is more common than many assume. The Verizon Data Breach Investigations Report attributes 68 percent of analyzed breaches to a non-malicious human element -- including misconfigurations and accidental exposures (Verizon DBIR 2024).
At the same time, the window in which a known gap may stay unpatched keeps shrinking. The average time from a vulnerability's disclosure to its exploitation fell to around five days in 2023 (Mandiant). In its latest report, the German BSI observes an average of 119 new vulnerabilities per day, a 24 percent increase over the previous year (BSI report 2025). Hardening buys time: a hardened server lets a single open gap escalate into a full breach less often. Every Shopware maintenance routine carries this baseline attitude into day-to-day operation.
Defense in depth rather than a single wall
Layer 1: Configure PHP Securely
PHP is the heart of almost every shop, and its default configuration is geared toward compatibility, not security. The OWASP PHP Configuration Cheat Sheet describes a set of directives that should be adjusted in production (OWASP PHP Configuration Cheat Sheet). Two of the most important concern information disclosure: expose_php = Off prevents PHP from revealing its version in every HTTP header, and display_errors = Off ensures that error messages with paths, database names or stack traces are not served to visitors but only written to the log.
Beyond that, it is worth restricting dangerous functions. disable_functions deactivates system calls such as exec, system or shell_exec, which a shop rarely needs in normal operation but which an attacker likes to abuse after a breach (OWASP PHP Configuration Cheat Sheet). open_basedir confines PHP file operations to the shop directory, and allow_url_fopen = Off together with allow_url_include = Off make it harder for a local weakness to turn into remote code execution. Which functions can be switched off safely depends on the specific shop and is verified in a staging environment before the change goes live.
; Minimize information disclosure
expose_php = Off
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/shop-error.log
; Confine file operations and block URL includes
open_basedir = /var/www/shop:/tmp
allow_url_fopen = Off
allow_url_include = Off
; Disable risky functions (review per shop)
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
; Secure sessions
session.cookie_httponly = On
session.cookie_secure = On
session.use_strict_mode = OnA current PHP version as the foundation
Layer 2: Harden the Web Server and TLS
The web server -- whether nginx or Apache -- is the first software that touches every request. Here too the rule applies: whatever is not needed should be switched off. Server tokens that reveal the version and module list can be reduced (server_tokens off or ServerTokens Prod). Automatic directory listing (autoindex) should be disabled so that no file and backup overviews are exposed. Sensitive paths such as .git, .env or configuration files are consistently excluded from being served.
A central building block is transport encryption. RFC 8996 has officially deprecated TLS 1.0 and TLS 1.1; modern servers should offer only TLS 1.2 and TLS 1.3 (RFC 8996). The Mozilla Server Side TLS recommendation serves as a practical template: the Intermediate profile covers most shops with TLS 1.2 and 1.3, while the Modern profile relies exclusively on TLS 1.3 (Mozilla Server Side TLS). The right choice of protocols and cipher suites can be kept in view through ongoing monitoring and is reviewed regularly.
# Reduce version and module info
server_tokens off;
autoindex off;
# Only modern TLS versions (Mozilla Intermediate)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
# Do not serve sensitive paths
location ~ /\.(?!well-known) { deny all; }
location ~* (composer\.json|\.env|\.git) { deny all; }
# Upload directory: no PHP execution
location ~* /(media|files)/.*\.php$ { deny all; }Layer 3: File Permissions on a Least-Privilege Basis
File permissions decide who may do what on the server -- and are, in experience, one of the most frequently over-generous spots. The CIS Benchmarks recommend the principle of least privilege: every process and identity receives only the permissions needed for the task, and write access to the application code is removed from the runtime identity (CIS Benchmarks). In practice, for a shop this means: files typically 644, directories 755, and the web server user may read and execute the program code but not overwrite it.
Files: 644
Readable for the web server, writable only for the owner. Source code files should not be overwritable at runtime.
Directories: 755
Traversable and readable, write access only for the owner. This serves content without allowing uncontrolled creation of files.
Writable paths only where needed
Only clearly delimited directories such as cache, logs and uploads receive write access -- never the entire application tree.
Separate ownership
Separate the deploy identity from the runtime identity: whoever deploys may write; the running web server process only reads and executes.
Protect secrets
Configuration files with credentials (.env) receive tight permissions (around 640) and live outside the publicly served directory.
Avoid 777
Full access for everyone is almost never necessary and a tempting target. Writable upload folders are additionally excluded from PHP execution.
# Base: files 644, directories 755
find /var/www/shop -type f -exec chmod 644 {} +
find /var/www/shop -type d -exec chmod 755 {} +
# Ownership: deploy user owns the code, web server reads
chown -R deploy:www-data /var/www/shop
# Only these paths may be written by the web server
for dir in var/cache var/log files media; do
chmod -R 775 "/var/www/shop/$dir"
done
# Keep secrets tight
chmod 640 /var/www/shop/.envLayer 4: Set Security Headers
HTTP security headers are an effective and comparatively cheap hardening measure -- and yet astonishingly rarely set. According to the HTTP Archive Web Almanac 2024, only around 19 percent of all tested hosts serve a Content-Security-Policy at all (HTTP Archive Web Almanac 2024). For HTTP Strict Transport Security, the figure across all sites is around 31 percent (HTTP Archive Web Almanac 2024). Yet a few lines can raise the bar against entire classes of attack, from protocol downgrades to injected third-party code.
| Header | Protects against | Recommended starting point |
|---|---|---|
| Strict-Transport-Security | protocol downgrade, unencrypted first request | max-age=63072000; includeSubDomains |
| Content-Security-Policy | cross-site scripting, injection of foreign scripts | start restrictive, test in report mode |
| X-Content-Type-Options | MIME sniffing into misinterpreted files | nosniff |
| Referrer-Policy | leakage of URL data to third parties | strict-origin-when-cross-origin |
| X-Frame-Options | clickjacking via embedding in foreign pages | SAMEORIGIN (or CSP frame-ancestors) |
The Content-Security-Policy deserves particular care: set too strictly, it blocks legitimate scripts and disrupts the shop; set too loosely, it misses its purpose. A proven approach is to first measure in pure report mode which sources the shop actually loads, then tighten the policy step by step. This fine-tuning belongs in a staging environment before it moves into live operation.
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;Layer 5: Secure Access and the Firewall
The outermost layer governs who reaches the server at all. A firewall that opens only the truly required ports -- usually 443 for HTTPS and a secured SSH access -- reduces the attack surface considerably. SSH access itself should be switched to key authentication, with password login disabled and no direct root access. Tools such as Fail2ban detect repeated failed attempts and block suspicious addresses automatically.
The urgency is measurable: the BSI reports that 80 percent of organizations affected by ransomware were small and medium-sized enterprises -- so hardening is not a topic for large corporations alone (BSI report 2025). An upstream Web Application Firewall can additionally filter common attack patterns before they reach the application. The key point remains: a WAF is a supplement, not a replacement for hardened configuration and timely updates. Which measures an ongoing operation covers is best clarified in a personal conversation.
Document and version hardening
Keeping Hardening in Live Operation
Hardening is not a state but a process. Updates shift configurations, new features open new paths, and a cipher suite that was secure yesterday may count as weak tomorrow. That is why regular review belongs to maintenance: are the file permissions still correct after the last deployment? Did new default files appear after an update? Does the TLS configuration meet current recommendations? This recurring check is part of a well-thought-out maintenance contract and prevents hardening, once achieved, from quietly eroding again.
- Regularly review PHP directives (expose_php, display_errors, disable_functions, open_basedir) against the current OWASP recommendation.
- Periodically compare the TLS configuration against the Mozilla profiles and remove outdated ciphers.
- Re-check security headers after every major release -- especially the Content-Security-Policy.
- Verify file permissions after every deployment; no 777 directories, no writable code paths.
- Cross-check open ports, SSH access and Fail2ban rules during maintenance.
- Apply security updates for the operating system, web server, PHP and shop promptly -- this closes the window that hardening alone only bridges.
Security does not come from a single tool but from patiently clearing away everything nobody needs -- and doing so with discipline across every update.
Whoever has once cleanly hardened PHP, web server, file permissions, headers and access gains a resilient foundation that supports every further maintenance measure. Interlocked with timely security updates, controlled releases via staging and ongoing monitoring, the result is a shop that withstands a single error instead of breaking on it. Our managed service support handles the setup and lasting upkeep of these layers -- so that hardening does not remain a good intention.