2026-02-20 11:00:23 +00:00
|
|
|
# Redirect www to non-www
|
|
|
|
|
RewriteEngine On
|
2026-03-21 09:48:46 +00:00
|
|
|
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
2026-02-20 11:00:23 +00:00
|
|
|
RewriteRule ^(.*)$ https://%1/$1 [L,R=301]
|
|
|
|
|
|
Fix navbar across all pages: add nav include, fonts, active state, spacing, stats, error pages
- Add nav.php include to 5 missing pages (cost-calculator, thank-you, 403, 404, 500)
- Add ErrorDocument directives to .htaccess for custom 403/404/500 pages
- Fix bogus accuracy stats (homepage, web-scraping, location pages)
- Fix invisible CTA buttons on property and financial service pages
- Add Google Fonts (Roboto Slab + Lato) to all pages missing it (tools, blog articles, error pages)
- Add active nav link highlighting (teal underline for current page)
- Improve footer contrast to WCAG AA, equal-height cards, mobile text scaling
- Consistent navbar-to-content spacing across all pages
- Bump cache version to v1.1.3
2026-02-11 07:15:11 +00:00
|
|
|
# Custom error pages
|
|
|
|
|
ErrorDocument 403 /403.php
|
|
|
|
|
ErrorDocument 404 /404.php
|
|
|
|
|
ErrorDocument 500 /500.php
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Protect sensitive files
|
2026-02-04 03:17:55 +00:00
|
|
|
<FilesMatch "^\.(.*)$|\.log$|\.sql$|\.conf$|config\.php$|\.email-config\.php$|\.htaccess|\.htpasswd|\.ini|\.sh|\.inc|\.bak$">
|
|
|
|
|
Require all denied
|
|
|
|
|
</FilesMatch>
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Protect handlers (POST only)
|
2026-02-04 03:17:55 +00:00
|
|
|
<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"
|
2026-02-05 04:11:15 +00:00
|
|
|
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
|
2026-02-04 03:17:55 +00:00
|
|
|
<FilesMatch "(quote|contact)\.php$">
|
|
|
|
|
Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
|
|
|
|
|
Header set Pragma "no-cache"
|
|
|
|
|
</FilesMatch>
|
|
|
|
|
</IfModule>
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Compression
|
2026-02-04 03:17:55 +00:00
|
|
|
<IfModule mod_deflate.c>
|
2026-03-21 09:48:46 +00:00
|
|
|
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/javascript application/json image/svg+xml font/woff font/woff2
|
2026-02-04 03:17:55 +00:00
|
|
|
</IfModule>
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Caching
|
2026-02-04 03:17:55 +00:00
|
|
|
<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>
|
2026-03-21 09:48:46 +00:00
|
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|woff|woff2|ttf|otf)$">
|
2026-02-04 03:17:55 +00:00
|
|
|
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
|
2026-03-21 09:48:46 +00:00
|
|
|
ServerSignature Off
|
2026-02-04 03:17:55 +00:00
|
|
|
|
|
|
|
|
<IfModule mod_rewrite.c>
|
|
|
|
|
RewriteEngine On
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Block requests for non-existent PHP files (webshell scanners)
|
2026-03-02 11:15:06 +00:00
|
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
|
|
|
RewriteRule \.php$ - [F,L]
|
|
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# 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]
|
2026-02-20 11:00:23 +00:00
|
|
|
|
2026-02-04 03:17:55 +00:00
|
|
|
# Clean URL rewriting - remove .php extension
|
|
|
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
|
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
|
|
|
RewriteCond %{REQUEST_FILENAME}.php -f
|
2026-03-02 11:08:53 +00:00
|
|
|
RewriteRule ^(.+?)/?$ $1.php [END]
|
2026-02-04 03:17:55 +00:00
|
|
|
|
2026-03-21 09:48:46 +00:00
|
|
|
# Block access to sensitive directories
|
|
|
|
|
RewriteRule ^(logs|database|docker)(/.*)?$ - [F,L]
|
|
|
|
|
RewriteRule ^\.git(/.*)?$ - [F,L]
|
2026-02-05 04:11:15 +00:00
|
|
|
</IfModule>
|