Skip to content

Instantly share code, notes, and snippets.

@ustoopia
Last active November 10, 2024 22:36
Show Gist options
  • Save ustoopia/2a54d06ab990597d139d6eb8a327e470 to your computer and use it in GitHub Desktop.
Save ustoopia/2a54d06ab990597d139d6eb8a327e470 to your computer and use it in GitHub Desktop.
Config to harden Nginx running Wordpress sites
############ Config to harden Nginx running Wordpress sites
# Paste this in a new file like: /etc/nginx/conf.wp/harden-wordpress.conf
# in main/vhost config add: include /etc/nginx/conf.wp/harden-wordpress.conf;
#
# Restart nginx and voila. This gist is based on:
# https://gist.github.com/nfsarmento/57db5abba08b315b67f174cd178bea88
#
############ WordPress ####################
# Disable logging for favicon and robots.txt
location = /favicon.ico {
try_files /favicon.ico @empty;
access_log off;
log_not_found off;
expires max;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
try_files $uri /index.php?$args;
}
#Deny access to wp-content folders for suspicious files
location ~* ^/(wp-content)/(.*?)\.(zip|gz|tar|bzip2|7z)\$ {
deny all;
}
location ~ ^/wp-content/uploads/sucuri {
deny all;
}
location ~ ^/wp-content/updraft {
deny all;
}
#Disable execution of scripts other than PHP from your document root
location ~* .(pl|cgi|py|sh|lua|asp)$ {
return 444;
}
#Disable access to your configuration files and other files that you don’t want to users are able to see
location ~* /(wp-config.php|readme.html|license.txt|nginx.conf) {
deny all;
}
# Disable wp-config.txt
location = /wp-config.txt {
deny all;
access_log off;
log_not_found off;
}
# Disallow php in upload folder and add webp rewrite
location /wp-content/uploads/ {
location ~ \.php$ {
#Prevent Direct Access Of PHP Files From Web Browsers
deny all;
}
# webp rewrite rules
location ~ \.(png|jpe?g)$ {
add_header Vary "Accept-Encoding";
add_header "Access-Control-Allow-Origin" "*";
add_header Cache-Control "public, no-transform";
access_log off;
log_not_found off;
expires max;
try_files $uri $uri =404;
}
}
# nginx block xmlrpc.php requests
location /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
return 444;
}
# nginx block wpscann on plugins folder
location ~* ^/wp-content/plugins/.+\.(txt|log|md)$ {
deny all;
error_page 403 =404 / ;
}
# block access to install.php and upgrade.php
location ^~ /wp-admin/install.php {
deny all;
error_page 403 =404 / ;
}
location ^~ /wp-admin/upgrade.php {
deny all;
error_page 403 =404 / ;
}
# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# Stop scann for the follow files on plugins folder
location ~* ^/wp-content/plugins/.+\.(txt|log|md)$ {
deny all;
error_page 403 =404 / ;
}
# Stop scann for the follow files on themes folder
location ~* ^/wp-content/themes/.+\.(txt|log|md)$ {
deny all;
error_page 403 =404 / ;
}
#Direct PHP File Access
#If somehow, a hacker successfully sneaks in a PHP file onto your site,
#they’ll be able to run this file by loading file which effectively becomes a backdoor to infiltrate your site.
#We should disable direct access to any PHP files by adding the following rules:
location ~* /(?:uploads|files|wp-content|wp-includes|akismet)/.*.php$ {
deny all;
access_log off;
log_not_found off;
}
#Dotfiles
#Similar to PHP file, a dotfile like .htaccess, .user.ini, and .git may contain sensitive information.
#To be on the safer side, it’s better to disable direct access to these files.
location ~ /\.(svn|git)/* {
deny all;
access_log off;
log_not_found off;
}
location ~ /\.ht {
deny all;
access_log off;
log_not_found off;
}
location ~ /\.user.ini {
deny all;
access_log off;
log_not_found off;
}
# Deny access to uploads that aren’t images, videos, music, etc.
location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ {
deny all;
}
# Deny backup extensions & log files
location ~* ^.+\.(bak|log|old|orig|original|php#|php~|php_bak|save|swo|swp|sql)$ {
deny all;
access_log off;
log_not_found off;
}
#WordFence
location ~ \.user\.ini$ {
deny all;
}
# WordPress: deny wp-content, wp-includes php files
location ~* ^/(?:wp-content|wp-includes)/.*\.php$ {
deny all;
}
# WordPress: deny wp-content/uploads nasty stuff
location ~* ^/wp-content/uploads/.*\.(?:s?html?|php|js|swf)$ {
deny all;
}
# WordPress: deny general stuff
location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|wp-comments-post\.php|readme\.html|license\.txt)$ {
deny all;
}
# NGINX RESTRICTIONS
# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(curl|heic|swf|tiff|rss|atom|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off;
log_not_found off;
expires max;
}
# Web fonts send expires headers
location ~* \.(?:eot|otf|ttf|woff|woff2)$ {
expires max;
access_log off;
add_header Cache-Control "public";
}
# SVGs & MP4 WEBM send expires headers - this rule is set specific to ns site
location ~* \.(?:svg|svgz|mp4|webm)$ {
expires max;
access_log off;
add_header Cache-Control "public";
}
# Media: images, icons, video, audio send expires headers.
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|aac|m4a|mp3|ogg|ogv|webp)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# Cache css & js files
location ~* \.(?:css(\.map)?|js(\.map)?)$ {
add_header "Access-Control-Allow-Origin" "*";
access_log off;
log_not_found off;
expires 30d;
}
# CSS and Javascript send expires headers.
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# HTML send expires headers.
location ~* \.(html)$ {
expires 7d;
access_log off;
add_header Cache-Control "public";
}
# Security settings for better privacy
# Deny hidden files
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
location ~ /\. {
deny all;
}
# Return 403 forbidden for readme.(txt|html) or license.(txt|html) or example.(txt|html) or other common git repository files
location ~* "/(^$|readme|license|example|README|LEGALNOTICE|INSTALLATION|CHANGELOG)\.(txt|html|md)" {
deny all;
}
# Deny backup extensions & log files and return 403 forbidden
location ~* "\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf)$" {
deny all;
}
# common nginx configuration to block sql injection and other attacks
location ~* "(eval\()" {
deny all;
}
location ~* "(127\.0\.0\.1)" {
deny all;
}
location ~* "([a-z0-9]{2000})" {
deny all;
}
location ~* "(javascript\:)(.*)(\;)" {
deny all;
}
location ~* "(base64_encode)(.*)(\()" {
deny all;
}
location ~* "(GLOBALS|REQUEST)(=|\[|%)" {
deny all;
}
location ~* "(<|%3C).*script.*(>|%3)" {
deny all;
}
location ~ "(\\|\.\.\.|\.\./|~|`|<|>|\|)" {
deny all;
}
location ~* "(boot\.ini|etc/passwd|self/environ)" {
deny all;
}
location ~* "(thumbs?(_editor|open)?|tim(thumb)?)\.php" {
deny all;
}
location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" {
deny all;
}
location ~* "(https?|ftp|php):/" {
deny all;
}
location ~* "(=\\\'|=\\%27|/\\\'/?)\." {
deny all;
}
location ~ "(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")" {
deny all;
}
location ~ "(~|`|<|>|:|;|%|\\|\s|\{|\}|\[|\]|\|)" {
deny all;
}
location ~* "/(=|\$&|_mm|(wp-)?config\.|cgi-|etc/passwd|muieblack)" {
deny all;
}
location ~* "(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)" {
deny all;
}
location ~* "/(^$|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell|config|settings|configuration)\.php" {
deny all;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment