This guide walks you through the process of setting up a secure and optimized VPS using Apache, MySQL, PHP, and Cloudflare. It covers everything from system updates to security headers and performance enhancements. Of course, the information presented here is not something new, but it is collected in one place and well organized check list.
Create a Swap File
Swap helps when your system runs out of physical RAM (in case of excessive load or DDoS).
Here’s how to create a 2GB swap file:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Disable Core Dump Files (Crash Dumps)
Core dumps are generated when applications crash, and they can consume a lot of space. It can take a second to fill an entire drive with crash dumps and they are only useful for professional developers who will actually analyze and reverse them.
Here’s how to disable them system-wide:
Set limits in limits.conf:
sudo nano /etc/security/limits.conf
Add these lines at the end:
* hard core 0
* soft core 0
Disable Core Dumps in sysctl:
sudo nano /etc/sysctl.conf
Add:
fs.suid_dumpable = 0
Run:
sudo sysctl -p
Disable Core Dumps via systemd:
sudo systemctl edit — full systemd-coredump
Find this section:
[Service]
Set:
ExecStart=
ExecStart=/lib/systemd/systemd-coredump — backtrace=no — journal=none — storage=none
Restart service:
sudo systemctl daemon-reexec
Disable IPv6 (Recommended Unless Needed)
You need to disable unused interfaces. Disabling IPv6 will minimize attack vectors and make it easier to develop firewall rules.
Edit sysctl.conf:
sudo nano /etc/sysctl.conf
Add these lines at the end:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
Apply changes:
sudo sysctl -p
Change SSH Port (Optional)
I recommend changing the SSH port from the default one. If you don’t do this, many automated bots will try to bruteforce your server, creating unwanted load, logs and possible security risks. This also helps the attacker identify your server and its structure, what services are you using. Before changing the SSH port, make sure the port is open and your firewall is not blocking it.
To change the port edit SSH config:
sudo nano /etc/ssh/sshd_config
Find the line #Port 22 and change it to something like Port 2289
Optional security tweaks in the same file:
PermitRootLogin no
PasswordAuthentication no
Restart SSH Server:
sudo systemctl restart ssh
Update the Server & Install Essentials
Let’s start with installing the LAMP stack. I prefer Apache instead of Ngnix since it is more classic and has support for .htaccess CMS. Performance does not really play a role for small projects.
Start by updating the server:
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
sudo do-release-upgrade
Install the basic stack (Apache + MySQL + PHP):
sudo apt install apache2
sudo apt install mysql-server
sudo apt install php libapache2-mod-php php-mysql
sudo apt install php-gd php-curl php-mbstring php-zip unzip
To see all php extensions (use Q to exit):
apt search php- | less
Create a MySQL Database and User
Enter MySQL:
sudo mysql
Here is an example how to create basic database and a user:
CREATE DATABASE example_database;
CREATE USER ‘example_user’@’%’ IDENTIFIED BY ‘password’;
ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘password’;
GRANT ALL ON example_database.* TO ‘example_user’@’%’;
FLUSH PRIVILEGES;
EXIT;
Disable Directory Listing
By default Apache displays all the content inside the directory if no index.html file is present. This possesses great security risks and needs to be disabled by all times.
Edit the Apache config:
sudo nano /etc/apache2/apache2.conf
Find lines with Options Indexes and remove Indexes.
Restart Apache:
sudo service apache2 restart
Hide Apache & PHP Versions
Hiding Apache and PHP versions helps protect your web server. If attackers know exact versions, they can exploit known vulnerabilities. Hiding this info does not affect server performance but adds difficulty for hackers.
To hide Apache version edit apache2.conf:
sudo nano /etc/apache2/apache2.conf
Add the following lines:
ServerSignature Off
ServerTokens Prod
To hide PHP Version edit php.ini:
sudo nano /etc/php/*/apache2/php.ini
Set expose_php = Off
Don’t forget to restart Apache after every manipulation:
sudo service apache2 restart
Configure Apache log rotation (Optional)
Default log rotation for Apache is 14 days which sometimes may be not enough (especially if you process payments). In this case, it makes sense to increase the storage period for logs.
Edit the Apache logrotate config:
sudo nano /etc/logrotate.d/apache2
Replace rotate 14 with the number of days you want (for example rotate 30)
Add .htaccess Rules
A very important step is to set up the htaccess file. While most CMS generate its own htaccess, you still may need to add a few rules to make your server more secure.
Inside your site’s root directory (/var/www/html or similar), create/edit .htaccess, here is a few example rules (this is just an example, never leave important files in www directory):
RewriteEngine on
# Block access to configuration files
Require all denied
# Block access to .git directory explicitly (additional protection)
RedirectMatch 403 /\.git
# Silent IP ban. When accessing your website, the attacker will get a maintenance page.
# What is good about this method is that results are being cached and the hacker still will get a maintenance page after he changes ip.
RewriteCond %{REMOTE_ADDR} ^111\.111\.111\.111$
RewriteRule .* /maintenence.html [R=301,L]
# Block access to backup files (don't rely on it, never leave important files in www dir)
RewriteRule ^backup/.*\.(sql|rb|py)$ - [F,L,NC]
# Redirect to construction page when accessing admin directory unless from a specific IP
RewriteCond %{REMOTE_ADDR} != 111.111.111.111
RewriteCond %{REQUEST_URI} admin
RewriteRule .* /construction.php [L]
# Block access with 404 code to logs directory
RewriteRule ^logs.*$ - [F,L]
Install ModSecurity (Apache Web Application Firewall)
ModSecurity is a additional firewall that helps detect and block malicious HTTP traffic.
Install and activate:
sudo apt install libapache2-mod-security2 -y
sudo a2enmod security2
sudo systemctl restart apache2
Download default OWASP rules:
cd /etc/modsecurity
sudo git clone https://github.com/coreruleset/coreruleset.git
sudo mv coreruleset /etc/modsecurity/owasp-crs
cp modsecurity.conf-recommended modsecurity.conf
edit /etc/modsecurity/modsecurity.conf
and set SecRuleEngine On
Restart Apache:
sudo systemctl restart apache2
Test to see if it works with simple XSS injection:
http://yoursite.com/?id=1' OR ‘1’=’1
If it is not working check if the module is loaded:
apachectl -M | grep security2
Security HTTP Headers
Make sure to use security headers such as Content-Security-Policy, Referrer-Policy, X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security in your application.
PHP example:
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' telegram.org t.me cdn.jsdelivr.net code.jquery.com; style-src 'self' 'unsafe-inline' cdn.jsdelivr.net; img-src 'self' data: cdn.jsdelivr.net telegra.ph; frame-src 'self' https://www.youtube.com telegram.org t.me cdn.jsdelivr.net;");
header("Referrer-Policy: no-referrer");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");
Clean the Server
Some applications may create lots of useless files, especially if not used right. Even if the system is working properly it is still generating trash and it is very important to clean up trash from time to time. There is a cool little tool called BleachBit, that I like to use.
Install BleachBit to clean up junk:
sudo apt install bleachbit
To clean use:
bleachbit — list | grep -E “[a-z0–9_\-]+\.[a-z0–9_\-]+” | xargs bleachbit — clean
Setup Process Manager & Log Rotation (if needed)
Install Node + PM2:
npm install -g n
n stable
npm install pm2@latest -g
pm2 startup
Enable log rotation for PM2:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 1G
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:rotateInterval ‘0 * * * *’
☁️ Setup Cloudflare
Cloudflare setup is very important. Not only it provides CDN and free SSL, but also may stop bots and DDoS. However, improper setup provides no protection against DDoS and bots.
Configure your DNS:
Type Name Content Proxy TTL
A domain.com 111.111.11.11 Proxied Auto
A www 111111.11.11 Proxied Auto
other records for mail, validation, etc
Cloudflare SSL/TLS:
- Always Use HTTPS: ON
- Minimum TLS Version: TLS 1.0
- TLS 1.3: ON
- Opportunistic Encryption: ON
- Automatic HTTPS Rewrites: ON
- Authenticated Origin Pulls: ON
Cloudflare Security:
Rate Limiting: Block >100 requests per 10 seconds. (depends on your website)
Custom Rules (WAF):
- Block bad requests (IP, URL, User-Agent).
- Block access to secure and unused directories.
- JS Challenge or CAPTCHA for unknown clients.
- JS Challenge or CAPTCHA for users from other countries. For example, if you provide service in USA you don't need to be accessible from India.
- Block HTTP and IPv6. IPv6 is rarely used by average users. It’s often favored for activities like parsing due to its affordability.
- JS Challenge for low HTTP versions (HTTP /1).
- Create whitelist for known good bots/IPs.
- Never use “I’m under attack” mode — use rules instead.
It is very important that you disable disable direct access to your website. Your website should not be accessible by IP. Many web server administrators assume that attacker will not attack their website just because they never publish their IP. However, server IP address can easily be found using resources like Censys, Shodan, Zoomeye, dnshistory. This will allow attacker to bypass Cloudflare filters and attack your website directly.
Cloudflare acts as a reverse proxy, so all legit traffic will come from their IPs. More experianced admins block everything else at the firewall level using Cloudflare’s current IP list like that:
ufw allow from 103.21.244.0/22 to any port 443
ufw allow from 103.21.244.0/22 to any port 80
... etc
The example above works and enough in most cases, however the attack still may attack your website by register another domain on Cloudflare, disabling protection and pointing it to your server’s IP.
Not only you need to block all direct access to your server via IP address, but also block access from any other domains that may point to your server’s IP. To do this:
Edit your default Apache host to deny all requests:
ServerName _
Require all denied
Verify domain in your host:
Redirect 403 /
Some security advises
Never share your access credentials, instead create separate accounts with limited permissions and enforce strong passwords.
Generally it is a good idea to separate database and web servers. This will improve security and stability.
Test your server with free tools like pentest-tools.com. Stay away from CMS add-ons.
Encrypting may be worth it for protecting sensitive data. Recommended tools:
- VeraCrypt for full-disk encryption
- encryptfs for encrypted folders
- gpg for encrypting files