Disabling Unused Services:
MariaDB
- This is the default database service for CloudPanel, compatible with MySQL instructions
- Disable it if you are not using this database
sudo systemctl disable mariadb --now
PHP.FPM
- These are services that allow the execution of PHP applications
- Disable them if you do not use PHP applications; you can disable only the unused versions. PHP sites use PHP 8.0 by default and can be changed via the configuration panel. If PHP 8.0 is disabled, some processes such as creating PHP sites may fail.
sudo systemctl disable php7.1-fpm --now sudo systemctl disable php7.2-fpm --now sudo systemctl disable php7.3-fpm --now sudo systemctl disable php7.4-fpm --now sudo systemctl disable php8.0-fpm --now sudo systemctl disable php8.1-fpm --now sudo systemctl disable php8.2-fpm --now sudo systemctl disable php8.3-fpm --now sudo systemctl disable php8.4-fpm --now
Less Commonly Used Service Packages:
- Memcached: An object caching system equivalent to Redis
- Postfix: A service for sending and delivering email messages
- ProFTPD: A service that enables FTP access
- Uncomplicated Firewall: A good firewall to protect open ports on the operating system when exposed to the internet
- Varnish: A good page caching system but dependent on web service manipulation
sudo systemctl disable memcached --now sudo systemctl disable postfix --now sudo systemctl disable proftpd --now sudo systemctl disable ufw --now sudo systemctl disable varnish --now
Important Panel Settings:
- Panel site backups: https://[domain]/admin/remote-backup
- Panel domain (default port: 8443): https://[domain]/admin/settings
- Panel timezone: https://[domain]/admin/instance/settings
- User 2FA: https://[domain]/security
- User timezone: https://[domain]/settings
Important Shell Settings:
Redis recommends that the vm.overcommit_memory setting should always be 1, and that setting a bind address, which is not yet available, can prevent Redis from starting. You can run the lines below to add vm.overcommit_memory = 1 and net.ipv4.ip_nonlocal_bind = 1 to the /etc/sysctl.conf file and then apply the configuration by running sysctl -p.
{
# 1. Handle vm.overcommit_memory
if grep -q "^vm.overcommit_memory" /etc/sysctl.conf; then
sudo sed -i 's/^vm.overcommit_memory.*/vm.overcommit_memory = 1/' /etc/sysctl.conf
else
echo "vm.overcommit_memory = 1" | sudo tee -a /etc/sysctl.conf
fi
# 2. Handle net.ipv4.ip_nonlocal_bind
if grep -q "^net.ipv4.ip_nonlocal_bind" /etc/sysctl.conf; then
sudo sed -i 's/^net.ipv4.ip_nonlocal_bind.*/net.ipv4.ip_nonlocal_bind = 1/' /etc/sysctl.conf
else
echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.conf
fi
# 3. Apply changes immediately
sudo sysctl -p
}You can check if Redis shows this recommendation or any other error in its log by running:
sudo tail -f /var/log/redis/redis-server.log
You can monitor the queries executed on Redis with this command:
redis-cli monitor
Important vhost Settings:
Main nginx Configuration, Including Removal of the Old GeoIP and Updating Security Header Rules:
Resets nginx rules, including removal of GeoIP, performance and security optimizations, usually without impact on applications:
########## Write /usr/local/bin/cf-fail2ban.sh file to enable CloudFlare ban/unban calls ##########
sudo tee /usr/local/bin/cf-fail2ban.sh > /dev/null << 'EOF'
#!/bin/bash
ACTION="$1"
NAME="$2"
IP="$3"
CF_ACCOUNT="<<cf account id>>"
CF_TOKEN="<<cf Account.Account Firewall Access Rules token>>"
CF_TARGET="ip"
API_URL="https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT}/firewall/access_rules/rules"
if [ "$ACTION" = "ban" ]; then
curl -s -o /dev/null -X POST "$API_URL" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"mode\":\"block\",\"configuration\":{\"target\":\"$CF_TARGET\",\"value\":\"$IP\"},\"notes\":\"Fail2Ban $NAME\"}"
elif [ "$ACTION" = "unban" ]; then
RULE_ID=$(curl -s -X GET "$API_URL?mode=block&configuration.target=$CF_TARGET&configuration.value=$IP&page=1&per_page=1" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
| jq -r '.result[0].id // empty')
if [ -n "$RULE_ID" ]; then
curl -s -o /dev/null -X DELETE "$API_URL/$RULE_ID" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json"
fi
fi
EOF
########## Make /usr/local/bin/cf-fail2ban.sh executable ##########
sudo chmod +x /usr/local/bin/cf-fail2ban.sh
########## Update /etc/fail2ban/action.d/ui-custom-action.conf to trigger CF script by Fail2Ban ##########
sudo grep -q "cf-fail2ban.sh ban" /etc/fail2ban/action.d/ui-custom-action.conf || \
sudo sed -i 's|^actionban = |actionban = /usr/local/bin/cf-fail2ban.sh ban "<name>" "<ip>"\n |' /etc/fail2ban/action.d/ui-custom-action.conf
sudo grep -q "cf-fail2ban.sh unban" /etc/fail2ban/action.d/ui-custom-action.conf || \
sudo sed -i 's|^actionunban = |actionunban = /usr/local/bin/cf-fail2ban.sh unban "<name>" "<ip>"\n |' /etc/fail2ban/action.d/ui-custom-action.conf
########## Add crontab to read cloudflare/ips and write conf.d/cloudflare_realip.conf for nginx ##########
(sudo crontab -l 2>/dev/null | grep -v 'cloudflare_realip.conf'; echo "49 7 * * * sed -e 's/allow/set_real_ip_from/' -e '/deny all;/d' /etc/nginx/cloudflare/ips > /etc/nginx/conf.d/cloudflare_realip.conf && systemctl reload nginx") | sudo crontab -
########## Rewrite nginx.conf file ##########
sudo tee /etc/nginx/nginx.conf > /dev/null << 'EOF'
user root;
worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 8192;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 2000;
# multi_accept on;
}
http {
real_ip_recursive on;
set_real_ip_from 127.0.0.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
include /etc/nginx/conf.d/cloudflare_realip.conf;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format cloudflare '$http_cf_connecting_ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
tcp_nopush on;
tcp_nodelay on;
client_max_body_size 64M;
keepalive_timeout 65;
types_hash_max_size 2048;
server_names_hash_bucket_size 128;
server_tokens off;
port_in_redirect off;
disable_symlinks if_not_owner from=/home/;
map $scheme $fastcgi_https { ## Detect when HTTPS is used
default off;
https on;
}
include /etc/nginx/blocked_ips;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_prefer_server_ciphers on;
ssl_conf_command Options KTLS;
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/nginx/ssl/dhparams.pem;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
limit_req_zone $binary_remote_addr zone=limit:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=static:5m rate=30r/s;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
include /etc/nginx/sites-enabled/*.conf;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
}
EOF
########## Rewrite global_settings file ##########
sudo tee /etc/nginx/global_settings > /dev/null << 'EOF'
### Include security headers for the server block
include /etc/nginx/security_headers;
### BEGIN Enabling Nostr domain validation and Lightning Address
location ~ ^/.well-known/(nostr.json|lnurlp/.*) {
add_header Content-Type application/json always;
add_header Access-Control-Allow-Origin * always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header alt-svc 'h3=":443"; ma=86400' always;
}
### END Enabling Nostr domain validation and Lightning Address
### BEGIN Settings for Traffic Advice
location = /.well-known/traffic-advice {
types { }
default_type "application/trafficadvice+json; charset=utf-8";
allow all;
}
### END Settings for Traffic Advice
### BEGIN Redirect security.txt
location /.well-known/security.txt {
if ($host != 'icc.gg') {
return 301 https://icc.gg/.well-known/security.txt;
}
}
### END Redirect security.txt
### BEGIN Deny access to hidden files and version control
location ~ /\.(ht|svn|git) {
deny all;
access_log off;
log_not_found off;
}
### END Deny access to hidden files and version control
### BEGIN Block for WordPress legacy RPC
location = /xmlrpc.php {
deny all;
}
### END Block for WordPress legacy RPC
EOF
########## Write global_settings file ##########
sudo tee /etc/nginx/security_headers > /dev/null << 'EOF'
### BEGIN Header security standards
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Permissions-Policy "geolocation=(), microphone=(), accelerometer=(), camera=(), gyroscope=(), magnetometer=(), payment=(), usb=(), display-capture=(), midi=()" always;
add_header Content-Security-Policy "img-src https: data: blob:; script-src 'unsafe-inline' 'unsafe-eval'; script-src-elem 'self' 'unsafe-inline' blob: https: data:; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline' https:; frame-src 'self' blob: https:; worker-src 'self' blob:; frame-ancestors 'self'; object-src 'self'; base-uri about: https:; upgrade-insecure-requests;" always;
add_header alt-svc 'h3=":443"; ma=86400' always;
### END Header security standards
EOF
########## Rewrite global_settings file ##########
sudo tee /etc/nginx/sites-enabled/default.conf > /dev/null << 'EOF'
### 1. HTTP (Port 80) Catch-All: Redirects ALL HTTP traffic to HTTPS
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Upgrades all port 80 traffic to HTTPS preserving the requested domain
return 301 https://$host$request_uri;
}
### 2. HTTPS (Port 443) Catch-All: Drops unmatched SSL/TLS connections
server {
listen 443 default_server ssl;
listen [::]:443 default_server ssl;
listen 443 quic reuseport default_server;
listen [::]:443 quic reuseport default_server;
server_name _;
# Aborts TLS handshake if domain SNI does not match any vhost block
ssl_reject_handshake on;
# Fallback to close connection cleanly without response headers
return 444;
}
EOF
########## Remove all legacy GEOIP features ##########
sudo find /etc/nginx -type f -exec sed -i '/GEOIP_/Id' {} +
sudo rm -rf /etc/nginx/geoip
sudo rm -f /etc/nginx/proxy.conf
sudo rm -f /etc/nginx/modules-enabled/50-mod-http-geoip.conf
sudo rm -f /usr/share/nginx/modules-available/mod-http-geoip.conf
########## Rename to disable all extra modules on nginx but brotli ##########
sudo bash -c 'cd /etc/nginx/modules-enabled && for f in *.conf; do [[ "$f" != "50-mod-ngx-brotli.conf" ]] && mv "$f" "${f%.conf}.disabled"; done'
########## Rewrite custom-domain.conf file ##########
DOMAIN=$(sudo grep -m1 -E '^\s*server_name\s+' /etc/nginx/sites-enabled/custom-domain.conf | sed -e 's/^[[:space:]]*server_name[[:space:]]\+//' -e 's/;.*//')
cat << 'EOF' | sed "s/{{DOMAIN}}/$DOMAIN/g" | sudo tee /etc/nginx/sites-enabled/custom-domain.conf > /dev/null
server {
listen 443 quic;
listen 443 ssl;
listen [::]:443 quic;
listen [::]:443 ssl;
http2 on;
http3 on;
ssl_certificate_key /etc/nginx/ssl-certificates/custom-domain.key;
ssl_certificate /etc/nginx/ssl-certificates/custom-domain.crt;
server_name {{DOMAIN}};
client_max_body_size 5048M;
root /home/clp/htdocs/app/files/public;
#access_log /home/clp/logs/nginx/access.log;
error_log /home/clp/logs/nginx/error.log;
add_header Cache-Control no-transform;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $http_host;
proxy_pass https://127.0.0.1:8443/;
proxy_max_temp_file_size 0;
proxy_connect_timeout 7200;
proxy_send_timeout 7200;
proxy_read_timeout 7200;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
}
}
EOF
clear
########## Execute crontab line, test and restart fail2ban & nginx ##########
sudo sed -e 's/allow/set_real_ip_from/' -e '/deny all;/d' /etc/nginx/cloudflare/ips | sudo tee /etc/nginx/conf.d/cloudflare_realip.conf > /dev/null
sudo fail2ban-client -t && sudo fail2ban-client reload
sudo nginx -t && sudo systemctl reload nginxInstallation of Specific Systems on CloudPanel:
In YOURLS installations, where it is necessary to change the search from index.php to yourls-loader.php, modify the entry try_files $uri $uri/ /index.php?$args; as follows:
### BEGIN custom entry for YOURLS try_files $uri $uri/ /yourls-loader.php$is_args$args; # try_files $uri $uri/ /index.php?$args; ### END custom entry for YOURLS
Done!