Last active
February 15, 2023 11:17
-
-
Save mtx-z/02becd9cc1fe1beebed6be7b5460f2ca to your computer and use it in GitHub Desktop.
Wordpress .htaccess with Header Caching + CSP Content Security Policy + Gzip compression + PHP execution protection + Spam protection (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#WP block - see https://fr.wordpress.org/support/article/htaccess/ | |
RewriteEngine On | |
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] | |
RewriteBase / | |
RewriteRule ^index\.php$ - [L] | |
# ajouter un slash après /wp-admin | |
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L] | |
RewriteCond %{REQUEST_FILENAME} -f [OR] | |
RewriteCond %{REQUEST_FILENAME} -d | |
RewriteRule ^ - [L] | |
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L] | |
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L] | |
RewriteRule . index.php [L] | |
#END WP | |
# Disable folder indexing | |
Options All -Indexes | |
IndexIgnore * | |
<IfModule mod_autoindex.c> | |
Options -Indexes | |
</IfModule> | |
ServerSignature Off | |
# Follow symlink | |
Options +FollowSymLinks | |
# Timezone | |
SetEnv TZ Europe/Paris | |
# Use UTF-8 encoding for anything served text/plain or text/html | |
AddDefaultCharset UTF-8 | |
# Force UTF-8 for a number of file formats | |
<IfModule mod_mime.c> | |
AddCharset UTF-8 .atom .css .js .json .rss .vtt .xml | |
</IfModule> | |
# Protect .htaccess and .htpasswds | |
<Files ~ "^.*\.([Hh][Tt][AaPp])"> | |
order allow,deny | |
deny from all | |
satisfy all | |
</Files> | |
# Prevent comment spamming | |
<IfModule mod_rewrite.c> | |
RewriteCond %{REQUEST_METHOD} POST | |
RewriteCond %{REQUEST_URI} .wp-comments-post\.php* | |
RewriteCond %{HTTP_REFERER} !.monsite.com.* [OR] | |
RewriteCond %{HTTP_USER_AGENT} ^$ | |
RewriteRule (.*) ^https://%{REMOTE_ADDR}/$ [R=301,L] | |
</IfModule> | |
# author URL protection ?author= | |
<IfModule mod_rewrite.c> | |
RewriteCond %{QUERY_STRING} ^author=([0-9]*) | |
RewriteRule .* - [F] | |
</IfModule> | |
# Prevent hotlinking by referer check | |
RewriteEngine On | |
RewriteCond %{HTTP_REFERER} !^$ | |
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?mon-site.fr [NC] | |
RewriteRule \.(jpg|jpeg|png|gif)$ https://fakeimg.pl/400x200/?text=Pas_touche_aux_images [NC,R,L] | |
# Redirect non-www to www | |
#RewriteEngine On | |
#RewriteCond %{HTTP_HOST} ^monsite.com [NC] | |
#RewriteRule ^(.*)$ https://www.monsite.com/$1 [L,R=301] | |
# Redirect www to non-www | |
RewriteEngine on | |
RewriteCond %{HTTP_HOST} ^www\.monsite\.com [NC] | |
RewriteRule ^(.*)$ https://monsite.com/$1 [L,R=301] | |
# Redirect to HTTPS | |
RewriteCond %{SERVER_PORT} ^80$ | |
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [L,R] | |
# CACHE | |
# Browser header cache | |
<IfModule mod_expires.c> | |
ExpiresActive on | |
ExpiresDefault "access plus 1 month" | |
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5) | |
ExpiresByType text/cache-manifest "access plus 0 seconds" | |
# Your document html | |
ExpiresByType text/html "access plus 0 seconds" | |
# Data | |
ExpiresByType text/xml "access plus 0 seconds" | |
ExpiresByType application/xml "access plus 0 seconds" | |
ExpiresByType application/json "access plus 0 seconds" | |
# Feed | |
ExpiresByType application/rss+xml "access plus 1 hour" | |
ExpiresByType application/atom+xml "access plus 1 hour" | |
# Favicon (cannot be renamed) | |
ExpiresByType image/x-icon "access plus 1 week" | |
# Media: images, video, audio | |
ExpiresByType image/gif "access plus 4 months" | |
ExpiresByType image/png "access plus 4 months" | |
ExpiresByType image/jpeg "access plus 4 months" | |
ExpiresByType image/webp "access plus 4 months" | |
ExpiresByType video/ogg "access plus 4 months" | |
ExpiresByType audio/ogg "access plus 4 months" | |
ExpiresByType video/mp4 "access plus 4 months" | |
ExpiresByType video/webm "access plus 4 months" | |
# HTC files (css3pie) | |
ExpiresByType text/x-component "access plus 1 month" | |
# Webfonts | |
ExpiresByType font/ttf "access plus 4 months" | |
ExpiresByType font/otf "access plus 4 months" | |
ExpiresByType font/woff "access plus 4 months" | |
ExpiresByType font/woff2 "access plus 4 months" | |
ExpiresByType image/svg+xml "access plus 1 month" | |
ExpiresByType application/vnd.ms-fontobject "access plus 1 month" | |
# CSS and JavaScript | |
ExpiresByType text/css "access plus 1 year" | |
ExpiresByType application/javascript "access plus 1 year" | |
</IfModule> | |
# Gzip compression | |
<IfModule mod_deflate.c> | |
# Active compression | |
SetOutputFilter DEFLATE | |
# Force deflate for mangled headers | |
<IfModule mod_setenvif.c> | |
<IfModule mod_headers.c> | |
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding | |
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding | |
# Don’t compress images and other uncompressible content | |
SetEnvIfNoCase Request_URI \ | |
\.(?:gif|jpe?g|png|rar|zip|exe|flv|mov|wma|mp3|avi|swf|mp?g|mp4|webm|webp|pdf)$ no-gzip dont-vary | |
</IfModule> | |
</IfModule> | |
# Compress all output labeled with one of the following MIME-types | |
<IfModule mod_filter.c> | |
AddOutputFilterByType DEFLATE application/atom+xml \ | |
application/javascript \ | |
application/json \ | |
application/rss+xml \ | |
application/vnd.ms-fontobject \ | |
application/x-font-ttf \ | |
application/xhtml+xml \ | |
application/xml \ | |
font/opentype \ | |
image/svg+xml \ | |
image/x-icon \ | |
text/css \ | |
text/html \ | |
text/plain \ | |
text/x-component \ | |
text/xml | |
</IfModule> | |
<IfModule mod_headers.c> | |
Header append Vary: Accept-Encoding | |
</IfModule> | |
</IfModule> | |
# Add mime type | |
<IfModule mod_mime.c> | |
AddType text/html .html_gzip | |
AddEncoding gzip .html_gzip | |
</IfModule> | |
<IfModule mod_setenvif.c> | |
SetEnvIfNoCase Request_URI \.html_gzip$ no-gzip | |
</IfModule> | |
# Headers | |
Header unset ETag | |
FileETag None | |
# FileETag None is not enough for every server. | |
<IfModule mod_headers.c> | |
Header unset ETag | |
</IfModule> | |
<ifModule mod_headers.c> | |
<filesMatch "\.(ico|jpe?g|png|gif|swf)$"> | |
Header set Cache-Control "public" | |
</filesMatch> | |
<filesMatch "\.(css)$"> | |
Header set Cache-Control "public" | |
</filesMatch> | |
<filesMatch "\.(js)$"> | |
Header set Cache-Control "private" | |
</filesMatch> | |
<filesMatch "\.(x?html?|php)$"> | |
Header set Cache-Control "private, must-revalidate" | |
</filesMatch> | |
</ifModule> | |
<IfModule mod_alias.c> | |
<FilesMatch "\.(css|htc|js|asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)$"> | |
<IfModule mod_headers.c> | |
Header unset Pragma | |
Header append Cache-Control "public" | |
</IfModule> | |
</FilesMatch> | |
</IfModule> | |
# END CACHE | |
# Static compression | |
<IfModule mod_deflate.c> | |
AddOutputFilterByType DEFLATE text/xhtml text/html text/plain text/xml text/javascript application/x-javascript text/css | |
BrowserMatch ^Mozilla/4 gzip-only-text/html | |
BrowserMatch ^Mozilla/4\.0[678] no-gzip | |
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html | |
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary | |
Header append Vary User-Agent env=!dont-vary | |
</IfModule> | |
AddOutputFilterByType DEFLATE text/html | |
AddOutputFilterByType DEFLATE text/plain | |
AddOutputFilterByType DEFLATE text/xml | |
AddOutputFilterByType DEFLATE text/css | |
AddOutputFilterByType DEFLATE text/javascript | |
AddOutputFilterByType DEFLATE font/opentype | |
AddOutputFilterByType DEFLATE application/rss+xml | |
AddOutputFilterByType DEFLATE application/javascript | |
AddOutputFilterByType DEFLATE application/json | |
# Block few scripts | |
RewriteEngine On | |
RewriteBase / | |
RewriteRule ^wp-admin/includes/ - [F,L] | |
RewriteRule !^wp-includes/ - [S=3] | |
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] | |
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] | |
RewriteRule ^wp-includes/theme-compat/ - [F,L] | |
# Injection protection | |
RewriteCond %{REQUEST_METHOD} GET | |
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR] | |
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR] | |
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC] | |
RewriteRule .* - [F] | |
# CPS (XSS, clickjacking & MIME-Type sniffing) | |
<ifModule mod_headers.c> | |
Header set X-Frame-Options "sameorigin" | |
Header set X-XSS-Protection "1; mode=block" | |
Header set X-Content-Type-Options "nosniff" | |
Header set Strict-Transport-Security "max-age=31536000; preload" | |
Header set Referrer-Policy "strict-origin-when-cross-origin" | |
Header set X-DNS-Prefetch-Control "on" | |
Header set Permissions-Policy "autoplay=*, fullscreen=* vertical-scroll=*" | |
</ifModule> | |
# PROTECT install.php | |
# Uncomment or change to 'Allow from all' for install of WordPress | |
<Files install.php> | |
Order Allow,Deny | |
Deny from all | |
Satisfy all | |
</Files> | |
# Prevent wp-config.php access | |
<files wp-config.php> | |
order allow,deny | |
deny from all | |
</files> | |
# Protect XMLRPC (needed for Apps, Offline-Blogging-Tools, Pingback, etc.) | |
# If you use that, these tools will not work anymore | |
<Files xmlrpc.php> | |
Order Deny,Allow | |
Deny from all | |
</Files> | |
# Prevent browser and search engines to request .log (e.g. WP DEBUG LOG) and .txt (e.g. plugins readme) files. | |
# Must be placed in /wp-content/.htaccess | |
<FilesMatch "\.(log|txt)$"> | |
Order Allow,Deny | |
Deny from all | |
</FilesMatch> | |
# Hide WordPress, system & sensitive files | |
<FilesMatch "(^\.|wp-config(-sample)*\.php)"> | |
Order Deny,Allow | |
Deny from all | |
</FilesMatch> | |
# Protect some other files | |
<FilesMatch "(liesmich.html|readme.html|license.txt|(.*)\.bak)"> | |
Order Deny,Allow | |
Deny from all | |
</FilesMatch> | |
# Block the include-only files. | |
# Do not use in Multisite without reading the note in Codex! | |
# See: https://wordpress.org/support/article/hardening-wordpress/#securing-wp-includes | |
<IfModule mod_rewrite.c> | |
RewriteEngine On | |
RewriteBase / | |
RewriteRule ^wp-admin/includes/ - [F,L] | |
RewriteRule !^wp-includes/ - [S=3] | |
# If you run multisite, comment the next line out (see note above) | |
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] | |
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] | |
RewriteRule ^wp-includes/theme-compat/ - [F,L] | |
RewriteRule ^wp-admin/install\.php$ - [F] | |
RewriteCond %{REQUEST_FILENAME} -f | |
RewriteRule (^|.*/)\.(git|svn)/.* - [F] | |
# Disable PHP in Uploads | |
RewriteRule ^wp\-content/uploads/.*\.(?:php[1-7]?|pht|phtml?|phps)\.?$ - [NC,F] | |
# Disable PHP in Plugins | |
RewriteRule ^wp\-content/plugins/.*\.(?:php[1-7]?|pht|phtml?|phps)\.?$ - [NC,F] | |
# Disable PHP in themes | |
RewriteRule ^wp\-content/themes/.*\.(?:php[1-7]?|pht|phtml?|phps)\.?$ - [NC,F] | |
</IfModule> | |
# Force secure cookies (uncomment if you use HTTP://, which you should NOT, use HTTPS with letsencrypt at least) | |
<IfModule mod_headers.c> | |
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure | |
</IfModule> | |
#Unset headers revealing versions strings | |
<IfModule mod_headers.c> | |
Header unset X-Powered-By | |
Header unset X-Pingback | |
Header unset SERVER | |
</IfModule> | |
# Filter Request Methods | |
# See: https://perishablepress.com/disable-trace-and-track-for-better-security/ | |
<IfModule mod_rewrite.c> | |
RewriteEngine on | |
RewriteCond %{REQUEST_METHOD} ^(TRACE|DELETE|TRACK) [NC] | |
RewriteRule ^(.*)$ - [F,L] | |
</IfModule> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment