Created
October 3, 2025 08:09
-
-
Save rubendob/fac1db2090ab4e543e280719f9ec0a21 to your computer and use it in GitHub Desktop.
Nginx for Wordpress
This file contains hidden or 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
| server { | |
| listen 443 ssl; | |
| access_log /var/log/nginx/access.log main; | |
| # Use a generic placeholder, not your real domain | |
| server_name example.com; | |
| # Replace with your real certificate paths (kept generic for sharing) | |
| ssl_certificate /etc/ssl/certs/fullchain.pem; | |
| ssl_certificate_key /etc/ssl/private/privkey.pem; | |
| # TLS hardening (keep modern protocols/ciphers only) | |
| ssl_protocols TLSv1.2 TLSv1.3; | |
| ssl_ciphers HIGH:!aNULL:!MD5; | |
| ssl_session_cache shared:SSL:1m; # ~4000 sessions | |
| ssl_session_timeout 1h; # Session reuse window | |
| ssl_session_tickets off; # Disable tickets if you can rotate keys | |
| ssl_buffer_size 4k; # Reasonable TLS record size | |
| ssl_prefer_server_ciphers on; | |
| # Adjust if you upload large media via WP | |
| client_max_body_size 128M; | |
| root /var/www/html/; | |
| index index.php; | |
| # ------------------------- | |
| # Security headers | |
| # ------------------------- | |
| # Clickjacking protection: allow framing only from same origin | |
| add_header X-Frame-Options "SAMEORIGIN" always; | |
| # MIME sniffing protection | |
| add_header X-Content-Type-Options "nosniff" always; | |
| # HSTS: force HTTPS for one year including subdomains (enable only once HTTPS is stable) | |
| add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; | |
| # Referrer policy: send full URL on same-origin, only origin on cross-origin | |
| add_header Referrer-Policy "strict-origin-when-cross-origin" always; | |
| # Permissions-Policy: disable browser features not needed by WP frontends | |
| add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), gyroscope=(), magnetometer=(), accelerometer=(), fullscreen=(), interest-cohort=()" always; | |
| # ------------------------- | |
| # General routing | |
| # ------------------------- | |
| location / { | |
| # Try static file first, then directory, then route to index.php with query string | |
| try_files $uri $uri/ /index.php?$query_string; | |
| gzip_static on; | |
| autoindex off; | |
| } | |
| # Cache static assets for 30 days (adjust to your cache strategy/CDN) | |
| location ~* \.(css|js|jpg|jpeg|png|gif|ico|txt|woff|woff2|ttf)$ { | |
| expires 30d; | |
| } | |
| # ------------------------- | |
| # WordPress-specific hardening | |
| # ------------------------- | |
| # Limit access to xmlrpc: allow only a trusted IP (replace with your own or deny all) | |
| location = /xmlrpc.php { | |
| allow 203.0.113.10; # <--- replace or remove 'allow' and just 'deny all;' | |
| deny all; | |
| } | |
| # Deny direct access to scripts inside common WP directories | |
| location ~* /(?:uploads|files|wp-content|wp-includes|akismet)/.*\.(?:html?|php|js|swf)$ { | |
| deny all; | |
| access_log off; | |
| log_not_found off; | |
| } | |
| # Extra protection in uploads: block script-like files | |
| location ~* ^/wp-content/uploads/.*\.(?:s?html?|php|js|swf)$ { | |
| deny all; | |
| } | |
| # Hide VCS/hidden files (.git, .svn, etc.) | |
| location ~ /\.(?:svn|git)/* { | |
| deny all; | |
| access_log off; | |
| log_not_found off; | |
| } | |
| # Block direct access to .htaccess if present (Apache artifacts) | |
| location ~ /\.htaccess { | |
| deny all; | |
| access_log off; | |
| log_not_found off; | |
| } | |
| # Block PHP/user ini files | |
| location ~ /\.user\.ini { | |
| deny all; | |
| access_log off; | |
| log_not_found off; | |
| } | |
| # Protect sensitive WP core files | |
| location ~* /(wp-config\.php|wp-cron\.php) { | |
| deny all; | |
| } | |
| # Disallow execution of non-PHP scripts from document root (common web shells) | |
| location ~* \.(?:pl|cgi|py|sh|lua|asp)$ { | |
| return 444; | |
| } | |
| # Prevent plugin folder reconnaissance (block text/log/markdown listings) | |
| location ~* ^/wp-content/plugins/.+\.(?:txt|log|md)$ { | |
| deny all; | |
| error_page 403 =404 /; | |
| } | |
| # Prevent theme folder reconnaissance | |
| location ~* ^/wp-content/themes/.+\.(?:txt|log|md)$ { | |
| deny all; | |
| error_page 403 =404 /; | |
| } | |
| # Block access to installer/upgrade scripts (should never be directly reachable) | |
| location = /wp-admin/install.php { | |
| deny all; | |
| error_page 403 =404 /; | |
| } | |
| location = /wp-admin/upgrade.php { | |
| deny all; | |
| error_page 403 =404 /; | |
| } | |
| # Deny backup/log/temp/DB dump extensions | |
| location ~* ^.+\.(?:bak|log|old|orig|original|php~|php_bak|save|swo|swp|sql)$ { | |
| deny all; | |
| access_log off; | |
| log_not_found off; | |
| } | |
| # Block common public files that should not be served | |
| location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|readme\.html|license\.txt)$ { | |
| deny all; | |
| } | |
| # ------------------------- | |
| # PHP handling (php-fpm upstream) | |
| # ------------------------- | |
| location ~ \.(?:php|phar)(/.*)?$ { | |
| fastcgi_split_path_info ^(.+\.(?:php|phar))(/.*)$; | |
| fastcgi_intercept_errors on; | |
| fastcgi_index index.php; | |
| fastcgi_connect_timeout 10s; | |
| fastcgi_send_timeout 30s; | |
| fastcgi_read_timeout 30s; | |
| # Standard FastCGI params | |
| fastcgi_param QUERY_STRING $query_string; | |
| fastcgi_param REQUEST_METHOD $request_method; | |
| fastcgi_param CONTENT_TYPE $content_type; | |
| fastcgi_param CONTENT_LENGTH $content_length; | |
| fastcgi_param SCRIPT_NAME $fastcgi_script_name; | |
| fastcgi_param REQUEST_URI $request_uri; | |
| fastcgi_param DOCUMENT_URI $document_uri; | |
| fastcgi_param DOCUMENT_ROOT $document_root; | |
| fastcgi_param SERVER_PROTOCOL $server_protocol; | |
| fastcgi_param REQUEST_SCHEME $scheme; | |
| fastcgi_param HTTPS $https if_not_empty; | |
| fastcgi_param GATEWAY_INTERFACE CGI/1.1; | |
| fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; | |
| fastcgi_param REMOTE_ADDR $remote_addr; | |
| fastcgi_param REMOTE_PORT $remote_port; | |
| fastcgi_param SERVER_ADDR $server_addr; | |
| fastcgi_param SERVER_PORT $server_port; | |
| fastcgi_param SERVER_NAME $server_name; | |
| # Required if PHP was built with --enable-force-cgi-redirect | |
| fastcgi_param REDIRECT_STATUS 200; | |
| # Map script to real path and pass PATH_INFO | |
| fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; | |
| fastcgi_param PATH_INFO $fastcgi_path_info; | |
| # Upstream name must match your upstream config (e.g., in http{}: upstream php-fpm { server 127.0.0.1:9000; }) | |
| fastcgi_pass php-fpm; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment