105 lines
3.6 KiB
ApacheConf
105 lines
3.6 KiB
ApacheConf
# Redirect www to non-www
|
|
RewriteEngine On
|
|
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
|
RewriteRule ^(.*)$ https://%1/$1 [L,R=301]
|
|
|
|
# Custom error pages
|
|
ErrorDocument 403 /403.php
|
|
ErrorDocument 404 /404.php
|
|
ErrorDocument 500 /500.php
|
|
|
|
# Protect sensitive files
|
|
<FilesMatch "^\.(.*)$|\.log$|\.sql$|\.conf$|config\.php$|\.email-config\.php$|\.htaccess|\.htpasswd|\.ini|\.sh|\.inc|\.bak$">
|
|
Require all denied
|
|
</FilesMatch>
|
|
|
|
# Protect handlers (POST only)
|
|
<Files "contact-handler.php">
|
|
<LimitExcept POST>
|
|
Require all denied
|
|
</LimitExcept>
|
|
</Files>
|
|
<Files "quote-handler.php">
|
|
<LimitExcept POST>
|
|
Require all denied
|
|
</LimitExcept>
|
|
</Files>
|
|
|
|
# Security headers
|
|
<IfModule mod_headers.c>
|
|
Header always set X-Content-Type-Options "nosniff"
|
|
Header always set X-Frame-Options "SAMEORIGIN"
|
|
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
|
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
|
|
<FilesMatch "(quote|contact)\.php$">
|
|
Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
|
|
Header set Pragma "no-cache"
|
|
</FilesMatch>
|
|
</IfModule>
|
|
|
|
# Compression
|
|
<IfModule mod_deflate.c>
|
|
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json image/svg+xml font/woff font/woff2
|
|
</IfModule>
|
|
|
|
# Caching
|
|
<IfModule mod_expires.c>
|
|
ExpiresActive On
|
|
ExpiresByType image/jpeg "access plus 1 year"
|
|
ExpiresByType image/png "access plus 1 year"
|
|
ExpiresByType image/webp "access plus 1 year"
|
|
ExpiresByType image/svg+xml "access plus 1 year"
|
|
ExpiresByType image/x-icon "access plus 1 year"
|
|
ExpiresByType font/woff "access plus 1 year"
|
|
ExpiresByType font/woff2 "access plus 1 year"
|
|
ExpiresByType text/css "access plus 1 month"
|
|
ExpiresByType application/javascript "access plus 1 month"
|
|
ExpiresByType application/json "access plus 0 seconds"
|
|
ExpiresDefault "access plus 1 week"
|
|
</IfModule>
|
|
<IfModule mod_headers.c>
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|woff|woff2|ttf|otf)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
</FilesMatch>
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=2592000, public"
|
|
</FilesMatch>
|
|
Header set Connection keep-alive
|
|
</IfModule>
|
|
|
|
FileETag None
|
|
Header unset ETag
|
|
Options -Indexes
|
|
ServerSignature Off
|
|
|
|
<IfModule mod_rewrite.c>
|
|
RewriteEngine On
|
|
|
|
# Block requests for non-existent PHP files (webshell scanners)
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteRule \.php$ - [F,L]
|
|
|
|
# Redirect old pages to homepage
|
|
RewriteRule ^services(/.*)?$ / [R=301,L]
|
|
RewriteRule ^locations(/.*)?$ / [R=301,L]
|
|
RewriteRule ^tools(/.*)?$ / [R=301,L]
|
|
RewriteRule ^project-types/?$ / [R=301,L]
|
|
RewriteRule ^web-scraping-services(/.*)?$ / [R=301,L]
|
|
RewriteRule ^data-scraping-services(/.*)?$ / [R=301,L]
|
|
RewriteRule ^price-monitoring-services(/.*)?$ / [R=301,L]
|
|
RewriteRule ^data-analytics-services(/.*)?$ / [R=301,L]
|
|
RewriteRule ^data-analytics-consultancy-london(/.*)?$ / [R=301,L]
|
|
RewriteRule ^data-analytics-services-london(/.*)?$ / [R=301,L]
|
|
RewriteRule ^data-services-london(/.*)?$ / [R=301,L]
|
|
|
|
# Clean URL rewriting - remove .php extension
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME}.php -f
|
|
RewriteRule ^(.+?)/?$ $1.php [END]
|
|
|
|
# Block access to sensitive directories
|
|
RewriteRule ^(logs|database|docker)(/.*)?$ - [F,L]
|
|
RewriteRule ^\.git(/.*)?$ - [F,L]
|
|
</IfModule>
|