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.
Key takeaways
- Hardening means allowing a system only what its task requires. The OWASP Top 10 list misconfiguration as its own risk category covering around 90 percent (OWASP Top 10 2021) of tested applications, and time-to-exploit averaged five days in 2023 (Mandiant).
- In the PHP configuration,
expose_phpanddisplay_errorsbelong on Off, alongsideopen_basedir, blocked system calls such as exec or shell_exec andallow_url_fopen = Off(OWASP PHP Configuration Cheat Sheet). Which functions can go is verified per shop on staging. - On the web server, server tokens and autoindex are switched off and paths like .git or .env are never served. RFC 8996 deprecates TLS 1.0 and 1.1, so only TLS 1.2 and 1.3 remain, following the Mozilla Intermediate profile.
- File permissions follow least privilege: 644 for files, 755 for directories, write access only for cache, logs and uploads, and a tightly restricted .env outside the served directory (CIS Benchmarks). Upload folders are additionally excluded from PHP execution.
- Security headers cost little and are often missing: only about 19 percent (HTTP Archive Web Almanac 2024) of hosts serve a Content-Security-Policy and 31 percent set HSTS. The CSP is measured in report mode first, then tightened step by step.
- Only required ports stay open, SSH runs on keys without root login, and a WAF supplements hardening without replacing configuration or updates. Permissions, headers and TLS are re-checked after every deployment and kept under version control.
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 (Verizon DBIR 2024) of analyzed breaches to a non-malicious human element -- including misconfigurations and accidental exposures.
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 (BSI report 2025) of organizations affected by ransomware were small and medium-sized enterprises -- so hardening is not a topic for large corporations alone. 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.
expose_php = Off and display_errors = Off to avoid disclosing version and error details, as well as open_basedir and disable_functions to restrict file operations and risky system functions (OWASP PHP Configuration Cheat Sheet). Which functions can be switched off safely depends on the shop and is tested in a staging environment beforehand.644 for files and 755 for directories, with the running web server able to read and execute the code but not overwrite it (CIS Benchmarks). Write access goes only to clearly delimited paths such as cache, logs and uploads. Full access (777) is almost never necessary, and upload folders are additionally excluded from PHP execution.