Forum Moderators: Robert Charlton & goodroi
# Activate the Rewrite Engine
RewriteEngine On
RewriteBase/
# Security headers (must come before everything)
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set Content-Security-Policy "default-src 'self'; \
***I hid it here.
object-src 'none'; \
base-uri 'self'; \
form-action 'self'; \
frame-ancestors 'none';"
# Define custom MIME types for specific files
AddType image/webp .webp
AddType application/manifest+json .webmanifest
# Remove trailing slash from non-directory URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]
# Prevent rewriting if file or directory exists
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Directories without slash → with slash
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !/$
RewriteRule ^(.+)$ %{REQUEST_URI}/ [R=301,L]
# Remove the .php extension from URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^(.+)$ $1.php [L]
# Cache Policies for Static Resources
<IfModule mod_expires.c>
ExpiresActive On
# Images: 1 month
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 month"
# Videos: 1 month
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
#CSS: 1 month
ExpiresByType text/css "access plus 1 month"
# Scripts: 1 week
ExpiresByType application/javascript "access plus 1 week"
ExpiresByType application/x-javascript "access plus 1 week"
# Sources: 1 month
ExpiresByType font/woff "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 month"
ExpiresByType font/ttf "access plus 1 month"
ExpiresByType font/otf "access plus 1 month"
</IfModule>
# Error pages personalized
ErrorDocument 400 /errors/error.php
ErrorDocument 401 /errors/error.php
ErrorDocument 403 /errors/error.php
ErrorDocument 404 /errors/error.php
ErrorDocument 500 /errors/error.php
ErrorDocument 502 /errors/error.php
ErrorDocument 503 /errors/error.php
ErrorDocument 504 /errors/error.php
# Enable Brotli compression
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/plain
AddOutputFilterByType BROTLI_COMPRESS text/html
AddOutputFilterByType BROTLI_COMPRESS text/xml
AddOutputFilterByType BROTLI_COMPRESS text/css
AddOutputFilterByType BROTLI_COMPRESS application/xml
AddOutputFilterByType BROTLI_COMPRESS application/xhtml+xml
AddOutputFilterByType BROTLI_COMPRESS application/rss+xml
AddOutputFilterByType BROTLI_COMPRESS application/javascript
AddOutputFilterByType BROTLI_COMPRESS application/x-javascript
AddOutputFilterByType BROTLI_COMPRESS application/json
AddOutputFilterByType BROTLI_COMPRESS font/ttf
AddOutputFilterByType BROTLI_COMPRESS font/otf
AddOutputFilterByType BROTLI_COMPRESS font/woff
AddOutputFilterByType BROTLI_COMPRESS font/woff2
</IfModule>
# Enable Gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/woff
AddOutputFilterByType DEFLATE font/woff2
</IfModule>
# Remove the .php extension from URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^(.+)$ $1.php [L]
Uhm, this ruleset doesn’t remove php from URLs. It adds .php to all requests that don’t already have it. Removing the .php extension from requests that have it would be a different rule. (And all those -f and -d tests are horribly inefficient. It's generally possible to write rules that bypass this step.) Header always set X-Frame-Options "DENY" is intended for that, though I had not seen it in that format before. I've used this for the same purpose: Header set X-Frame-Options "deny" I had not seen it in that format before
The difference between the two lists is that the headers contained in the latter [i.e. "always"] are added to the response even on error, and persisted across internal redirects (for example, ErrorDocument handlers).This is probably worth mulling over, though not necessarily in the case of the current thread. I mean, who cares if they frame your 403 page ;)
66.249.68.35 - - [11/Mar/2025:20:28:31 -0300] "GET /robots.txt HTTP/2.0" 200 252 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:32 -0300] "GET / HTTP/2.0" 200 24684 "-" "Mozilla/5.0 (compatible; Google-InspectionTool/1.0;)" example.com 10.10.10.10
66.249.68.33 - - [11/Mar/2025:20:28:32 -0300] "GET / HTTP/2.0" 200 24684 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0;)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/lp.css?v=2.0.9 HTTP/2.0" 200 13835 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/carousel.css?v=2.0.9 HTTP/2.0" 200 700 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/carousel.css?v=2.0.9 HTTP/2.0" 200 700 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/cookie-banner.css?v=2.0.9 HTTP/2.0" 200 762 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/main.css?v=2.0.9 HTTP/2.0" 200 3588 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/images/logo-960x276.webp HTTP/2.0" 200 10174 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/main.css?v=2.0.9 HTTP/2.0" 200 3588 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/lp.css?v=2.0.9 HTTP/2.0" 200 13835 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/images/logo-960x276.webp HTTP/2.0" 200 10174 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/css/cookie-banner.css?v=2.0.9 HTTP/2.0" 200 762 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:33 -0300] "GET /assets/js/cookie-banner.js?v=2.0.5 HTTP/2.0" 200 301 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/js/cookie-banner.js?v=2.0.5 HTTP/2.0" 200 301 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/videos/video03.mp4 HTTP/2.0" 200 494420 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/videos/video02.mp4 HTTP/2.0" 200 453277 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.33 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/videos/video02.mp4 HTTP/2.0" 200 453277 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/videos/video01.mp4 HTTP/2.0" 200 456813 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:34 -0300] "GET /assets/videos/video01.mp4 HTTP/2.0" 200 456813 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:35 -0300] "GET /assets/videos/video03.mp4 HTTP/2.0" 200 494420 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.33 - - [11/Mar/2025:20:28:37 -0300] "GET /assets/images/foto01.webp HTTP/2.0" 200 31902 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:38 -0300] "GET /assets/images/foto02.webp HTTP/2.0" 200 33994 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:38 -0300] "GET /assets/images/foto03.webp HTTP/2.0" 200 55010 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:40 -0300] "GET /assets/images/foto02.webp HTTP/2.0" 200 33994 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.34 - - [11/Mar/2025:20:28:40 -0300] "GET /assets/images/foto03.webp HTTP/2.0" 200 55010 "https://example.com/" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Google-InspectionTool/1.0)" example.com 10.10.10.10
66.249.68.35 - - [11/Mar/2025:20:29:08 -0300] "GET / HTTP/2.0" 200 24684 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.141 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" example.com 10.10.10.10
<img src="https://www.example.com/assets/images/foto03.webp" />. I don't think the htaccess files and headers will prevent hot-linlinking. I don't think the htaccess files and headers will prevent hot-linlinking.Blocking hotlinks is trivial. When there is a request for an image, check the referer. If it is neither blank (not just crawlers but some human browsers) nor a short list of authorized referers (your own site and the standard search engines), you can either 403 the request outright, or--less work for the server--rewrite to your chosen NO HOTLINKS image.
example.com/?p=123493
The site exists, but the page within the site does not exist.
[edited by: mcneely at 6:23 pm (utc) on Oct 3, 2025]
Google even listed the urls as indexed but no actual page could be tracked down ...