Server Optimization
PHP configuration
For file uploads to work, PHP must be configured properly. The following PHP configuration variables may need to be set or configured, in your PHP php.ini file, .htaccess file, or settings.php files.
file_uploads = On
must be set to "On"upload_max_filesize = 24M
can't be larger thanpost_max_size
max_input_time = 300
small values may cause timeouts for large file uploadsmemory_limit = 64M
small values may cause out of memory errors for large file uploadsmax_execution_time = 180
small values may cause timeouts for large file uploadspost_max_size = 24M
limits the size of input submitted to the website (including attached files)
change the error reporting (so that notices aren't shown any longer):
[...]
;error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
error_reporting = E_ALL & ~E_NOTICE
[...]
https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfa...
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting
; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting
; of zero causes PHP to behave as before. Default is 1. You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
; http://www.php.net/manual/en/ini.core.php#ini.cgi.fix-pathinfo
cgi.fix_pathinfo=0
Passing Uncontrolled Requests to PHP
Many example NGINX configurations for PHP on the web advocate passing every URI ending in .php
to the PHP interpreter. Note that this presents a serious security issue on most PHP setups as it may allow arbitrary code execution by third parties.
The problem section usually looks like this:
location ~* \.php$ {
fastcgi_pass backend;
# [...]
}
Here, every request ending in .php
will be passed to the FastCGI backend. The issue with this is that the default PHP configuration tries to guess which file you want to execute if the full path does not lead to an actual file on the filesystem.
For instance, if a request is made for /forum/avatar/1232.jpg/file.php which does not exist but if /forum/avatar/1232.jpg does, the PHP interpreter will process /forum/avatar/1232.jpg instead. If this contains embedded PHP code, this code will be executed accordingly.
Options for avoiding this are:
- Set
cgi.fix_pathinfo=0
inphp.ini
. This causes the PHP interpreter to only try the literal path given and to stop processing if the file is not found.
Very useful command to check running processes
PHP-FPM Configuration
php-fpm -tt
/etc/php-fpm.d/www.conf
/etc/php-fpm.conf
https://devcoops.com/how-to-determine-the-correct-number-of-max-child-pr...
https://tideways.com/profiler/blog/an-introduction-to-php-fpm-tuning
Modify and rotate
pm.max_children = 100 # The hard-limit total number of processes allowed
pm.start_servers = 20 # When php-fpm starts, have this many processes waiting for requests
pm.min_spare_servers = 10 # Number spare processes php-fpm will create
pm.max_spare_servers = 20 # Max number of spare (waiting for connections) processes allowed to be created
pm.process_idle_timeout = 10s;
Test for handling processes:
ab -n 1000 -c 10 http://localhost/index.php
ab -n 5000 -c 20 http://localhost/index.php
ab -n 10000 -c 200 http://localhost/index.php
RAM consumption
determine Memory_per_User-->
ps aux | awk 'NR != 1 {x[$1] += $4} END{ for(z in x) {print z, x[z]"%"}}'
- ps -ylC httpd --sort:rss | awk '{sum+=$8; ++n} END {print "Tot="sum"("n")";print "Avg="sum"/"n"="sum/n/1024"MB"}'
- ps -ylC httpd | awk '{x += $8;y += 1} END {print "Average Process Size (MB): "x/((y-1)*1024)}'f
- ps -ylC httpd | awk '{x += $8;y += 1} END {print "Apache Memory Usage (MB): "x/1024; print "Average Proccess Size (MB): "x/((y-1)*1024)}'
sed -i 's|memory_limit = 128M|memory_limit = 256M|' /etc/php.ini
grep 'memory_limit' /etc/php.ini
multiple run:
sed -i 's|memory_limit = 512M|memory_limit = 256M|' /opt/php-5.6.40/lib/php.ini /opt/php-7.2.16/lib/php.ini /opt/php-7.3.3/php.ini /opt/php-7.3.3/lib/php.ini
grep 'memory_limit' /opt/php-5.6.40/lib/php.ini /opt/php-7.2.16/lib/php.ini /opt/php-7.3.3/php.ini /opt/php-7.3.3/lib/php.ini
Decrease CPU load
- yum install cpulimit
- top
- then run your resource-intensive process (like opening many pages of your website at once)
- and you'll see, which command devours all your resources (PID:__)
- cpulimit --pid 31 --limit 86
- cpulimit -e clamd -l 50
Tuning MySQL Performance
MySQLTuner is a Perl script that analyzes your MySQL performance and, based on the statistics it gathers, gives recommendations which variables you should adjust in order to increase performance. That way, you can tune your my.cnf file to tease out the last bit of performance from your MySQL server and make it work more efficiently.e with MySQLTuner
You can download the MySQLTuner script as follows:
wget http://drupal.mamatuik.com/PALS/public/CENTOS7/mysqltuner.pl
In order to run it, we must make it executable:
chmod +x mysqltuner.pl
Afterwards, we can run it. You need your MySQL root password for it:
./mysqltuner.pl
/etc/my.cnf | [mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 # Settings user and group are ignored when systemd is used. # If you need to run mysqld under a different user or group, # customize your systemd unit file for mariadb according to the # instructions in http://fedoraproject.org/wiki/Systemd max_allowed_packet=32M #Configure tmp_table_size and max_heap_table_size #Both directives should have the same size and will help you prevent disk writes. The tmp_table_size is the maximum amount of size of internal in-memory tables. In case the limit in question is exceeded the table will be converted to on-disk MyISAM table. max_heap_table_size = 32M tmp_table_size = 32M max_connections = 50 thread_cache_size = 50 #query_cache_size=SIZE #The amount of memory (SIZE) allocated for caching query results. #The default value is 0, which disables the query cache. #query_cache_type=OPTION #Set the query cache type. Possible options are as follows: #0 : Don't cache results in or retrieve results from the query cache. #1 : Cache all query results except for those that begin with SELECT S_NO_CACHE. #2 : Cache results only for queries that begin with SELECT SQL_CACHE query_cache_size = 16M query_cache_type = 1 query_cache_limit = 256K query_cache_min_res_unit = 2k interactive_timeout = 60 wait_timeout = 60 connect_timeout = 60 [mysqld_safe] log-error=/var/log/mariadb/mariadb.log pid-file=/var/run/mariadb/mariadb.pid # # include all files from the config directory # !includedir /etc/my.cnf.d [mysqldump] quick max_allowed_packet = 32M [mysql] no-auto-rehash default-character-set = utf8mb4 |
---|---|
/etc/php.ini | ;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;; ;IonCube Loader |
/etc/php.d/solr.ini | extension=solr.so |
Virtual Host Files
A method of adding virtual host containers to the Apache configuration is by using virtual host files. This is described in more detail in ApacheVhostDir.
If inserting as first host, name the file something that shows first in a directory listing, like 0Default.conf.
If adding a catch all virtual host, name the file something that will always be listed last, like zDefault.conf.
- First Virtual Host (0Default.conf)
As a first virtual host, the ServerName should be something specific that is not actually used. It can be as simple as:
<VirtualHost *:80>
ServerName fail
</VirtualHost>
- Last Virtual Host (zDefault.conf)
As a catch all virtual host, it should match everything. This can done with:
<VirtualHost *:80>
ServerAlias *
</VirtualHost>
- Restart Apache
To make your changes take effect, restart Apache.
service httpd graceful
Rest of the Server Performance
Solr | /etc/default/solr.in.sh SOLR_JAVA_MEM="-Xms256m -Xmx512m" |
---|
Multi-Processing Module | nano /etc/httpd/conf.modules.d/00-mpm.conf #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so LoadModule mpm_event_module modules/mod_mpm_event.so |
---|
nano /etc/httpd/conf.d/php.conf {php-fpm}<Directory /usr/share>
<FilesMatch \.php$>
# SetHandler application/x-httpd-php
SetHandler "proxy:fcgi://127.0.0.1:9000"
# SetHandler proxy:fcgi://php-fpm
</FilesMatch>
</Directory>
nano /etc/phpMyAdmin/config.inc.php$cfg['Servers'][$i]['auth_type'] = 'cookie';
Mailman | /etc/httpd/conf.d/mailman.conf: #ScriptAlias /cgi-bin/mailman/ /usr/lib/mailman/cgi-bin/ |
---|
PHP-configuration | /etc/httpd/conf.d/php.conf: #php_value session.save_handler "files" #php_value session.save_path "/var/lib/php/session" |
---|
httpd -V |
---|
AH00548: NameVirtualHost has no effect and will be removed in the next release /etc/httpd/conf/httpd.conf:367 AH00513: WARNING: MaxRequestWorkers of 30 is not an integer multiple of ThreadsPerChild of 25, decreasing to nearest multiple 25, for a maximum of 1 servers. Server version: Apache/2.4.6 (CentOS) Server built: Nov 5 2018 01:47:09 Server's Module Magic Number: 20120211:24 Server loaded: APR 1.4.8, APR-UTIL 1.5.2 Compiled using: APR 1.4.8, APR-UTIL 1.5.2 Architecture: 64-bit Server MPM: event threaded: yes (fixed thread count) forked: yes (variable process count) Server compiled with.... |
systemctl restart httpd php-fpm && systemctl enable httpd php-fpm
service php-5.6.40-fpm restart
service php-7.3.3-fpm restart
service php-7.2.16-fpm restart
service httpd restart
Allocate RAM wisely for Apache/etc/httpd/conf/httpd.conf | <IfModule mpm_event_module> StartServers 3 MinSpareThreads 25 MaxSpareThreads 75 ThreadLimit 64 ThreadsPerChild 25 MaxRequestWorkers 30 MaxConnectionsPerChild 1000 </IfModule> |
---|---|
ps -ylC httpd | awk '{x += $8;y += 1} END {print "Average Process Size (MB): "x/((y-1)*1024)}'f | get average process' SIZE = 18.6659 ServerLimit=RAM/2 or better (RAM - reserved memory) let's assume, reserved memory: 512MB MaxClients = 512/18.7=27 |
dmidecode -t 17 dmidecode --type memory | RAM information |
/etc/httpd/conf/httpd.conf | <IfModule prefork.c> |
list the currently loaded modules with: | httpd -M |
---|
Test for server speed
ab -n 1000 -c 20 http://localhost/index.php
Vultr
iptables -A INPUT -s 127.0.0.1 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -I INPUT 1 -m tcp -p tcp --dport 111 -j DROP
iptables -I INPUT 1 -m udp -p udp --dport 111 -j DROP
Networking Tuning Tips
echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
cat /etc/sysctl.conf
# Red Hat recommends not using multiple interfaces in the same network segment.
# However, if this is unavoidable, you can use arp_filter to prevent ARP Flux
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_tuni$
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
marked "red" - is an additional setting
== Portmapper servers ==
Portmapper is a service usually used with NFS. When this is not properly firewalled, it can be abused to conduct DDOS attacks. We recommend that all portmapper services be behind a firewall, and restricted to only IPs that need to contact them.
For Linux machines, please add firewall rules to block port 111 on both UDP and TCP:
iptables -I INPUT 1 -m tcp -p tcp --dport 111 -j DROP
iptables -I INPUT 1 -m udp -p udp --dport 111 -j DROP
Please see https://blog.cloudflare.com/reflections-on-reflections/ for more information on reflection attacks.