Do you want to run a website like this? This website is built with WordPress, hosted on a virtual private server (VPS) with nginx as a reverse proxy and MySQL as database management system (DBMS). A DNS record associates the domain (davte.it) to the public ip address of the VPS. A trusted certificate is provided by Let’s Encrypt.
You may purchase a WordPress hosting plan: your provider will take care of registering a DNS record, installing a reverse proxy (typically Apache) and a DBMS, and downloading and configuring WordPress; you will just have to run the WordPress installer to create an administrator account, then you can focus on the content.
This solution is a generally a little more expensive and easier to manage but gives you much less control on what you can do with your website. With a dedicated VPS, in fact, you may install any kind of online service, e.g. NextCloud or Jupyter or GoGS etc., and register a subdomain for each service. Furthermore, you may run your own script on this remote machine, for example a davtelepot Telegram bot.
Setup your VPS
When you buy a VPS, your provider will give you a static IPv4 address and a range of IPv6 addresses. Check that at least one static IP address is included in your VPS plan, since this is required to run a public website.
I suggest choosing Ubuntu as operating system; if available, you may select a pre-installed LEMP environment (if not available, you will have to install nginx, MySQL and php). Your provider will ask you for a root password, or generate one for you. You may log in via SSH, if your VPS comes with SSH installed and working; otherwise you will need to set it up using a remote terminal (follow your provider’s instructions).
sudo apt install openssh-server
sudo ufw allow ssh # Firewall settings: allow incoming connections on port 22
Let’s say your IP address is 1.1.1.1
. You may connect to your VPS with this command:
ssh root@1.1.1.1
Install required programs
A LEMP stack includes the reverse proxy nginx, the DMBS MySQL and the php interpreter. If your plan does not include a LEMP stack, you can install these programs individually.
sudo apt install nginx
sudo apt install mysql-server
sudo apt install php-fpm
To configure MySQL server follow these instructions.
sudo mysql_secure_installation utility
To use Let’s Encrypt as free certificate provider, install certbot for nginx.
add-apt-repository ppa:certbot/certbot # DEPRECATED
apt-get install python-certbot-nginx
sudo apt install certbot python3-certbot-nginx
Prepare WordPress
Download WordPress source code, set user and permissions to files and folders, copy the configuration sample.
cd /var/www
wget https://wordpress.org/latest.zip
unzip latest.zip
chown -R www-data:www-data wordpress # Use nginx user or ftp user
find wordpress -type f -print0|xargs -0 chmod 664
find wordpress -type d -print0|xargs -0 chmod 775
cp wordpress/wp-config-sample.php wordpress/wp-config.php
chmod 660 wordpress/wp-config.php
Then you have to create a MySQL user and database and grant all privileges on the database to the user.
mysql
CREATE DATABASE wp_example;
CREATE USER 'aaa'@'localhost' IDENTIFIED BY 'password123';
GRANT ALL PRIVILEGES ON wp_example.* TO "aaa"@"localhost";
FLUSH PRIVILEGES;
EXIT
Once that is done, complete WordPress configuration by copying user, host, password, database name and a table prefix of your choice in wp-config.php
.
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wp_example' );
/** MySQL database username */
define( 'DB_USER', 'aaa' );
/** MySQL database password */
define( 'DB_PASSWORD', 'password123' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', 'utf8_unicode_ci' );
/**#@+
* Authentication Unique Keys and Salts.
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* Paste the keys and salts here
*/
$table_prefix = 'wpe_';
Reverse proxy configuration
Assume your website is www.example.com
. You want to redirect requests made to example.com
to www.example.com
, and allow only HTTPS requests.
HTTP requests are made on port 80
by default, whereas HTTPS requests on port 443
. The former consist of plain text, the latter are encrypted with server’s certificate.
For now, we will use a self-signed certificate: only after registering a DNS record pointing to your IP address you may obtain a trusted third-party certificate.
# Generate a self-signed certificate and its key
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
Create an example.conf file in /etc/nginx/sites-available/
folder. This folder should contain one file per website you host (yes, the same machine may host several websites!).
# sudo nano /etc/nginx/sites-available/example.conf
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
root /var/www/example/wordpress;
server_name www.example.com;
location / {
index index.php;
try_files $uri/ $fastcgi_script_name /index.php?$args $uri.html;
expires max;
# Transform non-php non-file requests into php requests
if (!-e $request_filename) {
rewrite ^.*$ /index.php last;
}
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
try_files index.html $fastcgi_script_name =404;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass unix:/var/run/php/phpn.m-fpm.sock; # replace phpn.m with your version
}
}
Link this file in the sites-enabled folder, test the nginx configuration and restart nginx.
sudo ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx.service
With this configuration, all HTTP(S) requests to (www.)example.com are redirected to https://www.example.com, the URL is converted to a file path relative to /var/www/example/wordpress; if a file is found, it is served by nginx; otherwise, the request is processed by php interpreter.
This nginx configuration supports both IPv4 and IPv6.
TLS configuration
HTTPS means “HTTP over SSL/TLS”, where SSL or TLS are security protocols. HTTP requests are plain text, readable by anyone; to convey security to the transmission of data, a cryptographic protocol is used to encrypt the exchanged data. At the beginning of the communication, the client makes sure it is speaking with the right server by encrypting a key using the server’s certificate: only the rightful owner of the server can decrypt with their private key what is encrypted using the public key stated in the trusted third-party certificate. The rest of the communication is encrypted symmetrically with an agreed password.
There are many cryptographic protocols available for HTTPS communications, but only TLSv1.2 and TLSv1.3 are currently considered secure. Supporting only these two may make your website inaccessible for outdated clients, but achieves a stronger security.
Create a /etc/letsencrypt/options-ssl-nginx.conf
file and set HTTP header options and encryption accepted protocols and ciphers. Do not trust this example: check out currently secure protocols and ciphers here.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
ssl_session_tickets on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ecdh_curve auto;
keepalive_timeout 70;
ssl_buffer_size 1400;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;
This configuration file must be included in any nginx server block: if one single server block is missing these options, all blocks may fail to support TLSv1.3 (some versions of Nextcloud, with their own configuration files, tend to break this rule and you will need to fix them manually).
Example of server block including these settings:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
...
WordPress installation
Simply visit your website’s homepage to run through the WordPress guided installation process.
Security advice
- Do not install unnecessary or untrustworthy WordPress plugins; keep their number as low as possible
- Keep your operating system, WordPress, php, nginx, MySQL, and all other software up to date
- Reboot your server after major updates
- Hide or protect your /wp-login directory to prevent brute-force attacks
- Use strong passwords and keep them safe; do not use the same password for different services; change them whenever you suspect they may have been exposed
- Support only secure protocols
Register a DNS record
- A record: associates the domain (example.com) and its subdomains to one or more IPv4 addresses; when a client queries an IPv4 DNS for a specific subdomain, it will be directed towards the corresponding IP address.
- AAAA record: the same as A record, but with IPv6 addresses; please notice that not all providers have an IPv6 DNS, so IPv6-only clients may be unable to find your website; this may become an issue as soon as IPv4 gets deprecated, but unfortunately this is not happening soon.
- If your provider supports CAA records, you may associate your domain with one or more CAs and provide an email to report issues. Only certificates provided by those CAs will be accepted for your domain.
- MX record: if your plan includes a mail service, this record is set for you by your provider; otherwise, you will have to provide information about mail server(s) associated with your domain; if your VPS is good enough, you may host your own mail server. This record is important to allow email verification.
Example of A record and AAAA record:
//A
//HostName,IPAddress
A,,1.1.1.1 // Your VPS IPv4 address
A,webmail,5.5.5.5 // Your mail server address
A,www,1.1.1.1
//AAAA
//HostName,IPV6Address
AAAA,,:::::::1 // Your VPS IPv6 address
AAAA,www,:::::::1
Get a Let’s Encrypt certificate
Let’s Encrypt is a trusted nonprofit Certificate Authority whose mission is to provide TLS certificates for free, to make the Internet a safer place.
The easiest way to manage certificates in a LEMP environment is using certbot: once you own a domain and control a VPS at the corresponding IP address, just run sudo certbot --nginx
: it will take care of everything, from certificate issuing and renewing to nginx HTTPS configuration.