Prérequis
- Un serveur sous Ubuntu, Debian, AlmaLinux, Rocky, CentOS, Fedora, Alpine ou openSUSE : voir nos VPS au Canada
- Une connexion SSH ouverte en root : Se connecter en SSH à votre VPS
- Pour l'étape HTTPS seulement : un nom de domaine dont l'enregistrement A pointe sur l'adresse IP du serveur
Nginx sert les pages, PHP-FPM exécute le code PHP : à eux deux, ils font tourner WordPress, Laravel ou n'importe quel site PHP. Ce guide installe les deux, relie Nginx à PHP-FPM dans un site (monsite.exemple.ca, dossier /var/www/monsite), règle PHP pour un vrai site (taille des envois, mémoire, fuseau horaire), ouvre le pare-feu et passe en HTTPS avec Certbot. Choisissez votre système ci-dessus. Copiez la commande avec le bouton Copier, collez-la dans votre terminal, et comparez avec le résultat attendu.
1Mettre à jour le système
apt update && apt upgrade -y... 212 upgraded, 8 newly installed, 0 to remove and 4 not upgraded. (les nombres dépendent de l'âge de l'image)
dnf upgrade -y... Complete!
dnf upgrade -y... Complete!
apk update && apk upgrade && reboot... OK: 158 MiB in 209 packages (les nombres dépendent de l'image ; la connexion SSH se coupe au redémarrage)
Alpine redémarre après la mise à jour : quand elle remplace le noyau, les modules du noyau en cours disparaissent et le pare-feu de l'étape 5 ne peut plus se charger (« Couldn't determine iptables version »). Reconnectez-vous en SSH après une minute.
zypper -n refresh && zypper -n updateAll repositories have been refreshed. ...
2Installer Nginx et PHP-FPM
Les dépôts de chaque système fournissent une version récente de PHP. Les extensions installées ici sont celles que demandent WordPress, Laravel et la plupart des applications.
apt install -y nginx php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl... Created symlink /etc/systemd/system/multi-user.target.wants/php8.3-fpm.service → ... (php8.4-fpm sur Debian 13 ; Nginx et PHP-FPM démarrent tout seuls)
dnf module enable -y php:8.3 && dnf install -y nginx php-fpm php-cli php-mysqlnd php-gd php-mbstring php-xml php-pecl-zip php-intl php-opcache && systemctl enable --now nginx php-fpm... Complete! Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → ... Created symlink /etc/systemd/system/multi-user.target.wants/php-fpm.service → ...
dnf install -y nginx php-fpm php-cli php-mysqlnd php-gd php-mbstring php-xml php-pecl-zip php-intl php-opcache && systemctl enable --now nginx php-fpm... Complete! Created symlink '/etc/systemd/system/multi-user.target.wants/nginx.service' → ... Created symlink '/etc/systemd/system/multi-user.target.wants/php-fpm.service' → ...
apk add nginx curl php84-fpm php84-mysqli php84-pdo_mysql php84-curl php84-gd php84-mbstring php84-xml php84-dom php84-simplexml php84-xmlwriter php84-zip php84-intl php84-exif php84-opcache php84-session php84-ctype php84-openssl php84-fileinfo php84-tokenizer php84-phar php84-iconv && rc-update add nginx default && rc-update add php-fpm84 default && rc-service nginx start && rc-service php-fpm84 start... * service nginx added to runlevel default * service php-fpm84 added to runlevel default * Starting nginx ... [ ok ] * Starting PHP FastCGI Process Manager ... [ ok ]
zypper -n install nginx php8-fpm php8-cli php8-mysql php8-curl php8-gd php8-mbstring php8-dom php8-xmlreader php8-xmlwriter php8-zip php8-intl php8-exif php8-opcache php8-openssl php8-ctype php8-tokenizer php8-fileinfo php8-iconv php8-phar && systemctl enable --now nginx php-fpm... Running post-transaction scripts [...done] Created symlink '/etc/systemd/system/multi-user.target.wants/nginx.service' → ... Created symlink '/etc/systemd/system/multi-user.target.wants/php-fpm.service' → ...
nginx -v && php -vnginx version: nginx/1.24.0 (Ubuntu) PHP 8.3.6 (cli) (built: ...) (NTS) ... (Debian 13 : nginx/1.26.3 et PHP 8.4.24)
nginx -v && php -vnginx version: nginx/1.20.1 PHP 8.3.33 (cli) (built: ...) (NTS gcc x86_64) ... (le troisième chiffre de PHP varie d'un dépôt à l'autre : 8.3.19 sur CentOS Stream)
nginx -v && php -vnginx version: nginx/1.30.4 PHP 8.5.10 (cli) (built: ...) (NTS gcc x86_64) ...
nginx -v && php84 -vnginx version: nginx/1.30.4 PHP 8.4.25 (cli) (built: ...) (NTS) ... (sur Alpine, la commande s'appelle php84, pas php)
nginx -v && php -vnginx version: nginx/1.27.2 PHP 8.4.24 (cli) (built: ...) (NTS) ...
3Créer le site
Le site vit dans /var/www/monsite ; remplacez monsite.exemple.ca par votre nom de domaine. La page index.php affiche la version de PHP et trois réglages : elle sert de témoin pour la suite. Dans le fichier Nginx, le bloc location ~ \.php$ envoie les fichiers .php à PHP-FPM, et le dernier bloc refuse les fichiers cachés comme .env ou .git.
mkdir -p /var/www/monsite
cat > /var/www/monsite/index.php << 'EOF'
<?php
echo 'PHP ', PHP_VERSION, ' (', php_sapi_name(), ') upload ', ini_get('upload_max_filesize'), ' memoire ', ini_get('memory_limit'), ' fuseau ', date_default_timezone_get(), "\n";
EOFcat > /etc/nginx/sites-available/monsite.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name monsite.exemple.ca;
root /var/www/monsite;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
location ~ /\. {
deny all;
}
}
EOF
ln -s /etc/nginx/sites-available/monsite.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/defaultcat > /etc/nginx/conf.d/monsite.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name monsite.exemple.ca;
root /var/www/monsite;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php-fpm/www.sock;
}
location ~ /\. {
deny all;
}
}
EOFcat > /etc/nginx/conf.d/monsite.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name monsite.exemple.ca;
root /var/www/monsite;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php-fpm/www.sock;
}
location ~ /\. {
deny all;
}
}
EOFcat > /etc/nginx/http.d/monsite.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name monsite.exemple.ca;
root /var/www/monsite;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
location ~ /\. {
deny all;
}
}
EOF
rm -f /etc/nginx/http.d/default.confcat > /etc/nginx/vhosts.d/monsite.conf << 'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name monsite.exemple.ca;
root /var/www/monsite;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
location ~ /\. {
deny all;
}
}
EOFnginx -t && systemctl reload nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
nginx -t && systemctl reload nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
nginx -t && systemctl reload nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
nginx -t && rc-service nginx reloadnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful * Reloading nginx configuration ... [ ok ]
nginx -t && systemctl reload nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
curl http://127.0.0.1/PHP 8.3.6 (fpm-fcgi) upload 2M memoire 128M fuseau UTC (Debian 13 : PHP 8.4.24)
curl http://127.0.0.1/PHP 8.3.33 (fpm-fcgi) upload 2M memoire 128M fuseau UTC
curl http://127.0.0.1/PHP 8.5.10 (fpm-fcgi) upload 2M memoire 128M fuseau UTC
curl http://127.0.0.1/PHP 8.4.25 (fpm-fcgi) upload 2M memoire 128M fuseau UTC
curl http://127.0.0.1/PHP 8.4.24 (fpm-fcgi) upload 2M memoire 128M fuseau UTC
Dans le navigateur, http://ADRESSE_IP/ affiche la même ligne. Les valeurs 2M, 128M et UTC sont celles de PHP par défaut : l'étape suivante les change.
4Régler PHP pour un site
Les valeurs par défaut conviennent à une page témoin, pas à un site : 2 Mo par fichier envoyé, 128 Mo de mémoire par script, fuseau UTC. Remplacez America/Toronto par votre fuseau horaire, puis redémarrez PHP-FPM pour qu'il relise sa configuration.
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
cat > /etc/php/$PHPV/fpm/conf.d/90-monsite.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
date.timezone = America/Toronto
EOF
systemctl restart php$PHPV-fpmcat > /etc/php.d/90-monsite.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
date.timezone = America/Toronto
EOF
systemctl restart php-fpmcat > /etc/php.d/90-monsite.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
date.timezone = America/Toronto
EOF
systemctl restart php-fpmcat > /etc/php84/conf.d/90-monsite.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
date.timezone = America/Toronto
EOF
rc-service php-fpm84 restartcat > /etc/php8/conf.d/90-monsite.ini << 'EOF'
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
date.timezone = America/Toronto
EOF
systemctl restart php-fpmcurl http://127.0.0.1/PHP 8.3.6 (fpm-fcgi) upload 64M memoire 256M fuseau America/Toronto (Debian 13 : PHP 8.4.24)
curl http://127.0.0.1/PHP 8.3.33 (fpm-fcgi) upload 64M memoire 256M fuseau America/Toronto
curl http://127.0.0.1/PHP 8.5.10 (fpm-fcgi) upload 64M memoire 256M fuseau America/Toronto
curl http://127.0.0.1/PHP 8.4.25 (fpm-fcgi) upload 64M memoire 256M fuseau America/Toronto
curl http://127.0.0.1/PHP 8.4.24 (fpm-fcgi) upload 64M memoire 256M fuseau America/Toronto
5Ouvrir le pare-feu
Le pare-feu laisse passer SSH, HTTP et HTTPS, et refuse le reste. Le détail (règles réservées à votre adresse, journal, suppression) est dans le tutoriel Configurer le pare-feu.
apt install -y ufw && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable... Firewall is active and enabled on system startup
dnf install -y firewalld && systemctl enable --now firewalld && firewall-cmd --permanent --add-service=http --add-service=https && firewall-cmd --reload... success success
dnf install -y firewalld && systemctl enable --now firewalld && firewall-cmd --permanent --add-service=http --add-service=https && firewall-cmd --reload... success success
apk add ufw && ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable && rc-update add ufw default... Firewall is active and enabled on system startup * service ufw added to runlevel default
systemctl enable --now firewalld && firewall-cmd --permanent --add-service=http --add-service=https && firewall-cmd --reloadsuccess success
6Passer en HTTPS avec Certbot
À faire quand votre nom de domaine pointe sur le serveur. Certbot obtient le certificat Let's Encrypt, réécrit le site Nginx pour servir en HTTPS et le renouvelle tout seul. Il demande une adresse courriel et l'accord aux conditions d'utilisation ; le détail (vérifications, renouvellement selon le système, erreurs) est dans le tutoriel Installer un certificat Let's Encrypt avec Certbot.
apt install -y certbot python3-certbot-nginxdnf install -y epel-release && dnf install -y certbot python3-certbot-nginxdnf install -y certbot python3-certbot-nginx python3-pyparsingapk add certbot certbot-nginxzypper -n install python313-certbot-nginxcertbot --nginx -d monsite.exemple.ca7Dimensionner PHP-FPM
PHP-FPM garde des processus prêts à répondre ; leur nombre maximal, pm.max_children, est bas par défaut. Trop bas, les visiteurs attendent leur tour ; trop haut, la mémoire déborde. La bonne valeur se calcule : la mémoire que vous réservez à PHP, divisée par le poids d'un processus. Mesurez ce poids après quelques visites de votre application (colonne RSS, en Ko).
ps -eo rss,user,comm | grep php-fpm34188 root php-fpm8.3 16316 www-data php-fpm8.3 17980 www-data php-fpm8.3 (le premier est le maître ; avec la page témoin, chaque processus de travail pèse 16 à 18 Mo, une application en pèse davantage)
ps -eo rss,user,comm | grep php-fpm36092 root php-fpm 18720 apache php-fpm 20732 apache php-fpm 19496 apache php-fpm 20744 apache php-fpm 15432 apache php-fpm (le premier est le maître ; avec la page témoin, chaque processus de travail pèse 15 à 21 Mo, une application en pèse davantage)
ps -eo rss,user,comm | grep php-fpm37932 root php-fpm 14188 apache php-fpm 16092 apache php-fpm 14644 apache php-fpm 14644 apache php-fpm 16080 apache php-fpm (le premier est le maître ; avec la page témoin, chaque processus de travail pèse 14 à 16 Mo, une application en pèse davantage)
ps -eo rss,user,comm | grep php-fpm30m root php-fpm84 12m nobody php-fpm84 14m nobody php-fpm84 (le premier est le maître ; avec la page témoin, chaque processus de travail pèse 12 à 14 Mo, une application en pèse davantage)
ps -eo rss,user,comm | grep php-fpm44024 root php-fpm 17088 wwwrun php-fpm 19540 wwwrun php-fpm (le premier est le maître ; avec la page témoin, chaque processus de travail pèse 17 à 20 Mo, une application en pèse davantage)
La valeur se change dans le fichier du pool www, ligne pm.max_children, puis PHP-FPM redémarre. Les lignes pm.start_servers, pm.min_spare_servers et pm.max_spare_servers du même fichier doivent rester inférieures ou égales à pm.max_children.
/etc/php/8.3/fpm/pool.d/www.conf- Ubuntu 24.04 (8.4 dans le chemin sur Debian 13)
systemctl restart php8.3-fpm(php8.4-fpm sur Debian 13) /etc/php-fpm.d/www.conf- AlmaLinux, Rocky, CentOS, Fedora
systemctl restart php-fpm /etc/php84/php-fpm.d/www.conf- Alpine
rc-service php-fpm84 restart /etc/php8/fpm/php-fpm.d/www.conf- openSUSE
systemctl restart php-fpm
Les commandes du quotidien
nginx -t && systemctl reload nginxsystemctl restart php8.3-fpmsystemctl status nginx php8.3-fpmtail -n 20 /var/log/nginx/error.logtail -f /var/log/nginx/access.logphp -mnginx -t && systemctl reload nginxsystemctl restart php-fpmsystemctl status nginx php-fpmtail -n 20 /var/log/nginx/error.logtail -f /var/log/nginx/access.logphp -mnginx -t && systemctl reload nginxsystemctl restart php-fpmsystemctl status nginx php-fpmtail -n 20 /var/log/nginx/error.logtail -f /var/log/nginx/access.logphp -mnginx -t && rc-service nginx reloadrc-service php-fpm84 restartrc-service nginx status && rc-service php-fpm84 statustail -n 20 /var/log/nginx/error.logtail -f /var/log/nginx/access.logphp84 -mnginx -t && systemctl reload nginxsystemctl restart php-fpmsystemctl status nginx php-fpmtail -n 20 /var/log/nginx/error.logtail -f /var/log/nginx/access.logphp -mNginx et PHP-FPM en service
Nginx sert votre site, PHP-FPM exécute le code, le pare-feu est ouvert et HTTPS tient en une commande. Pour aller plus loin : Installer MySQL ou MariaDB, puis WordPress ou votre application dans /var/www/monsite.
Erreurs fréquentes
L'image du serveur a été construite sur un autre disque et GRUB demande où se réinstaller : choisissez le disque de la machine (celui que lsblk affiche, en général /dev/sda) ; la mise à jour continue ensuite.
Nginx ne joint pas PHP-FPM : il est arrêté, ou fastcgi_pass ne pointe pas sur sa socket. /var/log/nginx/error.log le dit : « connect() to unix:/run/php/php-fpm.sock failed (2: No such file or directory) » (Ubuntu, Debian ; /run/php-fpm/www.sock sur AlmaLinux, Rocky, CentOS et Fedora) ou « connect() failed (111: Connection refused) ... 127.0.0.1:9000 » (Alpine, openSUSE). Redémarrez PHP-FPM (tableau ci-dessus) et comparez fastcgi_pass avec le fichier de l'étape 3.
PHP-FPM ne trouve pas le fichier à l'endroit que Nginx lui indique (error.log : « Primary script unknown »). Vérifiez la ligne root du site et le nom du fichier appelé : /var/www/monsite/index.php doit exister.
Le bloc location ~ \.php$ manque ou n'a pas été rechargé : Nginx sert le code source tel quel (type application/octet-stream). Reprenez le fichier du site de l'étape 3, puis nginx -t && systemctl reload nginx (rc-service nginx reload sur Alpine).
SELinux : les fichiers hors de /var/www n'ont pas l'étiquette web (error.log : « is forbidden (13: Permission denied) »). Gardez le site sous /var/www, ou étiquetez son dossier : chcon -R -t httpd_sys_content_t /srv/monsite.
Le site par défaut est toujours actif (sites-enabled/default sur Ubuntu et Debian, http.d/default.conf sur Alpine), ou Nginx n'a pas été rechargé après la création du site : reprenez la fin de l'étape 3.
PHP-FPM n'a pas redémarré, ou le fichier est dans le mauvais dossier conf.d. Vérifiez par la page témoin, pas par php -i : sur Ubuntu et Debian, la ligne de commande a sa propre configuration (/etc/php/8.3/cli), distincte de celle de PHP-FPM (/etc/php/8.3/fpm).