WordPress en AlmaLinux 8.4 (nginx, MariaDB, PHP, Redis)

Última revisión: 30 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: AlmaLinux 8.4
Servidor web: nginx
Base de Datos: MariaDB
Procesador: PHP 8.0
Caché: Redis

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

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

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

cat /etc/centos-release
dnf clean all && dnf -y upgrade

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

dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf config-manager --set-enabled powertools
dnf -y groupinstall "development tools"
dnf -y install vim curl zip unzip wget npm ImageMagick ImageMagick-devel libsodium libsodium-devel

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

dnf -y install dnf-automatic

Una vez instalado, configuraremos para que actualice sólo las actualizaciones de seguridad.

vim /etc/dnf/automatic.conf

Y cambiaremos algunas de las configuraciones

upgrade_type = security
apply_updates = yes
emit_via = motd

Una vez hayamos guardado, activaremos el sistema de forma automática.

systemctl enable --now dnf-automatic.timer

Instalación de MariaDB

El siguiente paso será la instalación de la base de datos.

dnf -y install mariadb-server mariadb

Iniciaremos el servicio.

systemctl enable mariadb.service
systemctl start mariadb.service
systemctl status mariadb.service

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

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.

dnf -y install nginx nginx-filesystem

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

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 instalaremos PHP 8.0

dnf -y install php80 php80-php-fpm php80-php-common php80-php-devel php80-php-cli php80-php-bcmath php80-php-gd php80-php-intl php80-php-imap php80-php-json php80-php-mbstring php80-php-mysqlnd php80-php-opcache php80-php-pear php80-php-soap php80-php-xml php80-php-pecl-zip php80-php-pecl-imagick php80-php-sodium

Y después PHP 7.4

dnf -y install php74 php74-php-fpm php74-php-common php74-php-devel php74-php-cli php74-php-bcmath php74-php-gd php74-php-intl php74-php-imap php74-php-json php74-php-mbstring php74-php-mysqlnd php74-php-opcache php74-php-pear php74-php-soap php74-php-xml php74-php-pecl-zip php74-php-pecl-imagick php74-php-pecl-imagick-devel php74-php-sodium

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

systemctl stop php80-php-fpm.service
systemctl enable php80-php-fpm.service
systemctl start php80-php-fpm.service
systemctl status php80-php-fpm.service
systemctl stop php74-php-fpm.service
systemctl enable php74-php-fpm.service
systemctl start php74-php-fpm.service
systemctl status php74-php-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.

dnf -y install redis php80-php-redis php74-php-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.service
systemctl enable redis.service
systemctl start redis.service
systemctl status redis.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.

dnf -y install snapd
systemctl enable snapd.socket
systemctl start snapd.socket
ln -s /var/lib/snapd/snap /snap
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 -rf /usr/share/nginx/html/*

Y crearemos la página por defecto.

vim /usr/share/nginx/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 /usr/share/nginx/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 nginx: 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. Primero instalaremos lo que el sistema incluye.

dnf -y install optipng pngquant jpegoptim

Posteriormente instalaremos compilando gifsicle.

cd
wget https://www.lcdf.org/gifsicle/gifsicle-1.92.tar.gz
tar xvf gifsicle-1.92.tar.gz
rm -rf gifsicle-1.92.tar.gz
cd gifsicle-1.92/
./configure
make && make check
make install
cd
gifsicle --help

A continuación haremos lo mismo con libwebp.

cd
wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.2.0.tar.gz
tar xvf libwebp-1.2.0.tar.gz
rm -rf libwebp-1.2.0.tar.gz
cd libwebp-1.2.0/
./configure
make && make check
make install
cd
dwebp --help

Finalmente instalaremos svgo.

npm install -g svgo@1.3.2

Y, para acabar, lo añadiremos a WP-CLI.

php74 "$(which wp)" package install typisttech/image-optimize-command:@stable --allow-root

Configuración de los servicios

Configuración de nginx

Configuraremos la configuración global de nginx.

cd /etc/nginx/
vim nginx.conf

Con el siguiente contenido:

user nginx;
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;
  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;
}

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/
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

Algunas configuraciones que puede interesarte modificar:

max_execution_time = 300
max_input_time = 300
memory_limit = 256M

Retocaremos la configuración del PHP OPcache

cd /etc/opt/remi/php80/php.d/
vim /etc/opt/remi/php80/php.d/10-opcache.ini

Con el siguiente contenido:

zend_extension=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
opcache.huge_code_pages=0
opcache.blacklist_filename=/etc/opt/remi/php80/php.d/opcache*.blacklist

Algunas configuraciones que puede interesarte modificar:

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/
vim /etc/php/8.0/fpm/pool.d/www.conf

E incluiremos este contenido.

[www]
;prefix = /path/to/pools/$pool
user = nginx
group = nginx
listen = /var/opt/remi/php80/run/php-fpm/www.sock
;listen.backlog = 511
listen.owner = nginx
listen.group = nginx
;listen.mode = 0660
;listen.acl_users = nginx
;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
;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 = /var/opt/remi/php80/log/php-fpm/www-slow.log
;request_slowlog_timeout = 0
;request_slowlog_trace_depth = 20
;request_terminate_timeout = 0
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
;chdir = /var/www
;catch_workers_output = yes
;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/opt/remi/php80/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 128M
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/opt/remi/php80/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/opt/remi/php80/lib/php/wsdlcache
;php_value[opcache.file_cache]  = /var/opt/remi/php80/lib/php/opcache

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 php80-php-fpm.service
systemctl status php80-php-fpm.service

Configuración de PHP 7.4

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

cd /etc/opt/remi/php74/
vim /etc/opt/remi/php74/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

Algunas configuraciones que puede interesarte modificar:

max_execution_time = 300
max_input_time = 300
memory_limit = 256M

Retocaremos la configuración del PHP OPcache

cd /etc/opt/remi/php74/php.d/
vim /etc/opt/remi/php74/php.d/10-opcache.ini

Con el siguiente contenido:

zend_extension=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
opcache.huge_code_pages=0
opcache.blacklist_filename=/etc/opt/remi/php74/php.d/opcache*.blacklist

Algunas configuraciones que puede interesarte modificar:

opcache.memory_consumption=512

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

cd /etc/opt/remi/php74/php-fpm.d/
vim /etc/opt/remi/php74/php-fpm.d/www.conf

E incluiremos este contenido.

[www]
;prefix = /path/to/pools/$pool
user = nginx
group = nginx
listen = /var/opt/remi/php74/run/php-fpm/www.sock
;listen.backlog = 511
listen.owner = nginx
listen.group = nginx
;listen.mode = 0660
;listen.acl_users = nginx
;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
;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 = /var/opt/remi/php80/log/php-fpm/www-slow.log
;request_slowlog_timeout = 0
;request_slowlog_trace_depth = 20
;request_terminate_timeout = 0
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
;chdir = /var/www
;catch_workers_output = yes
;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/opt/remi/php74/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 128M
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/opt/remi/php74/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/opt/remi/php74/lib/php/wsdlcache
;php_value[opcache.file_cache]  = /var/opt/remi/php74/lib/php/opcache

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 php74-php-fpm.service
systemctl status php74-php-fpm.service

Configuración de Redis

Abrimos el fichero de configuración general.

cd /etc/
vim /etc/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_6379.pid
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
# replicaof  
# masterauth 
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# repl-ping-replica-period 10
# repl-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
replica-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
# min-slaves-max-lag is set to 10.
# replica-announce-ip 5.5.5.5
# replica-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
replica-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-replica-validity-factor 10
# cluster-migration-barrier 1
# cluster-require-full-coverage yes
# cluster-replica-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 replica 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.service
systemctl status redis.service
systemctl restart php80-php-fpm.service
systemctl status php80-php-fpm.service
systemctl restart php74-php-fpm.service
systemctl status php74-php-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/conf.d/
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.

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/conf.d/
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/opt/remi/php74/run/php-fpm/www.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/
php74 "$(which wp)" core download --allow-root
php74 "$(which 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:

php74 "$(which 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 nginx:nginx ./
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/bin/php74; 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.

dnf clean all && dnf -y update

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.