-
-
Save akira28/a8cf5d3f534056842e93cec974cbb7fb to your computer and use it in GitHub Desktop.
Battle-tested Nginx configuration for Magento (source: www.hypernode.com)
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
# This is an annotated subset of the Nginx configuration from our Magento production platform @ www.hypernode.com | |
# See https://www.byte.nl/blog/magento-cacheleak-issue | |
# !!!! If you are a Hypernode customer, do not use this config as it will result in duplicate statements. !!!!! | |
user app; | |
worker_processes 4; | |
pid /var/run/nginx.pid; | |
events { | |
worker_connections 768; | |
} | |
http { | |
sendfile on; | |
tcp_nopush on; | |
tcp_nodelay on; | |
keepalive_timeout 65; | |
types_hash_max_size 2048; | |
server_tokens off; | |
server_names_hash_bucket_size 64; | |
# allows big media uploads | |
client_max_body_size 120m; | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; | |
# GeoIP support is included in the Ubuntu 12.04 Nginx. | |
# This enables logging, and the following: | |
# if ($geoip_country_code ~ (CN|ZW) ) { | |
# return 403; | |
# } | |
geoip_country /usr/share/GeoIP/GeoIP.dat; | |
gzip on; | |
gzip_disable "msie6"; | |
access_log /var/log/nginx/access.log; | |
error_log /var/log/nginx/error.log; | |
gzip_min_length 1000; | |
gzip_proxied any; | |
gzip_types text/plain text/html application/json application/xml text/css text/js application/x-javascript; | |
# Determine whether a request comes from a human, a search crawler or another bot. | |
map $http_user_agent $is_non_search_bot { | |
default ''; | |
~*(google|bing|pingdom|monitis.com|Zend_Http_Client) ''; | |
~*(http|crawler|spider|bot|search|ForusP|Wget/|Python-urllib|PHPCrawl|bGenius) 'bot'; | |
} | |
# Rate limit bots (that are not search spiders) to one PHP request per second. | |
# An empty '$limit_bots' would disable rate limiting for this requests | |
limit_req_zone $is_non_search_bot zone=bots:1m rate=1r/s; | |
limit_req_log_level error; | |
index index.html index.php; | |
server { | |
listen 80 default_server; | |
root /var/www; | |
# Android dupe request bug, https://www.byte.nl/blog/android-bug-can-kill-site-duplicate-requests | |
set $request_url "$scheme://$http_host$request_uri"; | |
if ($request_url = $http_referer) { | |
set $request_is_referer 1; | |
} | |
if ($http_user_agent ~ 'Android ([23]|4\.[0123])') { | |
set $android_buggy_ua 1; | |
} | |
set $android_dupe_bug "${request_method}${android_buggy_ua}${request_is_referer}"; | |
if ($android_dupe_bug = 'GET11') { | |
# http://en.wikipedia.org/wiki/List_of_HTTP_status_codes | |
return 429; | |
} | |
# Denied locations require a "^~" to prevent regexes (such as the PHP handler below) from matching | |
# http://nginx.org/en/docs/http/ngx_http_core_module.html#location | |
location ^~ /app/ { return 403; } | |
location ^~ /includes/ { return 403; } | |
location ^~ /media/downloadable/ { return 403; } | |
location ^~ /pkginfo/ { return 403; } | |
location ^~ /report/config.xml { return 403; } | |
location ^~ /var/ { return 403; } | |
location ^~ /lib/ { return 403; } | |
location ^~ /dev/ { return 403; } | |
location ^~ /RELEASE_NOTES.txt { return 403; } | |
location ^~ /downloader/pearlib { return 403; } | |
location ^~ /downloader/template { return 403; } | |
location ^~ /downloader/Maged { return 403; } | |
location ~* ^/errors/.+\.xml { return 403; } | |
# CVE-2015-3428 / AW_Blog vulnerability | |
# Note the .+ at the start: We want to allow url's like | |
# order=create_date, which would otherwise match. | |
if ($arg_order ~* .+(select|create|insert|update|drop|delete|concat|alter|load)) { | |
return 403; | |
} | |
# Don't skip .thumbs, this is a default directory where Magento places thumbnails | |
# Nginx cannot "not" match something, instead the target is matched with an empty block | |
# http://stackoverflow.com/a/16304073 | |
location ~ /\.thumbs { | |
} | |
# Skip .git, .htpasswd etc | |
location ~ /\. { | |
return 404; | |
} | |
set $fastcgi_root $document_root; | |
location / { | |
try_files $uri $uri/ @handler; | |
expires 30d; | |
} | |
# SUPEE 6285 | |
# Only allow the new url case sensitive lowercase, deny case insensitive | |
location ^~ /rss/order/new { | |
echo_exec @handler; | |
} | |
location ^~ /rss/catalog/notifystock { | |
echo_exec @handler; | |
} | |
location ^~ /rss/catalog/review { | |
echo_exec @handler; | |
} | |
location ~* /rss/order/new { | |
return 403; | |
} | |
location ~* /rss/catalog/notifystock { | |
return 403; | |
} | |
location ~* /rss/catalog/review { | |
return 403; | |
} | |
## Order IS important! this is required BEFORE the PHP regex | |
## Allow PHP scripts in skin and JS, but render static 404 pages when skin or js file is missing | |
## Magento has RewriteCond %{REQUEST_URI} !^/(media|skin|js)/ in default htaccess | |
location ~ ^/(skin|js)/ { | |
location ~ \.php$ { | |
echo_exec @phpfpm; | |
} | |
try_files $uri $uri/ =404; | |
expires 30d; | |
} | |
# Disallow PHP scripts in /media/ | |
# Also render static 404 pages for missing media | |
location ~ ^/media/ { | |
location ~ \.php$ { | |
return 403; | |
} | |
try_files $uri $uri/ =404; | |
expires 30d; | |
} | |
location @handler { | |
rewrite / /index.php; | |
} | |
location @fastcgi_backend { | |
# Bot rate limit, https://gist.github.com/supairish/2951524 | |
# Burst=0 (default) --WdG | |
limit_req zone=bots; | |
# server_name is read-only, so we need a temp var | |
set $my_server_name $server_name; | |
if ($my_server_name = "") { | |
set $my_server_name $http_host; | |
} | |
try_files $uri =404; | |
expires off; | |
root $fastcgi_root; | |
fastcgi_read_timeout 900s; | |
fastcgi_index index.php; | |
fastcgi_pass $fastcgi_pass; | |
include /etc/nginx/fastcgi_params; | |
fastcgi_param HTTP_AUTHORIZATION $http_authorization; | |
fastcgi_param SERVER_NAME $my_server_name; | |
fastcgi_param NGINX_REQUEST_TIME $date_gmt; | |
# If these variables are unset, set them to an empty value here | |
# so they are al least defined when fastcgi_param directives are called | |
if ($storecode = "") { | |
set $storecode ""; | |
} | |
if ($storetype = "") { | |
set $storetype ""; | |
} | |
# These are set in http.magerunmaps | |
fastcgi_param MAGE_RUN_CODE $storecode if_not_empty; | |
fastcgi_param MAGE_RUN_TYPE $storetype if_not_empty; | |
} | |
location @phpfpm { | |
set $log_handler phpfpm; | |
set $fastcgi_pass 127.0.0.1:9000; | |
echo_exec @fastcgi_backend; | |
} | |
location @hhvm { | |
set $log_handler hhvm; | |
set $fastcgi_pass 127.0.0.1:9001; | |
echo_exec @fastcgi_backend; | |
} | |
# Protection against unsecured magmi installs. User-editable | |
# so user may set it up as they want. Must be included here | |
# to catch and redirect PHP files, if this was loaded in later | |
# (after the default php-fpm handler for .php files) then we | |
# would not be able to redirect the magmi .php files (which are | |
# the ones we really MUST redirect). | |
location ~* /magmi($|/) { | |
return https://support.hypernode.com/knowledgebase/securing-access-to-magmi/; | |
} | |
location ~ .php/ { | |
rewrite ^(.*.php)/ $1 last; | |
} | |
# always execute our own handler for php-fpm, to prevent serving raw php code and to have | |
# a default when user removes configuration from ~/nginx/ | |
location ~ \.php$ { | |
echo_exec @phpfpm; | |
} | |
rewrite ^/minify/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last; | |
rewrite ^/skin/m/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last; | |
location /lib/minify/ { allow all; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment