This article is based on the php fourm Discuz (http://www.discuz.net), you should modify the configuration as needed.
Operation system: Freebsd 10.1 with ZFS
1. Enable httpready, aio, tmpfs
# echo 'accf_http_load=”YES”' >> /boot/loader.conf
# echo 'aio_load="YES"' >> /boot/loader.conf
# echo 'tmpfs /tmp tmpfs rw,mode=777 0 0' >> /etc/fstab
# kldload accf_http aio
# umount /tmp && mount /tmp
2. Create tmp mount point for nginx
# zfs create -o atime=off -o setuid=off -o checksum=off -o mountpoint=/var/tmp/nginx zroot/nginx
# chown www /var/tmp/nginx
3. Nginx configuration (nginx version 1.6)
compile nginx from port
# cd /usr/ports/www/nginx && make config && make install clean
make sure that you have enabled below modules:
FILE_AIO
GOOGLE_PERFTOOLS
HTTP
HTTP_ADDITION
HTTP_GEOIP
HTTP_REALIP
HTTP_REWRITE
HTTP_STATUS
CACHE_PURGE
HEADERS_MORE
LUA
create log folder
# mkdir /var/log/nginx
below is the configuration for nginx. Please be aware that I put the configuration in several different files.
main:
user www;
worker_processes 4; # Please change this number as needed
error_log /var/log/nginx/nginx-error.log info;
pid /var/run/nginx.pid;
google_perftools_profiles /tmp/nginx_gperf;
events:
events {
worker_connections 1024;
use kqueue;
}
http:
http {
aio sendfile;
sendfile on;
tcp_nopush on;
directio 4m; #this line can be removed because ZFS doesn't support directio
directio_alignment 4096;
recursive_error_pages on;
set_real_ip_from 127.0.0.0/8;
set_real_ip_from unix:;
real_ip_header X-Real-IP;
default_type application/octet-stream;
server_tokens off;
server_name_in_redirect off;
keepalive_timeout 120;
log_format main '$remote_addr | $time_local | $host | $request | $status | '
'$body_bytes_sent | $http_referer | $http_user_agent | '
'$http_x_forwarded_for';
log_format cache '$remote_addr | $time_local | $host | $request | $status | '
'$body_bytes_sent | $http_referer | $http_user_agent | '
'$http_x_forwarded_for | $upstream_cache_status';
client_body_temp_path /tmp/nginx_tmp_client;
fastcgi_temp_path /tmp/nginx_tmp_fcgi;
uwsgi_temp_path /tmp/nginx_tmp_uwsgi;
scgi_temp_path /tmp/nginx_tmp_sgi;
proxy_temp_path /tmp/nginx_tmp_proxy;
proxy_cache_path /var/tmp/nginx/cache levels=2:2 keys_zone=cache_disk:256m inactive=1d;
limit_req_zone $binary_remote_addr zone=scriptzone:4m rate=10r/s;
limit_req_zone $binary_remote_addr zone=staiczone:4m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=clientzone:4m;
include mime.types;
include map.conf;
include upstream.conf;
server {
listen 80 accept_filter=httpready;
include front.conf;
}
# Can use curl http://127.0.0.1:888 to check nginx status
server {
listen 127.0.0.1:888;
stub_status on;
access_log off;
}
include site/*.conf;
}
I will use map.conf and front.conf to separate static access and script access, also separate human access and robot access.
Use map.conf to sort out different access type:
map.conf:
# 基于geo的ip映射
# below are offline download servers ip address (such as thunder, qqdownload)
geo $geo {
ranges;
101.226.180.1-101.226.180.255 oldl;
111.161.24.1-111.161.24.255 oldl;
112.117.217.1-112.117.217.255 oldl;
112.90.17.1-112.90.17.255 oldl;
114.112.202.1-114.112.202.255 oldl;
114.80.183.1-114.80.189.255 oldl;
114.80.245.1-114.80.245.255 oldl;
116.55.230.1-116.55.230.255 oldl;
117.34.91.1-117.34.91.255 oldl;
118.122.36.1-118.122.36.255 oldl;
118.122.87.1-118.122.88.255 oldl;
119.120.94.1-119.120.94.255 oldl;
119.144.9.1-119.144.9.255 oldl;
119.147.41.1-119.147.41.255 oldl;
119.178.12.1-119.178.12.255 oldl;
119.188.11.1-119.188.12.255 oldl;
119.188.129.1-119.188.129.255 oldl;
119.188.13.1-119.188.15.255 oldl;
119.188.50.1-119.188.50.255 oldl;
119.189.1.1-119.189.1.255 oldl;
119.84.114.1-119.84.114.255 oldl;
119.97.178.1-119.97.178.255 oldl;
119.97.183.1-119.97.183.255 oldl;
121.10.120.1-121.10.120.255 oldl;
121.10.137.1-121.10.137.255 oldl;
121.10.24.1-121.10.24.255 oldl;
121.14.222.1-121.14.222.255 oldl;
121.14.82.1-121.14.82.255 oldl;
121.9.209.1-121.9.209.255 oldl;
121.9.246.1-121.9.246.255 oldl;
122.141.227.1-122.141.227.255 oldl;
122.141.235.1-122.141.235.255 oldl;
122.143.1.1-122.143.6.255 oldl;
122.228.241.1-122.228.241.255 oldl;
122.228.255.1-122.228.255.255 oldl;
123.129.219.1-123.129.219.255 oldl;
123.129.242.1-123.129.242.255 oldl;
123.183.223.1-123.183.223.255 oldl;
124.232.148.1-124.232.148.255 oldl;
124.95.156.1-124.95.156.255 oldl;
124.95.172.1-124.95.172.255 oldl;
124.95.173.1-124.95.173.255 oldl;
124.95.174.1-124.95.174.255 oldl;
125.221.46.1-125.221.46.255 oldl;
125.39.148.1-125.39.150.255 oldl;
125.39.72.1-125.39.72.255 oldl;
125.46.42.1-125.46.42.255 oldl;
125.78.242.1-125.78.242.255 oldl;
125.78.247.1-125.78.247.255 oldl;
180.153.115.1-180.153.115.255 oldl;
180.153.91.1-180.153.91.255 oldl;
182.118.125.1-182.118.125.255 oldl;
182.118.13.1-182.118.18.255 oldl;
182.140.142.1-182.140.142.255 oldl;
183.136.156.1-183.136.156.255 oldl;
183.60.208.1-183.60.208.255 oldl;
183.60.209.1-183.60.209.255 oldl;
183.63.33.1-183.63.33.255 oldl;
183.94.216.1-183.94.219.255 oldl;
183.94.228.1-183.94.231.255 oldl;
183.94.237.1-183.94.239.255 oldl;
211.137.100.1-211.137.100.255 oldl;
211.162.73.1-211.162.73.255 oldl;
211.98.168.1-211.98.171.255 oldl;
218.21.68.1-218.21.68.255 oldl;
218.26.232.1-218.26.232.255 oldl;
218.59.144.1-218.59.144.255 oldl;
218.6.13.1-218.6.13.255 oldl;
218.75.172.1-218.75.172.255 oldl;
219.129.83.1-219.129.83.255 oldl;
219.134.132.1-219.134.132.255 oldl;
220.113.9.1-220.113.9.255 oldl;
220.115.240.1-220.115.240.255 oldl;
220.249.103.1-220.249.103.255 oldl;
221.203.179.1-221.203.179.255 oldl;
221.204.204.1-221.204.204.255 oldl;
221.204.220.1-221.204.220.255 oldl;
221.215.87.1-221.215.87.255 oldl;
221.235.189.1-221.235.189.255 oldl;
221.235.205.1-221.235.205.255 oldl;
221.238.25.1-221.238.25.255 oldl;
221.4.246.1-221.4.246.255 oldl;
221.5.8.1-221.5.8.255 oldl;
222.141.53.1-222.141.53.255 oldl;
222.186.19.1-222.186.19.255 oldl;
222.73.133.1-222.73.133.255 oldl;
222.73.49.1-222.73.49.255 oldl;
58.222.25.1-58.222.25.255 oldl;
58.251.57.1-58.251.60.255 oldl;
58.251.61.1-58.251.61.255 oldl;
58.252.209.1-58.252.209.255 oldl;
58.254.134.1-58.254.134.255 oldl;
58.255.249.1-58.255.249.255 oldl;
58.255.250.1-58.255.253.255 oldl;
58.61.152.1-58.61.152.255 oldl;
58.61.39.1-58.61.39.255 oldl;
58.67.137.1-58.67.137.255 oldl;
60.18.146.1-60.18.146.255 oldl;
60.18.147.1-60.18.147.255 oldl;
60.19.64.1-60.19.64.255 oldl;
60.21.219.1-60.21.219.255 oldl;
60.214.64.1-60.214.64.255 oldl;
60.217.235.1-60.217.235.255 oldl;
60.221.254.1-60.221.254.255 oldl;
61.137.191.1-61.137.191.255 oldl;
61.138.177.1-61.138.177.255 oldl;
61.139.103.1-61.139.103.255 oldl;
61.147.76.1-61.147.76.255 oldl;
61.147.81.1-61.147.81.255 oldl;
61.147.94.1-61.147.94.255 oldl;
61.152.105.1-61.152.105.255 oldl;
61.178.227.1-61.178.227.255 oldl;
61.183.55.1-61.183.55.255 oldl;
61.188.190.1-61.188.190.255 oldl;
61.235.71.1-61.235.71.255 oldl;
61.54.12.1-61.54.12.255 oldl;
}
#filter access based on user-agent
map $http_user_agent $ifbot {
"~*Bot" isbot;
"~*Spider" isbot;
"~*archive" isbot;
"~*search" isbot;
"~*Yahoo" isbot;
"~Mediapartners-Google" isbot;
"~*Ruby" isbot;
"~*Player" isbot;
"~*Go http package" isbot;
"~*Lynx" isbot;
"~*Sleuth" isbot;
"~*Python" isbot;
"~*Wget" isbot;
"~*curl" isbot;
"~*perl" isbot;
"~*libfetch" isbot;
}
#filter script access
map $uri $my_filetype {
"~*.py$" script;
"~*.rb$" script;
"~*.fcgi$" script;
"~*.cgi$" script;
"~*.php$" script;
"~*.pl$" script;
}
#for discuz, usually we'll use url rewrite to rewrite *.php to *.html. Below section is about to map the *html back to *php
map $request_uri $my_uritype {
"~/topic-(.+).html$" f_static;
"~/article-([0-9]+)-([0-9]+).html$" f_static;
"~/forum-(w+)-([0-9]+).html$" f_static;
"~/thread-([0-9]+)-([0-9]+)-([0-9]+).html$" f_static;
"~/group-([0-9]+)-([0-9]+).html$" f_static;
"~/space-(username|uid)-(.+).html$" f_static;
"~/blog-([0-9]+)-([0-9]+).html$" f_static;
"~/(fid|tid)-([0-9]+).html$" f_static;
}
Use LUA to identify the robots which pretend to be a web browser
cookie.conf (for this script, please refer to http://ocdn.me/nginx-defense.html)
rewrite_by_lua '
local rdmnum = ngx.var.cookie_rdmnum
if(rdmnum == nil) then
rdmnum = math.random(999999)
end
local rdmid = ngx.md5("FreeBSD" .. ngx.var.remote_addr .. rdmnum)
if (ngx.var.cookie_rdmid ~= rdmid) then
ngx.header["Set-Cookie"] = {"rdmid=" .. rdmid, "rdmnum=" .. rdmnum}
return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.request_uri)
end
';
Front.conf
access_log /var/log/nginx/front-access.log main;
error_log /var/log/nginx/front-error.log;
# add vary to fix gzip related issue
more_set_headers 'Vary: Accept-Encoding, User-Agent';
# limit download speed
limit_rate_after 8m;
limit_rate 20k;
# limit total requests number
# 300/s should be enough for normal browse
limit_req zone=staiczone burst=10 nodelay;
limit_conn clientzone 10;
# cache purge放在最前面,保证可以正常清理
location ~ /purge(/.*) {
access_log /var/log/nginx/purge-disk.log main;
limit_conn clientzone 1;
limit_req zone=scriptzone;
include cookie.conf;
proxy_cache_purge cache_disk $host$1$is_args$args;
}
location / {
if ( $geo = "oldl" ) {
access_log /var/log/nginx/block-badip.log main;
return 301 $scheme://$remote_addr$request_uri;
break;
}
# sort out robort and spider
if ( $ifbot = "isbot" ) {
return 482;
break;
}
# use error_page for redirection. No need to worry about the strange http response code.
# We have already enabled recursive_error_pages in http part
return 481;
error_page 481 = @human;
error_page 482 = @isbot;
}
# For human browse
location @human {
internal;
error_log /var/log/nginx/human-static-error.log;
access_log /var/log/nginx/human-static-access.log cache;
if ( $request_method !~ (GET|HEAD) ) {
return 483;
break;
}
if ( $my_filetype = "script" ) {
return 483;
break;
}
if ( $my_uritype = "f_static" ) {
return 483;
break;
}
if ( $request_uri ~ (/$|/?) ) {
return 483;
break;
}
error_page 483 = @script;
include proxy.conf;
# If you only use 1 server, there is no need to use proxy_cache
proxy_cache cache_disk;
# Clear the cookie for static files. There is no need to add cookie for static files
more_clear_input_headers 'Cookie';
proxy_pass http://backend;
}
location @script {
internal;
error_log /var/log/nginx/human-script-error.log;
access_log /var/log/nginx/human-script-access.log main;
# limit access to php files
limit_req zone=scriptzone burst=3 nodelay;
# sort out the fake web browser
# exclude discuz flash upload
if ( $request_uri !~ "~mod=swfupload&action=swfupload" ) {
include cookie.conf;
}
include proxy.conf;
proxy_pass http://backend;
}
# below code is used for spider/robot access
location @isbot {
internal;
error_log /var/log/nginx/bot-static-error.log;
access_log /var/log/nginx/bot-static-access.log cache;
# for robot access, no post allowned
if ( $request_method !~ (GET|HEAD) ) {
access_log /var/log/nginx/bot-block.log main;
return 403;
break;
}
if ( $my_filetype = "script" ) {
return 484;
break;
}
if ( $my_uritype = "f_static" ) {
return 484;
break;
}
if ( $request_uri ~ (/$|/?) ) {
return 484;
}
error_page 484 = @botscript;
include proxy.conf;
# If you only use 1 server, there is no need to use proxy_cache
proxy_ignore_headers Set-Cookie Expires Cache-Control X-Accel-Expires X-Accel-Redirect;
proxy_cache cache_disk;
more_clear_input_headers 'Cookie';
proxy_pass http://backend;
}
location @botscript{
internal;
error_log /var/log/nginx/bot-script-error.log;
access_log /var/log/nginx/bot-script-access.log cache;
# limit robot access to php files
limit_conn clientzone 10;
limit_req zone=scriptzone;
include proxy.conf;
# enable cache for spider
proxy_ignore_headers Set-Cookie Expires Cache-Control X-Accel-Expires X-Accel-Redirect;
proxy_cache cache_disk;
more_clear_input_headers 'Cookie';
proxy_pass http://backend;
}
upstream.conf
upstream.conf内容
upstream backend {
server 12.34.56.78:90;
keepalive 128;
}
# if you only have 1 server, you can also use socket which is faster than IP connection
upstream backend {
server unix:/tmp/nginx-local.sock;
}
site/yourdomain.conf
server {
# use socket can save resource and it's faster than ip connecton
listen unix:/tmp/nginx-local.sock;
server_name .yourdomain.com;
index index.php;
root /path-to-your-site;
gzip on;
access_log /var/log/nginx/yourdomain-access.log main;
error_log /var/log/nginx/yourdomain-error.log;
location / {
rewrite ^([^.]*)/topic-(.+).html$ $1/portal.php?mod=topic&topic=$2 last;
rewrite ^([^.]*)/article-([0-9]+)-([0-9]+).html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
rewrite ^([^.]*)/forum-(w+)-([0-9]+).html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
rewrite ^([^.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+).html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
rewrite ^([^.]*)/group-([0-9]+)-([0-9]+).html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
rewrite ^([^.]*)/space-(username|uid)-(.+).html$ $1/home.php?mod=space&$2=$3 last;
rewrite ^([^.]*)/blog-([0-9]+)-([0-9]+).html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;
rewrite ^([^.]*)/(fid|tid)-([0-9]+).html$ $1/index.php?action=$2&value=$3 last;
rewrite ^([^.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_-]+).html$ $1/plugin.php?id=$2:$3 last;
}
# make sure that no php can execute in to data folder
location ~ /(data|config|template)/.*.php$ {
return 403;
break;
}
location ~ .*.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/tmp/php-fpm-yourdomain.sock;
fastcgi_index index.php;
expires -1;
}
}
logorotate
#!/bin/sh
today=$(date +%a)
logdir='/var/log/nginx'
if [ ! "${today}" ] || [ ! "${logdir}" ]; then
exit
else
rm -rf ${logdir}.${today}
mv ${logdir} ${logdir}.${today}
mkdir ${logdir}
service nginx reload
fi
exit
With this configuration, you can set different access rules for human access and bot access, you can also seperate static file access and script(php) access. It can spped up your website and improve the workload of your server.