Trying to secure Nginx the most for Joomla CMS under Ubuntu 11.10
<!-- to be saved at /usr/share/nginx/html/404.html --> | |
<html lang="en"><head><meta charset="utf-8"> | |
<meta name="viewport" content="initial-scale=1, minimum-scale=1, width=device-width"> | |
<title>Error 404 (Not Found)!!1</title> | |
<style> | |
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}} | |
</style> | |
<link type="text/css" rel="stylesheet" href="chrome-extension://cpngackimfmofbokmjmljamhdncknpmg/style.css"><script type="text/javascript" charset="utf-8" src="chrome-extension://cpngackimfmofbokmjmljamhdncknpmg/page_context.js"></script></head><body screen_capture_injected="true"> | |
<!-- | |
<a href="//www.google.com/"> | |
<img src="//www.google.com/images/errors/logo_sm.gif" alt="Google"></a> | |
--> | |
<p><b>404.</b> <ins>That.s an error.</ins> | |
<h2>The page you were looking for appears to | |
have been moved, deleted or does not exist.</h2> | |
</p><p>The requested URL <code>/wfwefwefwef</code> was not found on this server. <ins>That.s all we know.</ins> | |
</p> | |
<p> | |
<a href="http://www.waltercedric.com/contact.html" target="new">Please contact us to report a problem.</a> | |
</p> | |
<script type="text/javascript"> | |
var GOOG_FIXURL_LANG = 'en'; | |
var GOOG_FIXURL_SITE = 'http://www.waltercedric.com' | |
</script> | |
<script type="text/javascript" | |
src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"> | |
</script> | |
<ul> | |
<li>Try returning to the <a href="http://www.waltercedric.com">home page</a> and finding the page you were looking for.</li> | |
</ul> | |
<br /> | |
<h3>Sorry for your inconvenience</h3> | |
</body></html> |
# dedicated server with 8GB of RAM and php5-fpm | |
# issue a 'service php5-fpm restart' after changes | |
extension=apc.so | |
apc.enabled = 1 | |
apc.shm_segments = 1 | |
apc.shm_size = 512M | |
apc.optimization = 0 | |
apc.num_files_hint = 512 | |
apc.user_entries_hint = 1024 | |
apc.ttl = 0 | |
apc.user_ttl = 0 | |
apc.gc_ttl = 600 | |
apc.cache_by_default = 1 | |
apc.filters = "apc\.php$" | |
apc.slam_defense = 0 | |
apc.use_request_time = 1 | |
apc.mmap_file_mask = /tmp/apc.XXXXXX | |
;OR apc.mmap_file_mask = /dev/zero | |
apc.file_update_protection = 2 | |
apc.enable_cli = 0 | |
apc.max_file_size = 2M | |
apc.stat = 1 | |
apc.write_lock = 1 | |
apc.report_autofilter = 0 | |
apc.include_once_override = 0 | |
apc.rfc1867 = 0 | |
apc.rfc1867_prefix = "upload_" | |
apc.rfc1867_name = "APC_UPLOAD_PROGRESS" | |
apc.rfc1867_freq = 0 | |
apc.localcache = 1 | |
apc.localcache.size = 512 | |
apc.coredump_unmap = 0 | |
apc.stat_ctime = 0 |
## Reusable settings for PHP CMS, targetted at joomla | |
# | |
# latest version at https://gist.github.com/1620307 | |
# author cedric.walter, www.waltercedric.com | |
# to be saved in /etc/nginx/conf/joomla.conf | |
server_name_in_redirect off; | |
location / { | |
expires 1d; | |
# Note that nginx does not use .htaccess. | |
# This will allow for SEF URL’s | |
try_files $uri $uri/ /index.php?$args; | |
} | |
################################################################# | |
# Optimizations # | |
################################################################# | |
################################################################# | |
# Hardening # | |
################################################################# | |
server_tokens off; | |
# File uploads | |
client_max_body_size 10M; | |
client_body_buffer_size 128k; | |
# deny running scripts inside writable directories | |
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ { | |
return 403; | |
error_page 403 /403_error.html; | |
} | |
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). | |
location ~ /\. { | |
deny all; | |
access_log off; | |
log_not_found off; | |
} | |
# Only allow these request methods GET|HEAD|POST | |
# Do not accept DELETE, SEARCH and other methods | |
if ($request_method !~ ^(GET|HEAD|POST)$ ) { | |
return 444; | |
} | |
################################################################# | |
# Error # | |
################################################################# | |
error_page 404 /404.html; | |
location = /404.html { | |
root /usr/share/nginx/html; | |
} | |
# redirect server error pages to the static page /50x.html | |
# | |
error_page 500 502 503 504 /50x.html; | |
location = /50x.html { | |
root /usr/share/nginx/html; | |
} | |
################################################################# | |
# Rewrite # | |
################################################################# | |
# Add www to all urls | |
if ($host ~* ^([a-z0-9\-]+\.(be|fr|nl|de))$) { | |
rewrite ^(.*)$ http://www.$host$1 permanent; | |
} | |
proxy_cache_key "$host$request_uri$cookie_sessioncookie"; | |
# Pass all .php files onto a php-fpm/php-fcgi server. | |
# see https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ | |
# see http://cnedelcu.blogspot.com/2010/05/nginx-php-via-fastcgi-important.html | |
location ~ \.php$ { | |
# Zero-day exploit defense. | |
# http://forum.nginx.org/read.php?2,88845,page=3 | |
# Won't work properly (404 error) if the file is not stored on this server, which is entirely possible with php-fpm/php-fcgi. | |
# Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked. | |
try_files $uri =404; | |
fastcgi_split_path_info ^(.+\.php)(/.+)$; | |
include fastcgi_params; | |
fastcgi_index index.php; | |
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; | |
# fastcgi_intercept_errors on; | |
#fastcgi_pass php; | |
fastcgi_pass 127.0.0.1:9000; | |
} |
# These are my settings, run with the default mysql settings and then run after 24h tuning-primer.sh | |
# https://launchpad.net/mysql-tuning-primer | |
# and follow the recommendations! | |
# | |
# latest version at https://gist.github.com/1620307 | |
# author cedric.walter, www.waltercedric.com | |
# to be saved in /etc/mysql/my.cnf | |
[client] | |
port = 3306 | |
socket = /var/run/mysqld/mysqld.sock | |
[mysqld_safe] | |
socket = /var/run/mysqld/mysqld.sock | |
nice = 0 | |
# * IMPORTANT | |
# If you make changes to these settings and your system uses apparmor, you may | |
# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. | |
# | |
[mysqld] | |
user = mysql | |
socket = /var/run/mysqld/mysqld.sock | |
port = 3306 | |
basedir = /usr | |
datadir = /var/lib/mysql | |
tmpdir = /tmp | |
skip-external-locking | |
# Instead of skip-networking the default is now to listen only on | |
# localhost which is more compatible and is not less secure. | |
#bind-address = 127.0.0.1 | |
key_buffer = 100M | |
max_allowed_packet = 16M | |
thread_stack = 192K | |
thread_cache_size = 8 | |
myisam-recover = BACKUP | |
#max_connections = 100 | |
table_cache = 1512 | |
table_definition_cache = 1512 | |
tmp_table_size = 64M | |
read_buffer_size = 32M | |
#thread_concurrency = 10 | |
query_cache_limit = 1M | |
query_cache_size = 100M | |
log_error = /var/log/mysql/error.log | |
log_slow_queries = /var/log/mysql/mysql-slow.log | |
long_query_time = 2 | |
#log-queries-not-using-indexes | |
# The following can be used as easy to replay backup logs or for replication. | |
# note: if you are setting up a replication slave, see README.Debian about | |
# other settings you may need to change. | |
#server-id = 1 | |
#log_bin = /var/log/mysql/mysql-bin.log | |
expire_logs_days = 10 | |
max_binlog_size = 100M | |
# * InnoDB | |
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. | |
# Read the manual for more InnoDB related options. There are many! | |
# | |
# * Security Features | |
# | |
# Read the manual, too, if you want chroot! | |
# chroot = /var/lib/mysql/ | |
# | |
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". | |
# | |
# ssl-ca=/etc/mysql/cacert.pem | |
# ssl-cert=/etc/mysql/server-cert.pem | |
# ssl-key=/etc/mysql/server-key.pem | |
[mysqldump] | |
quick | |
quote-names | |
max_allowed_packet = 16M | |
[mysql] | |
[isamchk] | |
key_buffer = 16M | |
# | |
# * IMPORTANT: Additional settings that can override those from this file! | |
# The files must end with '.cnf', otherwise they'll be ignored. | |
# | |
!includedir /etc/mysql/conf.d/ |
## latest version at https://gist.github.com/1620307 | |
# | |
# author cedric.walter, www.waltercedric.com | |
# to be saved in /etc/nginx/nginx.conf | |
user www-data; | |
# = to the number of CPU - 1 | |
# you may want to leave one core for the system and its interrupts | |
# and the other cores for the web server | |
worker_processes 4; | |
pid /var/run/nginx.pid; | |
events { | |
# worker_connections : This is the amount of client connections a | |
# single child process will handle by themselves at any one time. | |
# (default: 1024) Note: Multiply worker_processes times worker_connections | |
# for the total amount of connections Nginx will handle. Our example is | |
# setup to handle 3*64=192 concurrent connections in total. Clients who | |
# connect after the max has been reached will be denied access. | |
worker_connections 1024; | |
# multi_accept on; | |
} | |
http { | |
## | |
# Basic Settings | |
## | |
sendfile on; | |
tcp_nopush on; | |
tcp_nodelay on; | |
types_hash_max_size 2048; | |
## Hardening #### | |
server_tokens off; | |
### (default is 8k or 16k) The directive specifies the client request body buffer size. | |
# If the request body is more than the buffer, then the entire request body or some part is written in a temporary file. | |
client_body_buffer_size 8K; | |
### Directive sets the headerbuffer size for the request header from client. For the overwhelming | |
### majority of requests a buffer size of 1K is sufficient. Increase this if you have a custom header | |
### or a large cookie sent from the client (e.g., wap client). | |
client_header_buffer_size 1k; | |
### Directive assigns the maximum accepted body size of client request, indicated by the line Content-Length | |
### in the header of request. If size is greater the given one, then the client gets the error | |
### "Request Entity Too Large" (413). Increase this when you are getting file uploads via the POST method. | |
client_max_body_size 2m; | |
### Directive assigns the maximum number and size of buffers for large headers to read from client request. | |
### By default the size of one buffer is equal to the size of page, depending on platform this either 4K or 8K, | |
### if at the end of working request connection converts to state keep-alive, then these buffers are freed. | |
### 2x1k will accept 2kB data URI. This will also help combat bad bots and DoS attacks. | |
large_client_header_buffers 2 1k; | |
### The first parameter assigns the timeout for keep-alive connections with the client. | |
### The server will close connections after this time. The optional second parameter assigns | |
### the time value in the header Keep-Alive: timeout=time of the response. This header can | |
### convince some browsers to close the connection, so that the server does not have to. Without | |
### this parameter, nginx does not send a Keep-Alive header (though this is not what makes a connection "keep-alive"). | |
keepalive_timeout 300 300; | |
### Directive sets the read timeout for the request body from client. | |
### The timeout is set only if a body is not get in one readstep. If after | |
### this time the client send nothing, nginx returns error "Request time out" | |
### (408). The default is 60. | |
client_body_timeout 10; | |
### Directive assigns timeout with reading of the title of the request of client. | |
### The timeout is set only if a header is not get in one readstep. If after this | |
### time the client send nothing, nginx returns error "Request time out" (408). | |
client_header_timeout 10; | |
### Directive assigns response timeout to client. Timeout is established not on entire | |
### transfer of answer, but only between two operations of reading, if after this time | |
### client will take nothing, then nginx is shutting down the connection. | |
send_timeout 10; | |
### Directive describes the zone, in which the session states are stored i.e. store in slimits. ### | |
### 1m can handle 32000 sessions with 32 bytes/session, set to 5m x 32000 session ### | |
limit_zone slimits $binary_remote_addr 5m; | |
### Control maximum number of simultaneous connections for one session i.e. ### | |
### restricts the amount of connections from a single ip address ### | |
limit_conn slimits 5; | |
### Solve upstream sent too big header while reading response header from upstream | |
### http://wiki.nginx.org/HttpProxyModule | |
# This directive set the buffer size, into which will be read the first part of the response, obtained from the proxied server. | |
# Default: 4k|8k | |
proxy_buffer_size 128k; | |
# This directive sets the number and the size of buffers, into which will be read the answer, obtained from the proxied server. | |
# By default, the size of one buffer is equal to the size of page. Depending on platform this is either 4K or 8K. | |
proxy_buffers 4 256k; | |
proxy_busy_buffers_size 256k; | |
fastcgi_buffers 8 16k; | |
fastcgi_buffer_size 32k; | |
# server_names_hash_bucket_size 64; | |
# server_name_in_redirect off; | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; | |
## | |
# Logging Settings | |
## | |
access_log /var/log/nginx/access.log; | |
error_log /var/log/nginx/error.log; | |
## | |
# Gzip Settings | |
## | |
gzip on; | |
gzip_http_version 1.1; | |
gzip_vary on; | |
gzip_comp_level 6; | |
gzip_proxied any; | |
gzip_types text/plain text/html text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js; | |
gzip_buffers 16 8k; | |
gzip_disable "MSIE [1-6]\.(?!.*SV1)"; | |
################################################################# | |
# Caching # | |
################################################################# | |
location ~* \.(ico|pdf|flv)$ { | |
access_log off; | |
expires 1y; | |
} | |
location = /robots.txt { | |
allow all; | |
log_not_found off; | |
access_log off; | |
} | |
## | |
# http://code.google.com/speed/page-speed/docs/caching.html#LeverageBrowserCaching | |
## | |
location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ { | |
access_log off; | |
expires 1y; | |
add_header Pragma public; | |
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; | |
} | |
## | |
# Virtual Host Configs | |
## | |
include /etc/nginx/conf.d/*.conf; | |
include /etc/nginx/sites-enabled/*; | |
} | |
#mail { | |
# # See sample authentication script at: | |
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript | |
# | |
# # auth_http localhost/auth.php; | |
# # pop3_capabilities "TOP" "USER"; | |
# # imap_capabilities "IMAP4rev1" "UIDPLUS"; | |
# | |
# server { | |
# listen localhost:110; | |
# protocol pop3; | |
# proxy on; | |
# } | |
# | |
# server { | |
# listen localhost:143; | |
# protocol imap; | |
# proxy on; | |
# } | |
#} |
## latest version at https://gist.github.com/1620307 | |
# | |
# author cedric.walter, www.waltercedric.com | |
# to be saved in /etc/php5/fpm/php5-fpm.conf | |
# 512MB of ram will offer you a maximum of 30 children and a minimum of 5. | |
# 2048MB of ram will offer you a maximum of 60 children and a minimum of 20. | |
# But be warned that these may not apply for the content you are processing with PHP. Image manipulation will require a far larger amount of ram and should be limited to less threads. | |
pm = dynamic | |
; The number of child processes to be created when pm is set to 'static' and the | |
; maximum number of child processes to be created when pm is set to 'dynamic'. | |
; This value sets the limit on the number of simultaneous requests that will be | |
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. | |
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP | |
; CGI. | |
; Note: Used when pm is set to either 'static' or 'dynamic' | |
; Note: This value is mandatory. | |
pm.max_children = 120 | |
; The number of child processes created on startup. | |
; Note: Used only when pm is set to 'dynamic' | |
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 | |
pm.start_servers = 30 | |
; The desired minimum number of idle server processes. | |
; Note: Used only when pm is set to 'dynamic' | |
; Note: Mandatory when pm is set to 'dynamic' | |
pm.min_spare_servers = 30 | |
; The desired maximum number of idle server processes. | |
; Note: Used only when pm is set to 'dynamic' | |
; Note: Mandatory when pm is set to 'dynamic' | |
pm.max_spare_servers = 60 | |
; The number of requests each child process should execute before respawning. | |
; This can be useful to work around memory leaks in 3rd party libraries. For | |
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. | |
; Default Value: 0 | |
;pm.max_requests = 500 |
server { | |
### The port number (80 for http) | |
listen 80; | |
### The domain name | |
server_name example.com www.example.com; | |
### The public root folder of the site | |
root /var/www/vhosts/example.com/httpdocs; | |
### The default index file. | |
index index.php; | |
### Where to write the site access log | |
access_log /var/www/vhosts/example.com/logs/example.log; | |
# Stop deep linking or hot linking | |
# apply this rule on any location that’s an image using Regexp | |
# block empty blocked or whiteliste referers | |
# test proper operation using http://altlab.com/hotlinkchecker.php | |
location ~* \.(png|gif|jpg|jpeg|swf|ico)(\?[0-9]+)?$ { | |
valid_referers none blocked example.com www.example.com ~\.google\. ~\.yahoo\. ~\.bing\. ~\.facebook\. ~\.fbcdn\.; | |
if ($invalid_referer) { | |
# return 403; | |
rewrite ^/images/(.*)\.(gif|jpg|jpeg|png)$ http://www.example.com/stop.jpg last; | |
} | |
} | |
### Password Protect /administrator area | |
location ~ /administrator/.*) { | |
auth_basic "Restricted"; | |
auth_basic_user_file /var/www/vhosts/example.com/httpdocs/passwd; | |
} | |
include /etc/nginx/conf/joomla.conf; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Well, everything is good except one issue: accessing /administrator URL I always got index.php downloaded instead of executed.
At the same time, accessing root of the site (I mean frontend) works fine.
Also "location ~ /administrator/.*)" looks like a bug -- I mean this lonely closing bracket.