WordPress en Ubuntu 20 (nginx, MariaDB, PHP, Redis)

Última revisión: 29 de mayo de 2021

Este tutorial ha sido creado en un VPS de Clouding.io. Puedes crear tu propio VPS desde 3€/mes.

Además, tienes la posibilidad de crear tu VPS con la imagen de WordPress en un clic.

COLABORACIÓN

Versiones a instalar

Sistema Operativo: Ubuntu 20 LTE
Servidor web: nginx
Base de Datos: MariaDB 10.5
Procesador: PHP 8.0 + PHP 7.4
Caché: Redis

Aquí te dejamos un pequeño manual de instalación desde una instalación de sistema operativo básico de Ubuntu 20.

Configurando el Sistema Operativo

Una vez esté instalado el sistema operativo, lo primero que configuraremos será la hora del servidor. En este caso configuraremos la zona horaria universal UTC.

timedatectl set-timezone 'UTC'
timedatectl set-ntp on

Lo siguiente que haremos es comprobar la versión del sistema operativo y, posteriormente, hacer una actualización completa del mismo.

lsb_release -a
apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove

Una vez esta todo actualizado, instalamos algunas herramientas y software base que puede ser útil tener en el sistema.

apt -y install software-properties-common curl vim zip unzip apt-transport-https sendmail npm

Antes de acabar, activaremos las actualizaciones automáticas de seguridad del sistema operativo.

apt -y install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Instalación de MariaDB

El siguiente paso será la instalación de la base de datos. En este caso vamos a utilizar MariaDB 10.5. Lo primero que haremos será configurar la descarga, y posteriormente su instalación.

curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version="mariadb-10.5"
apt -y install mariadb-server mariadb-client

Ahora que está instalada, procederemos a la configuración inicial. Para ello usaremos el sistema de instalación segura, que nos hará algunas preguntas.

mysql_secure_installation

A la pregunta de si queremos cambiar la contraseña, dependiendo de si hemos puesto o no en la instalación, la cambiaremos. En caso de no haber puesto ninguna, es muy recomendable ponerle una contraseña segura.

Set root password? [Y/n]: Y

Al resto de preguntas, contestaremos lo siguiente:

Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

En este momento ya tendremos la base de datos configurada. Ahora haremos que se ejecute en los re inicios del sistema y la iniciaremos.

systemctl stop mysql.service
systemctl enable mysql.service
systemctl start mysql.service
systemctl status mysql.service

Instalación de nginx

A partir de aquí tenemos la base de datos configurada y vamos a proceder a la instalación del servidor web. En este caso vamos a usar nginx. Para estar al día, no usaremos la versión que viene con el sistema operativo, sino una más actualizada y mantenida.

add-apt-repository -y -s ppa:ondrej/nginx
apt -y install nginx nginx-extras

Ahora que tenemos nginx instalado, lo vamos a configurar para que se inicie en los re inicios del sistema automáticamente.

systemctl stop nginx.service
systemctl enable nginx.service
systemctl start nginx.service
systemctl status nginx.service

Instalación de PHP

En este momento ya tenemos el servidor web, por lo que vamos a instalar y a configurar PHP para que funcione correctamente con la base de datos y el servidor web. En este caso vamos a instalar las versiones de PHP 8.0 y PHP 7.4. Primero haremos la instalación de los paquetes más actualizados (que no son los que vienen con el sistema operativo) y que en caso de necesitarlo, además, nos permitirían tener varias versiones de PHP en paralelo.

add-apt-repository -y -s ppa:ondrej/php

Primero instalaremos PHP 8.0

apt -y install php8.0 php8.0-fpm php8.0-common php8.0-dev php8.0-cli php8.0-bcmath php8.0-curl php8.0-gd php8.0-intl php8.0-imap php8.0-mbstring php8.0-mysql php8.0-opcache php8.0-soap php8.0-xml php8.0-zip php8.0-imagick imagemagick libmagickwand-dev

Posteriormente instalaremos PHP 7.4

apt -y install php7.4 php7.4-fpm php7.4-common php7.4-dev php7.4-cli php7.4-bcmath php7.4-curl php7.4-gd php7.4-intl php7.4-imap php7.4-json php7.4-mbstring php7.4-mysql php7.4-opcache php7.4-soap php7.4-xml php7.4-xmlrpc php7.4-zip php7.4-imagick imagemagick libmagickwand-dev

Y lanzaremos la instalación de la extensión Sodium.

apt -y install build-essential php-pear php-dev pkg-config
pecl channel-update pecl.php.net
cd
wget https://download.libsodium.org/libsodium/releases/LATEST.tar.gz
tar xvf LATEST.tar.gz
rm -rf LATEST.tar.gz
cd libsodium-stable/
./configure
make && make check
make install
cd
pecl install libsodium
echo "extension=sodium.so" >> /etc/php/8.0/mods-available/libsodium.ini
echo "extension=sodium.so" >> /etc/php/7.4/mods-available/libsodium.ini

Ahora que ya tenemos PHP correctamente instalado, vamos a activarlo de forma que cuando se reinicie el sistema se ejecute automáticamente.

systemctl stop php8.0-fpm.service
systemctl enable php8.0-fpm.service
systemctl start php8.0-fpm.service
systemctl status php8.0-fpm.service
systemctl stop php7.4-fpm.service
systemctl enable php7.4-fpm.service
systemctl start php7.4-fpm.service
systemctl status php7.4-fpm.service
php -v

Instalación de Redis

Para trabajar con unas mejoras en el rendimiento de la caché de objetos, vamos a dejar listo Redis como sistema de almacenamiento.

apt -y install redis-server php8.0-redis php7.4-redis

Posteriormente, y de la misma forma que el resto de elementos, lo vamos a configurar para que se inicie automáticamente si se reinicia el servidor.

systemctl stop redis-server.service
systemctl enable redis-server.service
systemctl start redis-server.service
systemctl status redis-server.service

Configuración del HTTPS

Como vamos a montar nuestra web sobre un servidor web seguro (HTTPS), necesitaremos instalar el generador de certificados de Let’s Encrypt. Para ello instalaremos el sistema de creación de certificados certbot.

snap install core && snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot

Si es la primera vez que vamos a usar la cuenta de correo necesaria para recibir avisos de la caducidad de los certificados, deberemos registrarla.

certbot register --email wordpress@example.com --agree-tos --no-eff-email

En caso de que ya la tengamos activa, podemos actualizarla.

certbot update_account --email wordpress@example.com --agree-tos --no-eff-email

Para que los certificados se actualicen automáticamente, activaremos una tarea programada (cron) una vez al día que automáticamente renueve los certificados.

crontab -e

Una vez dentro, configuraremos, por ejemplo, que se ejecute a las 06:45 cada mañana.

45 6 * * * certbot renew

Limpiando los sitios por defecto

Por defecto, el sistema responde a la dirección IP y a otros elementos. Para evitar que los sitios por defecto se muestren, eliminaremos las páginas por defecto.

Eliminamos todos los ficheros por defecto.

rm /var/www/html/index.*

Y crearemos la página por defecto.

vim /var/www/html/index.html

Donde añadiremos el contenido de la página por defecto.

<!DOCTYPE html>
<p>Hello World!</p>

Y crearemos un fichero de robots.txt para que no se indexen estas páginas por defecto.

vim /var/www/html/robots.txt

Donde añadiremos el contenido del fichero que impida la indexación.

User-Agent: *
Disallow: /

Instalación de WP-CLI

Para la creación de nuestros sitios WordPress, usaremos la instalación mediante WP-CLI.

cd 
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chown www-data: wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp

Y validamos que esté instalado y actualizado.

wp --info
wp cli update

Además, podemos instalar la extensión para optimizar las imágenes de nuestro WordPress.

apt -y install jpegoptim optipng pngquant gifsicle webp
npm install -g svgo@1.3.2
/usr/bin/php7.4 "$(which wp)" package install typisttech/image-optimize-command:@stable --allow-root

Configuración de los servicios

Configuración de nginx

Haremos unas copias de seguridad de los ficheros de configuración de sitios para posteriormente poner los nuestros.

cd /etc/nginx/sites-enabled/
rm default
cd /etc/nginx/sites-available/
rm default

Configuraremos la configuración global de nginx.

cd /etc/nginx/
vim nginx.conf

Con el siguiente contenido:

user www-data;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
include /etc/nginx/modules-enabled/*.conf;
events {
  multi_accept on;
  worker_connections 65535;
  use epoll;
}
http {
  charset utf-8;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  server_tokens off;
  more_clear_headers Server;
  log_not_found off;
  types_hash_max_size 2048;
  client_max_body_size 64m;
  keepalive_timeout 10;
  server_names_hash_bucket_size 128;
  server_names_hash_max_size 1024;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  #timeout
  proxy_read_timeout 300;
  proxy_connect_timeout 300;
  proxy_send_timeout 300;
  send_timeout 300;
  client_header_timeout 300;
  client_body_timeout 300;
  fastcgi_read_timeout 300;
  # logging
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  # gzip
  gzip on;
  gzip_comp_level 6;
  gzip_proxied any;
  gzip_min_length 256;
  gzip_buffers 16 8k;
  gzip_types application/atom+xml application/geo+json application/javascript application/x-javascript application/json application/ld+json application/manifest+json application/rdf+xml application/rss+xml application/vnd.ms-fontobject application/wasm application/x-web-app-manifest+json application/xhtml+xml application/xml font/eot font/otf font/ttf image/bmp image/svg+xml text/cache-manifest text/calendar text/css text/javascript text/markdown text/plain text/xml text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
  gzip_disable "MSIE [1-6].(?!.*SV1)";
  gzip_vary on;
  # more
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

Reiniciaremos nginx para validar que todo funciona correctamente.

nginx -t
systemctl restart nginx.service
systemctl status nginx.service

Configuración de PHP 8.0

Editaremos el fichero de configuración de PHP (php.ini).

cd /etc/php/8.0/fpm/
rm php.ini
vim /etc/php/8.0/fpm/php.ini

Con el siguiente contenido:

[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_async_signals,pcntl_unshare,
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = Off
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
html_errors = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 128M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 128M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 300
[CLI Server]
cli_server.color = On
[Date]
date.timezone = 'UTC'
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[mail function]
SMTP = localhost
smtp_port = 25
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = "a=href,area=href,frame=src,form="
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[ldap]
ldap.max_links = -1
[opcache]
opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=15
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

Algunas configuraciones que puede interesarte modificar:

max_execution_time = 300
max_input_time = 300
memory_limit = 256M
opcache.memory_consumption=512

Como vamos a usar PHP-FPM, también haremos cambios en su configuración.

cd /etc/php/8.0/fpm/pool.d/
rm www.conf
vim /etc/php/8.0/fpm/pool.d/www.conf

E incluiremos este contenido.

[www]
;prefix = /path/to/pools/$pool
user = www-data
group = www-data
listen = /run/php/php8.0-fpm.sock
;listen.backlog = 511
listen.owner = www-data
listen.group = www-data
;listen.mode = 0660
;listen.acl_users =
;listen.acl_groups =
;listen.allowed_clients = 127.0.0.1
; process.priority = -19
; process.dumpable = yes
pm = dynamic
pm.max_children = 16
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 12
;pm.process_idle_timeout = 10s;
pm.max_requests = 500
;pm.status_path = /status
;pm.status_listen = 127.0.0.1:9001
;ping.path = /ping
;ping.response = pong
;access.log = log/$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
;slowlog = log/$pool.log.slow
;request_slowlog_timeout = 0
;request_slowlog_trace_depth = 20                           
;request_terminate_timeout = 0
;request_terminate_timeout_track_finished = no
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
;chdir = /var/www
;catch_workers_output = yes
;decorate_workers_output = no
;clear_env = no
;security.limit_extensions = .php .php3 .php4 .php5 .php7
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M

Algunas configuraciones que puede interesarte modificar (tienes algunos ejemplos en la sección de Rendimiento WordPress):

pm = dynamic
pm.max_children = 16
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 12
pm.max_requests = 500

Y reiniciamos PHP.

systemctl restart php8.0-fpm.service
systemctl status php8.0-fpm.service

Configuración de PHP 7.4

Editaremos el fichero de configuración de PHP (php.ini).

cd /etc/php/7.4/fpm/
rm php.ini
vim /etc/php/7.4/fpm/php.ini

Con el siguiente contenido:

[PHP]
engine = On
short_open_tag = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = -1
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_async_signals,pcntl_unshare,
disable_classes =
zend.enable_gc = On
zend.exception_ignore_args = On
zend.exception_string_param_max_len = 0
expose_php = Off
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
html_errors = On
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 128M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 128M
max_file_uploads = 20
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 300
[CLI Server]
cli_server.color = On
[Date]
date.timezone = 'UTC'
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[mail function]
SMTP = localhost
smtp_port = 25
mail.add_x_header = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 0
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.sid_length = 26
session.trans_sid_tags = "a=href,area=href,frame=src,form="
session.sid_bits_per_character = 5
[Assertion]
zend.assertions = -1
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[ldap]
ldap.max_links = -1
[opcache]
opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=15
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

Algunas configuraciones que puede interesarte modificar:

max_execution_time = 300
max_input_time = 300
memory_limit = 256M
opcache.memory_consumption=512

Como vamos a usar PHP-FPM, también haremos cambios en su configuración.

cd /etc/php/7.4/fpm/pool.d/
rm www.conf
vim /etc/php/7.4/fpm/pool.d/www.conf

E incluiremos este contenido.

[www]
;prefix = /path/to/pools/$pool
user = www-data
group = www-data
listen = /run/php/php7.4-fpm.sock
;listen.backlog = 511
listen.owner = www-data
listen.group = www-data
;listen.mode = 0660
;listen.acl_users =
;listen.acl_groups =
;listen.allowed_clients = 127.0.0.1
; process.priority = -19
; process.dumpable = yes
pm = dynamic
pm.max_children = 16
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 12
;pm.process_idle_timeout = 10s;
pm.max_requests = 500
;pm.status_path = /status
;pm.status_listen = 127.0.0.1:9001
;ping.path = /ping
;ping.response = pong
;access.log = log/$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
;slowlog = log/$pool.log.slow
;request_slowlog_timeout = 0
;request_slowlog_trace_depth = 20                           
;request_terminate_timeout = 0
;request_terminate_timeout_track_finished = no
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
;chdir = /var/www
;catch_workers_output = yes
;decorate_workers_output = no
;clear_env = no
;security.limit_extensions = .php .php3 .php4 .php5 .php7
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M

Algunas configuraciones que puede interesarte modificar (tienes algunos ejemplos en la sección de Rendimiento WordPress):

pm = dynamic
pm.max_children = 16
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 12
pm.max_requests = 500

Y reiniciamos PHP.

systemctl restart php7.4-fpm.service
systemctl status php7.4-fpm.service

Configuración de Redis

Abrimos el fichero de configuración general.

cd /etc/redis/
rm redis.conf
vim /etc/redis/redis.conf

Con el siguiente contenido:

# include /path/to/local.conf
# include /path/to/other.conf
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
bind 127.0.0.1 ::1
protected-mode yes
port 6379
tcp-backlog 511
# unixsocket /var/run/redis/redis-server.sock
# unixsocketperm 700
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
# syslog-enabled no
# syslog-ident redis
# syslog-facility local0
databases 4
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
# slaveof  
# masterauth 
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# repl-ping-slave-period 10
# repl-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
slave-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
# min-slaves-max-lag is set to 10.
# slave-announce-ip 5.5.5.5
# slave-announce-port 1234
# requirepass foobared
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
# rename-command CONFIG ""
# maxclients 10000
maxmemory 512mb
maxmemory-policy allkeys-lfu
lfu-log-factor 10
lfu-decay-time 1
# maxmemory-samples 10
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble no
lua-time-limit 5000
# cluster-enabled yes
# cluster-config-file nodes-6379.conf
# cluster-node-timeout 15000
# cluster-slave-validity-factor 10
# cluster-migration-barrier 1
# cluster-require-full-coverage yes
# cluster-slave-no-failover no
# cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379
# cluster-announce-bus-port 6380
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
#  notify-keyspace-events Elg
#  notify-keyspace-events Ex
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 10
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# client-query-buffer-limit 1gb
# proto-max-bulk-len 512mb
hz 10
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
# activedefrag yes
# active-defrag-ignore-bytes 100mb
# active-defrag-threshold-lower 10
# active-defrag-threshold-upper 100
# active-defrag-cycle-min 25
# active-defrag-cycle-max 75

Algunas configuraciones que puede interesarte modificar:

maxmemory 512mb
maxmemory-policy allkeys-lfu
lfu-log-factor 10
lfu-decay-time 1

Reiniciamos Redis y PHP para cargar su configuración.

systemctl restart redis-server.service
systemctl status redis-server.service
systemctl restart php8.0-fpm.service
systemctl status php8.0-fpm.service
systemctl restart php7.4-fpm.service
systemctl status php7.4-fpm.service

Creación de un sitio web

Para estos ejemplos usaremos la carpeta raíz «/webs/» y el dominio «example.com«.

Creamos la carpeta donde alojaremos nuestros sitios web.

mkdir /webs/
cd /webs/

Y aquí crearemos tantos sitios como queramos. En este caso nuestro sitio de ejemplo.

mkdir /webs/example.com/

Configuración del sitio en nginx

Accedemos y creamos el fichero de configuración.

cd /etc/nginx/sites-available/
vim example.com.conf

Con el siguiente contenido:

server {
  listen 80;
  listen [::]:80;
  server_tokens off;
  server_name example.com www.example.com;
  root /webs/example.com;
  index index.php index.html;
  location ~ /.well-known {
    allow all;
  }
}

Creamos el enlace simbólico y reiniciamos nginx para activar esta configuración.

ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx.service
systemctl status nginx.service

Configuración del certificado TLS

Lanzamos el CertBot para la creación de certificados TLS.

certbot --authenticator webroot --installer nginx

Seleccionaremos el sitio (uno o varios dominios), y nos pedirá la ruta, que sería esta:

/webs/example.com

Actualización de nginx para HTTPS

Volvemos a acceder al fichero de configuración.

cd /etc/nginx/sites-available/
vim example.com.conf

Actualizando el contenido anterior con el que activa el HTTPS. En este caso vamos a configurarlo para PHP 7.4

#HTTP
server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;
  return 301 https://example.com$request_uri;
  access_log off;
}
#CANONICAL
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  # SSL
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  # SSL OCSP Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 208.67.222.222 8.8.8.8 valid=300s;
  resolver_timeout 2s;
  # Security headers
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header X-Frame-Options SAMEORIGIN;
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
  #logs
  access_log off;
  #CONFIG
  server_name www.example.com;
  return 301 https://example.com$request_uri;
  access_log off;
}
#REAL
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  # SSL
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  # SSL OCSP Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 208.67.222.222 8.8.8.8 valid=300s;
  resolver_timeout 2s;
  # Security headers
  add_header Referrer-Policy "strict-origin-when-cross-origin" always;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header X-Frame-Options SAMEORIGIN;
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
  #logs
  access_log /var/log/nginx/example.com-access.log combined buffer=64k flush=5m;
  error_log /var/log/nginx/example.com-error.log;
  #CONFIG
  server_name example.com;
  root /webs/example.com;
  index index.php index.html index.htm;
  set $cache_uri $request_uri;
  if ($request_method = POST) {
    set $cache_uri 'null cache';
  }
  if ($query_string != "") {
    set $cache_uri 'null cache';
  }
  if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-*.php)") {
    set $cache_uri 'null cache';
  }
  if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
    set $cache_uri 'null cache';
  }
  set $cachefile "/wp-content/cache/supercache/$http_host/$cache_uri/index.html";
  if ($https ~* "on") {
    set $cachefile "/wp-content/cache/supercache/$http_host/$cache_uri/index-https.html";
  }
  location / {
    try_files $cachefile $uri $uri/ /index.php?$args;
  }
  # HIDDEN FILES
  location ~ /.well-known {
    allow all;
  }
  location ~ /.ht {
    deny all;
  }
  location ~ /favicon.(ico|png) {
    log_not_found off;
    access_log off;
  }
  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }
  location ~* \.(bmp|bz2|cur|doc|docx|exe|gif|gz|htc|ico|jpeg|jpg|mid|midi|mp3|mp4|ogg|ogv|png|ppt|pptx|rar|rtf|svg|svgz|tar|tgz|wav|webm|webp|xls|xlsx|zip)$ {
    expires max;
    add_header Cache-Control "public";
    log_not_found off;
    access_log off;
  }
  location ~* \.(atom|css|js|rss|xml)$ {
    expires 1d;
    add_header Cache-Control "public";
    log_not_found off;
    access_log off;
  }
  location ~* \.(?:ttf|eot|woff|woff2|otf)$ {
    expires max;
    add_header Cache-Control "public";
    add_header Access-Control-Allow-Origin "*";
    log_not_found off;
    access_log off;
  }
  location ~* readme\.(html|txt) {
    deny all;
  }
  location ~* (licencia|license|LICENSE|olvasdel|lisenssi|liesmich)\.(html|txt) {
    deny all;
  }
  location ~* ^/wp-config {
    deny all;
  }
  location ~* ^/wp-cron\.php {
    deny all;
  }
  location ~* ^/wp-admin/(install|setup-config|upgrade)\.php {
    deny all;
  }
  location ~* ^/wp-admin/maint/repair\.php {
    deny all;
  }
  location ~* ^/wp-links-opml\.php {
    deny all;
  }
  location ~* ^/wp-content/mu-plugins/$ {
    return 404;
  }
  location ~* ^/wp-content/(plugins|themes)/(.+)/$ {
    return 404;
  }
  location ~* ^/wp-content/(?:uploads|files)/.+\.(html|js|php|shtml|swf)$ {
    deny all;
  }
  location ~* ^/wp-content/plugins/.+\.(aac|avi|bz2|cur|docx?|eot|exe|flv|gz|heic|htc|m4a|midi?|mov|mp3|mp4|mpe?g|ogg|ogv|otf|pdf|pptx?|rar|rtf|tar|tgz|tiff?|ttc|wav|wmv|xlsx?|zip) {
    deny all;
  }
  location ~* sftp-config.json {
    deny all;
  }
  location ~* (access|error)_log {
    deny all;
  }
  location ~* installer-log\.txt {
    deny all;
  }
  location ~* ^/wp-content/debug.log {
    deny all;
  }
  location ~* (^#.*#|\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|sw[op])|~)$ {
    deny all;
  }
  location ~* load-(scripts|styles)\.php {
    deny all;
  }
  # BLOCK XML-RPC
  location ~* /xmlrpc\.php {
    deny all;
  }
  # ROOT PHP
  location ~ .php$ {
    fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    fastcgi_index index.php;
    fastcgi_buffers 256 16k;
    fastcgi_buffer_size 128k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_hide_header X-Powered-By;
    fastcgi_hide_header X-Pingback;
    fastcgi_hide_header Link;
    fastcgi_intercept_errors off;
    fastcgi_split_path_info ^(.+.php)(/.+)$;
    try_files $fastcgi_script_name =404;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PHP_ADMIN_VALUE open_basedir=$document_root/:/usr/lib/php/:/tmp/;
    fastcgi_param PATH_INFO $path_info;
    set $path_info $fastcgi_path_info;
    include fastcgi.conf;
  }
}

Reiniciamos el nginx para cargar la nueva configuración.

nginx -t
systemctl restart nginx.service
systemctl status nginx.service

Configuración de la base de datos

Crearemos la base de datos para el sitio web que vamos a usar. Primero accedemos a la base de datos.

mysql -u root -p

Y crearemos la base de datos, el usuario y contraseña y activaremos los privilegios.

CREATE DATABASE example CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;
GRANT ALL ON example.* TO 'example'@'localhost' IDENTIFIED BY 'la_contraseña_aqui';
FLUSH PRIVILEGES;
quit

Creación del sitio web

Ahora ya tenemos todos los elementos necesarios para la creación de nuestro sitio.

cd /webs/example.com/
wp core download --allow-root
wp config create --dbprefix=wpprefix_ --locale=en_US --dbname=example --dbuser=example --dbpass='la_contraseña_aqui' --dbcharset=utf8mb4 --dbcollate=utf8mb4_general_ci --allow-root

Y haremos la instalación del sitio:

wp core install --url="example.com" --title="Mi WordPress" --admin_user="Admin_De_WordPress" --admin_password="Password_De_WordPress" --admin_email="wordpress@example.com" --allow-root

Y haremos una actualización de los ficheros y sus permisos.

cd /webs/example.com/
chown -R www-data:www-data ./
find /webs/example.com/ -type d -exec chmod 755 {} \;
find /webs/example.com/ -type f -exec chmod 644 {} \;

Mejora del WP-Config

Recuerda que para obtener una configuración mejorada, deberás incluir algunos elementos para aprovechar al máximo la configuración del servidor.

Lo mejor es que utilices la herramienta de Generador de WP-Config.

En cualquier caso, para que no de errores el sistema, como mínimo, deberás incluir estas líneas:

/* Cron */
define( 'DISABLE_WP_CRON', false );

/* Performance */
define( 'CONCATENATE_SCRIPTS', false );

La primera de ellas deshabilita el sistema interno de los Crones. Esto se hace por Seguridad y por Rendimiento (posteriormente lo activaremos mediante WP-CLI.)

La segunda se activa para evitar un posible ataque al panel de administración de WordPress. En caso de no activarse, y debido al bloqueo que se hace en el fichero de configuración de nginx, el panel de Administración no mostrará los CSS ni JavaScript.

Activación de WP-CLI para el Cron

Como último paso, activaremos el sistema de Crones del servidor mediante WP-CLI.

* * * * WP_CLI_PHP=/usr/local/bin/php; SHELL=/bin/bash; /usr/local/bin/wp cron event run --due-now --path=/webs/example.com/ --allow-root --skip-plugins --skip-themes >/dev/null 2>&1

Configuración de Plugins

Una vez hayamos acabado de crear nuestro sitio y lo hayamos configurado, deberíamos activar algunas de las herramientas que ayudarán a mejorar el rendimiento de WordPress.

Lo primero que haremos es validar que está todo correcto, activando las mejoras de Salud del Sitio.

Para que el sistema gestione e invalide la caché de PHP OPcaché.

Para que el sistema gestione e invalide la caché de Redis.

Para optimizar y gestionar mejores ficheros CSS y JavaScript.

Y para que estos ficheros, junto con los del propio WordPress se archiven en el disco generando caché de página, usaremos WP Super Caché.

Actualización final

Si es la primera vez que montas la máquina y un sitio, estará bien hacer una última actualización del servidor y posteriormente su reinicio.

apt -y update && apt -y upgrade && apt -y dist-upgrade && apt -y autoremove

En este momento podríamos reiniciar la máquina por completo para dejarlo todo activo y validar que si se reinicia la máquina todos los servicios funcionan.

reboot

Al reiniciarse la máquina tendremos todo listo para que nuestro WordPress funcione.


Sobre este documento

Este documento está regulado por la licencia EUPL v1.2, publicado en WP SysAdmin y creado por Javier Casares. Por favor, si utilizas este contenido en tu sitio web, tu presentación o cualquier material que distribuyas, recuerda hacer una mención a este sitio o a su autor, y teniendo que poner el material que crees bajo licencia EUPL.

Servicios de Administración de Sistemas WordPress

¿Tienes un sitio web con WordPress de alto tráfico? ¿Eres una Agencia con servidores con cPanel, Plesk u otro panel en los que mantienes WordPress para tus clientes?

Si es así y te interesa un servicio profesional de mantenimiento de infraestructura WordPress y de mejora del rendimiento de tus sitios web o los de tus clientes, contacta conmigo.